diff --git a/CHANGELOG.md b/CHANGELOG.md index 7083f5658..5a891dfa8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ ### Fixes - CLI/Update: preserve base environment when passing overrides to update subprocesses. (#713) — thanks @danielz1z. - Agents: treat message tool errors as failures so fallback replies still send; require `to` + `message` for `action=send`. (#717) — thanks @theglove44. +- Agents: route subagent transcripts to the target agent sessions directory and add regression coverage. (#708) — thanks @xMikeMickelson. +- Agents/Tools: preserve action enums when flattening tool schemas. (#708) — thanks @xMikeMickelson. ## 2026.1.10 diff --git a/src/agents/pi-tools.ts b/src/agents/pi-tools.ts index f0de8a707..54706bf54 100644 --- a/src/agents/pi-tools.ts +++ b/src/agents/pi-tools.ts @@ -127,6 +127,18 @@ function extractEnumValues(schema: unknown): unknown[] | undefined { const record = schema as Record; if (Array.isArray(record.enum)) return record.enum; if ("const" in record) return [record.const]; + const variants = Array.isArray(record.anyOf) + ? record.anyOf + : Array.isArray(record.oneOf) + ? record.oneOf + : null; + if (variants) { + const values = variants.flatMap((variant) => { + const extracted = extractEnumValues(variant); + return extracted ?? []; + }); + return values.length > 0 ? values : undefined; + } return undefined; } diff --git a/src/agents/tools/message-tool.ts b/src/agents/tools/message-tool.ts index 37b0d03f1..a0dd8808a 100644 --- a/src/agents/tools/message-tool.ts +++ b/src/agents/tools/message-tool.ts @@ -62,7 +62,6 @@ const AllMessageActions = [ "ban", ]; - const MessageToolCommonSchema = { provider: Type.Optional(Type.String()), media: Type.Optional(Type.String()), @@ -379,8 +378,7 @@ export function createMessageTool(options?: MessageToolOptions): AnyAgentTool { const mediaUrl = readStringParam(params, "media", { trim: false }) ?? (parsed.mediaUrls?.[0] || parsed.mediaUrl); - const replyTo = - readStringParam(params, "replyTo") ?? parsed.replyToId; + const replyTo = readStringParam(params, "replyTo") ?? parsed.replyToId; const threadId = readStringParam(params, "threadId"); const buttons = params.buttons; const gifPlayback = @@ -844,8 +842,7 @@ export function createMessageTool(options?: MessageToolOptions): AnyAgentTool { const mediaUrl = readStringParam(params, "media", { trim: false }) ?? (parsed.mediaUrls?.[0] || parsed.mediaUrl); - const replyTo = - readStringParam(params, "replyTo") ?? parsed.replyToId; + const replyTo = readStringParam(params, "replyTo") ?? parsed.replyToId; return await handleDiscordAction( { action: "threadReply", diff --git a/src/config/sessions.test.ts b/src/config/sessions.test.ts index 4863c3dd7..e750d958a 100644 --- a/src/config/sessions.test.ts +++ b/src/config/sessions.test.ts @@ -7,6 +7,7 @@ import { buildGroupDisplayName, deriveSessionKey, loadSessionStore, + resolveSessionFilePath, resolveSessionKey, resolveSessionTranscriptPath, resolveSessionTranscriptsDir, @@ -189,6 +190,31 @@ describe("sessions", () => { } }); + it("uses agent id when resolving session file fallback paths", () => { + const prev = process.env.CLAWDBOT_STATE_DIR; + process.env.CLAWDBOT_STATE_DIR = "/custom/state"; + try { + const sessionFile = resolveSessionFilePath("sess-2", undefined, { + agentId: "codex", + }); + expect(sessionFile).toBe( + path.join( + path.resolve("/custom/state"), + "agents", + "codex", + "sessions", + "sess-2.jsonl", + ), + ); + } finally { + if (prev === undefined) { + delete process.env.CLAWDBOT_STATE_DIR; + } else { + process.env.CLAWDBOT_STATE_DIR = prev; + } + } + }); + it("updateSessionStoreEntry merges concurrent patches", async () => { const mainSessionKey = "agent:main:main"; const dir = await fs.mkdtemp(path.join(os.tmpdir(), "clawdbot-sessions-"));