diff --git a/src/auto-reply/reply/commands-session-lifecycle.test.ts b/src/auto-reply/reply/commands-session-lifecycle.test.ts index f0052dca0a..60028dcd51 100644 --- a/src/auto-reply/reply/commands-session-lifecycle.test.ts +++ b/src/auto-reply/reply/commands-session-lifecycle.test.ts @@ -2,7 +2,7 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../../config/config.js"; import type { SessionBindingRecord } from "../../infra/outbound/session-binding-service.js"; import type { HandleCommandsParams } from "./commands-types.js"; -import { parseInlineDirectives } from "./directive-handling.js"; +import { parseInlineDirectives } from "./directive-handling.parse.js"; const THREAD_CHANNEL = "thread-chat"; const ROOM_CHANNEL = "room-chat"; diff --git a/src/auto-reply/reply/commands.test-harness.ts b/src/auto-reply/reply/commands.test-harness.ts index 60bc8f5657..1818e3b87d 100644 --- a/src/auto-reply/reply/commands.test-harness.ts +++ b/src/auto-reply/reply/commands.test-harness.ts @@ -2,7 +2,7 @@ import type { OpenClawConfig } from "../../config/config.js"; import type { MsgContext } from "../templating.js"; import { buildCommandContext } from "./commands-context.js"; import type { HandleCommandsParams } from "./commands-types.js"; -import { parseInlineDirectives } from "./directive-handling.js"; +import { parseInlineDirectives } from "./directive-handling.parse.js"; export function buildCommandTestParams( commandBody: string, diff --git a/src/auto-reply/reply/directive-handling.directive-only.ts b/src/auto-reply/reply/directive-handling.directive-only.ts new file mode 100644 index 0000000000..333402de4f --- /dev/null +++ b/src/auto-reply/reply/directive-handling.directive-only.ts @@ -0,0 +1,30 @@ +import type { OpenClawConfig } from "../../config/config.js"; +import type { MsgContext } from "../templating.js"; +import type { InlineDirectives } from "./directive-handling.parse.js"; +import { stripMentions, stripStructuralPrefixes } from "./mentions.js"; + +export function isDirectiveOnly(params: { + directives: InlineDirectives; + cleanedBody: string; + ctx: MsgContext; + cfg: OpenClawConfig; + agentId?: string; + isGroup: boolean; +}): boolean { + const { directives, cleanedBody, ctx, cfg, agentId, isGroup } = params; + if ( + !directives.hasThinkDirective && + !directives.hasVerboseDirective && + !directives.hasFastDirective && + !directives.hasReasoningDirective && + !directives.hasElevatedDirective && + !directives.hasExecDirective && + !directives.hasModelDirective && + !directives.hasQueueDirective + ) { + return false; + } + const stripped = stripStructuralPrefixes(cleanedBody ?? ""); + const noMentions = isGroup ? stripMentions(stripped, ctx, cfg, agentId) : stripped; + return noMentions.length === 0; +} diff --git a/src/auto-reply/reply/directive-handling.fast-lane.ts b/src/auto-reply/reply/directive-handling.fast-lane.ts index 0e0425672b..e12946de36 100644 --- a/src/auto-reply/reply/directive-handling.fast-lane.ts +++ b/src/auto-reply/reply/directive-handling.fast-lane.ts @@ -1,8 +1,8 @@ import type { ReplyPayload } from "../types.js"; +import { isDirectiveOnly } from "./directive-handling.directive-only.js"; import { handleDirectiveOnly } from "./directive-handling.impl.js"; import { resolveCurrentDirectiveLevels } from "./directive-handling.levels.js"; import type { ApplyInlineDirectivesFastLaneParams } from "./directive-handling.params.js"; -import { isDirectiveOnly } from "./directive-handling.parse.js"; export async function applyInlineDirectivesFastLane( params: ApplyInlineDirectivesFastLaneParams, diff --git a/src/auto-reply/reply/directive-handling.model.test.ts b/src/auto-reply/reply/directive-handling.model.test.ts index 24d4e9df00..dd746f12e1 100644 --- a/src/auto-reply/reply/directive-handling.model.test.ts +++ b/src/auto-reply/reply/directive-handling.model.test.ts @@ -30,11 +30,11 @@ import type { OpenClawConfig } from "../../config/config.js"; import type { SessionEntry } from "../../config/sessions.js"; import type { ElevatedLevel } from "../thinking.js"; import { handleDirectiveOnly } from "./directive-handling.impl.js"; -import { parseInlineDirectives } from "./directive-handling.js"; import { maybeHandleModelDirectiveInfo, resolveModelSelectionFromDirective, } from "./directive-handling.model.js"; +import { parseInlineDirectives } from "./directive-handling.parse.js"; import { persistInlineDirectives } from "./directive-handling.persist.js"; const liveModelSwitchMocks = vi.hoisted(() => ({ diff --git a/src/auto-reply/reply/directive-handling.parse.ts b/src/auto-reply/reply/directive-handling.parse.ts index 1834cc0b77..7cc5bd6058 100644 --- a/src/auto-reply/reply/directive-handling.parse.ts +++ b/src/auto-reply/reply/directive-handling.parse.ts @@ -1,7 +1,5 @@ -import type { OpenClawConfig } from "../../config/config.js"; import type { ExecAsk, ExecSecurity, ExecTarget } from "../../infra/exec-approvals.js"; import { extractModelDirective } from "../model.js"; -import type { MsgContext } from "../templating.js"; import type { ElevatedLevel, ReasoningLevel, ThinkLevel, VerboseLevel } from "./directives.js"; import { extractElevatedDirective, @@ -12,7 +10,6 @@ import { extractThinkDirective, extractVerboseDirective, } from "./directives.js"; -import { stripMentions, stripStructuralPrefixes } from "./mentions.js"; import { extractQueueDirective } from "./queue/directive.js"; import type { QueueDropPolicy, QueueMode } from "./queue/types.js"; @@ -201,29 +198,3 @@ export function parseInlineDirectives( hasQueueOptions, }; } - -export function isDirectiveOnly(params: { - directives: InlineDirectives; - cleanedBody: string; - ctx: MsgContext; - cfg: OpenClawConfig; - agentId?: string; - isGroup: boolean; -}): boolean { - const { directives, cleanedBody, ctx, cfg, agentId, isGroup } = params; - if ( - !directives.hasThinkDirective && - !directives.hasVerboseDirective && - !directives.hasFastDirective && - !directives.hasReasoningDirective && - !directives.hasElevatedDirective && - !directives.hasExecDirective && - !directives.hasModelDirective && - !directives.hasQueueDirective - ) { - return false; - } - const stripped = stripStructuralPrefixes(cleanedBody ?? ""); - const noMentions = isGroup ? stripMentions(stripped, ctx, cfg, agentId) : stripped; - return noMentions.length === 0; -} diff --git a/src/auto-reply/reply/directive-handling.ts b/src/auto-reply/reply/directive-handling.ts index 6c8d7160d3..c376ab0a2c 100644 --- a/src/auto-reply/reply/directive-handling.ts +++ b/src/auto-reply/reply/directive-handling.ts @@ -1,7 +1,8 @@ export { applyInlineDirectivesFastLane } from "./directive-handling.fast-lane.js"; export * from "./directive-handling.impl.js"; export type { InlineDirectives } from "./directive-handling.parse.js"; -export { isDirectiveOnly, parseInlineDirectives } from "./directive-handling.parse.js"; +export { isDirectiveOnly } from "./directive-handling.directive-only.js"; +export { parseInlineDirectives } from "./directive-handling.parse.js"; export { persistInlineDirectives } from "./directive-handling.persist.js"; export { resolveDefaultModel } from "./directive-handling.defaults.js"; export { formatDirectiveAck } from "./directive-handling.shared.js"; diff --git a/src/auto-reply/reply/get-reply-directives-apply.ts b/src/auto-reply/reply/get-reply-directives-apply.ts index 5c62b18260..84e15cb9f0 100644 --- a/src/auto-reply/reply/get-reply-directives-apply.ts +++ b/src/auto-reply/reply/get-reply-directives-apply.ts @@ -5,8 +5,9 @@ import type { MsgContext } from "../templating.js"; import type { ElevatedLevel } from "../thinking.js"; import type { ReplyPayload } from "../types.js"; import type { CommandContext } from "./commands-types.js"; +import { isDirectiveOnly } from "./directive-handling.directive-only.js"; import type { ApplyInlineDirectivesFastLaneParams } from "./directive-handling.params.js"; -import { isDirectiveOnly, type InlineDirectives } from "./directive-handling.parse.js"; +import type { InlineDirectives } from "./directive-handling.parse.js"; import { clearInlineDirectives } from "./get-reply-directives-utils.js"; import type { createModelSelectionState } from "./model-selection.js"; import type { TypingController } from "./typing.js"; diff --git a/src/auto-reply/reply/get-reply-exec-overrides.test.ts b/src/auto-reply/reply/get-reply-exec-overrides.test.ts index dc75d31142..25e2829c62 100644 --- a/src/auto-reply/reply/get-reply-exec-overrides.test.ts +++ b/src/auto-reply/reply/get-reply-exec-overrides.test.ts @@ -1,9 +1,6 @@ import { describe, expect, it } from "vitest"; -import type { ModelAliasIndex } from "../../agents/model-selection.js"; -import type { OpenClawConfig } from "../../config/config.js"; import type { SessionEntry } from "../../config/sessions.js"; import { parseInlineDirectives } from "./directive-handling.parse.js"; -import { persistInlineDirectives } from "./directive-handling.persist.js"; import { type ReplyExecOverrides, resolveReplyExecOverrides } from "./get-reply-exec-overrides.js"; const AGENT_EXEC_DEFAULTS = { @@ -21,33 +18,6 @@ function createSessionEntry(overrides?: Partial): SessionEntry { }; } -async function persistExecDirective(params: { - sessionEntry: SessionEntry; - sessionStore: Record; - body: string; -}) { - await persistInlineDirectives({ - directives: parseInlineDirectives(params.body), - cfg: { commands: { text: true } } as OpenClawConfig, - agentDir: "/tmp/agent", - sessionEntry: params.sessionEntry, - sessionStore: params.sessionStore, - sessionKey: "agent:main:main", - elevatedEnabled: false, - elevatedAllowed: false, - defaultProvider: "anthropic", - defaultModel: "claude-opus-4-6", - aliasIndex: { byAlias: new Map(), byKey: new Map() } satisfies ModelAliasIndex, - allowedModelKeys: new Set(), - provider: "anthropic", - model: "claude-opus-4-6", - initialModelLabel: "anthropic/claude-opus-4-6", - formatModelSwitchEvent: (label) => label, - agentCfg: undefined, - surface: "whatsapp", - }); -} - describe("reply exec overrides", () => { it("uses per-agent exec defaults when session and message are unset", () => { expect( @@ -90,19 +60,11 @@ describe("reply exec overrides", () => { }); }); - it("resolves the latest persisted exec directive for later turns", async () => { - const sessionEntry = createSessionEntry(); - const sessionStore = { "agent:main:main": sessionEntry }; - - await persistExecDirective({ - sessionEntry, - sessionStore, - body: "/exec host=gateway security=deny ask=off", - }); - await persistExecDirective({ - sessionEntry, - sessionStore, - body: "/exec host=gateway security=full ask=always", + it("uses persisted session exec fields for later turns", () => { + const sessionEntry = createSessionEntry({ + execHost: "gateway", + execSecurity: "full", + execAsk: "always", }); expect( diff --git a/src/auto-reply/reply/get-reply-inline-actions.ts b/src/auto-reply/reply/get-reply-inline-actions.ts index 1d1034a631..a2800094e4 100644 --- a/src/auto-reply/reply/get-reply-inline-actions.ts +++ b/src/auto-reply/reply/get-reply-inline-actions.ts @@ -27,8 +27,8 @@ import { } from "./abort-cutoff.js"; import { getAbortMemory, isAbortRequestText } from "./abort-primitives.js"; import type { buildStatusReply, handleCommands } from "./commands.runtime.js"; +import { isDirectiveOnly } from "./directive-handling.directive-only.js"; import type { InlineDirectives } from "./directive-handling.parse.js"; -import { isDirectiveOnly } from "./directive-handling.parse.js"; import { extractExplicitGroupId } from "./group-id.js"; import { stripMentions, stripStructuralPrefixes } from "./mentions.js"; import type { createModelSelectionState } from "./model-selection.js";