diff --git a/src/auto-reply/reply/commands-subagents-control.runtime.ts b/src/auto-reply/reply/commands-subagents-control.runtime.ts index c3b7672b82f..4e3b10c794f 100644 --- a/src/auto-reply/reply/commands-subagents-control.runtime.ts +++ b/src/auto-reply/reply/commands-subagents-control.runtime.ts @@ -1,6 +1,7 @@ export { killAllControlledSubagentRuns, killControlledSubagentRun, + listControlledSubagentRuns, sendControlledSubagentMessage, steerControlledSubagentRun, } from "../../agents/subagent-control.js"; diff --git a/src/auto-reply/reply/commands-subagents-focus.test.ts b/src/auto-reply/reply/commands-subagents-focus.test.ts index 991b234077c..7b95ccc1192 100644 --- a/src/auto-reply/reply/commands-subagents-focus.test.ts +++ b/src/auto-reply/reply/commands-subagents-focus.test.ts @@ -1,8 +1,11 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import type { OpenClawConfig } from "../../config/config.js"; +import type { SessionEntry } from "../../config/sessions/types.js"; import type { SessionBindingRecord } from "../../infra/outbound/session-binding-service.js"; import { handleSubagentsFocusAction } from "./commands-subagents/action-focus.js"; import { handleSubagentsUnfocusAction } from "./commands-subagents/action-unfocus.js"; +import type { HandleCommandsParams } from "./commands-types.js"; +import type { InlineDirectives } from "./directive-handling.js"; const THREAD_CHANNEL = "thread-chat"; const ROOM_CHANNEL = "room-chat"; @@ -150,6 +153,62 @@ function createSessionBindingCapabilities() { }; } +function buildCommandParams(params?: { + cfg?: OpenClawConfig; + chatType?: string; + senderId?: string; + sessionEntry?: SessionEntry; +}): HandleCommandsParams { + const directives: InlineDirectives = { + cleaned: "", + hasThinkDirective: false, + hasVerboseDirective: false, + hasFastDirective: false, + hasReasoningDirective: false, + hasElevatedDirective: false, + hasExecDirective: false, + hasExecOptions: false, + invalidExecHost: false, + invalidExecSecurity: false, + invalidExecAsk: false, + invalidExecNode: false, + hasStatusDirective: false, + hasModelDirective: false, + hasQueueDirective: false, + queueReset: false, + hasQueueOptions: false, + }; + return { + cfg: params?.cfg ?? baseCfg, + ctx: { + ChatType: params?.chatType ?? "group", + }, + command: { + surface: "whatsapp", + channel: "whatsapp", + ownerList: [], + senderIsOwner: true, + isAuthorizedSender: true, + senderId: params?.senderId ?? "user-1", + rawBodyNormalized: "", + commandBodyNormalized: "", + }, + directives, + elevated: { enabled: false, allowed: false, failures: [] }, + sessionEntry: params?.sessionEntry, + sessionKey: "agent:main:main", + workspaceDir: "/tmp/openclaw-subagents-focus", + defaultGroupActivation: () => "mention", + resolvedVerboseLevel: "off", + resolvedReasoningLevel: "off", + resolveDefaultThinkingLevel: async () => undefined, + provider: "whatsapp", + model: "test-model", + contextTokens: 0, + isGroup: true, + }; +} + function buildFocusContext(params?: { cfg?: OpenClawConfig; chatType?: string; @@ -157,38 +216,28 @@ function buildFocusContext(params?: { token?: string; }) { return { - params: { - cfg: params?.cfg ?? baseCfg, - ctx: { - ChatType: params?.chatType ?? "group", - }, - command: { - senderId: params?.senderId ?? "user-1", - }, - }, + params: buildCommandParams({ + cfg: params?.cfg, + chatType: params?.chatType, + senderId: params?.senderId, + }), handledPrefix: "/focus", requesterKey: "agent:main:main", runs: [], restTokens: [params?.token ?? "codex-acp"], - } as Parameters[0]; + } satisfies Parameters[0]; } function buildUnfocusContext(params?: { senderId?: string }) { return { - params: { - cfg: baseCfg, - ctx: { - ChatType: "group", - }, - command: { - senderId: params?.senderId ?? "user-1", - }, - }, + params: buildCommandParams({ + senderId: params?.senderId, + }), handledPrefix: "/unfocus", requesterKey: "agent:main:main", runs: [], restTokens: [], - } as Parameters[0]; + } satisfies Parameters[0]; } describe("focus actions", () => { diff --git a/src/auto-reply/reply/commands-subagents-spawn-action.test.ts b/src/auto-reply/reply/commands-subagents-spawn-action.test.ts index bb71ad2be21..702eeaf7018 100644 --- a/src/auto-reply/reply/commands-subagents-spawn-action.test.ts +++ b/src/auto-reply/reply/commands-subagents-spawn-action.test.ts @@ -1,7 +1,10 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import type { SpawnSubagentResult } from "../../agents/subagent-spawn.js"; import type { OpenClawConfig } from "../../config/config.js"; +import type { SessionEntry } from "../../config/sessions/types.js"; import { handleSubagentsSpawnAction } from "./commands-subagents/action-spawn.js"; +import type { HandleCommandsParams } from "./commands-types.js"; +import type { InlineDirectives } from "./directive-handling.js"; const spawnSubagentDirectMock = vi.hoisted(() => vi.fn()); @@ -35,30 +38,68 @@ function buildContext(params?: { requesterKey?: string; restTokens?: string[]; commandTo?: string | undefined; - context?: Record; - sessionEntry?: Record | undefined; + context?: Partial; + sessionEntry?: SessionEntry | undefined; }) { + const directives: InlineDirectives = { + cleaned: "", + hasThinkDirective: false, + hasVerboseDirective: false, + hasFastDirective: false, + hasReasoningDirective: false, + hasElevatedDirective: false, + hasExecDirective: false, + hasExecOptions: false, + invalidExecHost: false, + invalidExecSecurity: false, + invalidExecAsk: false, + invalidExecNode: false, + hasStatusDirective: false, + hasModelDirective: false, + hasQueueDirective: false, + queueReset: false, + hasQueueOptions: false, + }; + const ctx = { + OriginatingChannel: "whatsapp", + OriginatingTo: "channel:origin", + AccountId: "default", + MessageThreadId: "thread-1", + ...params?.context, + }; return { params: { cfg: params?.cfg ?? baseCfg, + ctx, command: { + surface: "whatsapp", channel: "whatsapp", + ownerList: [], + senderIsOwner: true, + isAuthorizedSender: true, + rawBodyNormalized: "", + commandBodyNormalized: "", to: params?.commandTo ?? "channel:command", }, - ctx: { - OriginatingChannel: "whatsapp", - OriginatingTo: "channel:origin", - AccountId: "default", - MessageThreadId: "thread-1", - ...params?.context, - }, + directives, + elevated: { enabled: false, allowed: false, failures: [] }, + sessionKey: "agent:main:main", + workspaceDir: "/tmp/openclaw-subagents-spawn", + defaultGroupActivation: () => "mention", + resolvedVerboseLevel: "off", + resolvedReasoningLevel: "off", + resolveDefaultThinkingLevel: async () => undefined, + provider: "whatsapp", + model: "test-model", + contextTokens: 0, + isGroup: true, ...(params?.sessionEntry ? { sessionEntry: params.sessionEntry } : {}), }, handledPrefix: "/subagents", requesterKey: params?.requesterKey ?? "agent:main:main", runs: [], restTokens: params?.restTokens ?? ["beta", "do", "the", "thing"], - } as Parameters[0]; + } satisfies Parameters[0]; } describe("subagents spawn action", () => { @@ -152,6 +193,8 @@ describe("subagents spawn action", () => { await handleSubagentsSpawnAction( buildContext({ sessionEntry: { + sessionId: "session-1", + updatedAt: Date.now(), groupId: "group-1", groupChannel: "#group-channel", space: "workspace-1", diff --git a/src/auto-reply/reply/commands-subagents/shared.ts b/src/auto-reply/reply/commands-subagents/shared.ts index 2a73e5bd640..cb363d1493f 100644 --- a/src/auto-reply/reply/commands-subagents/shared.ts +++ b/src/auto-reply/reply/commands-subagents/shared.ts @@ -25,7 +25,7 @@ import { truncateLine, } from "../../../shared/subagents-format.js"; import { resolveCommandSurfaceChannel, resolveChannelAccountId } from "../channel-context.js"; -import { extractMessageText } from "../commands-subagents-text.js"; +import { extractMessageText, type ChatMessage } from "../commands-subagents-text.js"; import type { CommandHandler, CommandHandlerResult } from "../commands-types.js"; import { formatRunLabel,