diff --git a/src/agents/agent-command.ts b/src/agents/agent-command.ts index 8bbbc6dd9f2..ea8215ee32f 100644 --- a/src/agents/agent-command.ts +++ b/src/agents/agent-command.ts @@ -218,8 +218,7 @@ type OverrideFieldClearedByDelete = | "authProfileOverrideCompactionCount" | "fallbackNoticeSelectedModel" | "fallbackNoticeActiveModel" - | "fallbackNoticeReason" - | "claudeCliSessionId"; + | "fallbackNoticeReason"; const OVERRIDE_FIELDS_CLEARED_BY_DELETE: OverrideFieldClearedByDelete[] = [ "providerOverride", @@ -230,7 +229,6 @@ const OVERRIDE_FIELDS_CLEARED_BY_DELETE: OverrideFieldClearedByDelete[] = [ "fallbackNoticeSelectedModel", "fallbackNoticeActiveModel", "fallbackNoticeReason", - "claudeCliSessionId", ]; const OVERRIDE_VALUE_MAX_LENGTH = 256; diff --git a/src/agents/cli-session.test.ts b/src/agents/cli-session.test.ts index dab350f7382..a0a28ea25d4 100644 --- a/src/agents/cli-session.test.ts +++ b/src/agents/cli-session.test.ts @@ -10,7 +10,7 @@ import { } from "./cli-session.js"; describe("cli-session helpers", () => { - it("persists binding metadata alongside legacy session ids", () => { + it("persists binding metadata in the canonical CLI session binding", () => { const entry: SessionEntry = { sessionId: "openclaw-session", updatedAt: Date.now(), @@ -27,8 +27,6 @@ describe("cli-session helpers", () => { mcpResumeHash: "mcp-resume-hash", }); - expect(entry.cliSessionIds?.["claude-cli"]).toBe("cli-session-1"); - expect(entry.claudeCliSessionId).toBe("cli-session-1"); expect(getCliSessionBinding(entry, "claude-cli")).toEqual({ sessionId: "cli-session-1", forceReuse: true, @@ -66,12 +64,11 @@ describe("cli-session helpers", () => { ).toEqual({ sessionId: "cli-session-1" }); }); - it("keeps legacy bindings reusable until richer metadata is persisted", () => { + it("keeps bindings reusable until richer metadata is persisted", () => { const entry: SessionEntry = { sessionId: "openclaw-session", updatedAt: Date.now(), - cliSessionIds: { "claude-cli": "legacy-session" }, - claudeCliSessionId: "legacy-session", + cliSessionBindings: { "claude-cli": { sessionId: "cli-session" } }, }; expect( @@ -79,15 +76,14 @@ describe("cli-session helpers", () => { binding: getCliSessionBinding(entry, "claude-cli"), authEpochVersion: 2, }), - ).toEqual({ sessionId: "legacy-session" }); + ).toEqual({ sessionId: "cli-session" }); }); - it("invalidates legacy bindings when auth, prompt, or MCP state changes", () => { + it("invalidates bindings without matching metadata when auth, prompt, or MCP state changes", () => { const entry: SessionEntry = { sessionId: "openclaw-session", updatedAt: Date.now(), - cliSessionIds: { "claude-cli": "legacy-session" }, - claudeCliSessionId: "legacy-session", + cliSessionBindings: { "claude-cli": { sessionId: "cli-session" } }, }; const binding = getCliSessionBinding(entry, "claude-cli"); @@ -363,8 +359,6 @@ describe("cli-session helpers", () => { clearAllCliSessions(entry); expect(entry.cliSessionBindings).toBeUndefined(); - expect(entry.cliSessionIds).toBeUndefined(); - expect(entry.claudeCliSessionId).toBeUndefined(); }); it("hashes trimmed extra system prompts consistently", () => { diff --git a/src/agents/cli-session.ts b/src/agents/cli-session.ts index b99a09fc372..bfe9dd9bad3 100644 --- a/src/agents/cli-session.ts +++ b/src/agents/cli-session.ts @@ -3,8 +3,6 @@ import type { CliSessionBinding, SessionEntry } from "../config/sessions.js"; import { normalizeOptionalString } from "../shared/string-coerce.js"; import { normalizeProviderId } from "./model-selection.js"; -const CLAUDE_CLI_BACKEND_ID = "claude-cli"; - export function hashCliSessionText(value: string | undefined): string | undefined { const trimmed = normalizeOptionalString(value); if (!trimmed) { @@ -35,17 +33,6 @@ export function getCliSessionBinding( mcpResumeHash: normalizeOptionalString(fromBindings?.mcpResumeHash), }; } - const fromMap = entry.cliSessionIds?.[normalized]; - const normalizedFromMap = normalizeOptionalString(fromMap); - if (normalizedFromMap) { - return { sessionId: normalizedFromMap }; - } - if (normalized === CLAUDE_CLI_BACKEND_ID) { - const legacy = normalizeOptionalString(entry.claudeCliSessionId); - if (legacy) { - return { sessionId: legacy }; - } - } return undefined; } @@ -95,10 +82,6 @@ export function setCliSessionBinding( : {}), }, }; - entry.cliSessionIds = { ...entry.cliSessionIds, [normalized]: trimmed }; - if (normalized === CLAUDE_CLI_BACKEND_ID) { - entry.claudeCliSessionId = trimmed; - } } export function clearCliSession(entry: SessionEntry, provider: string): void { @@ -108,20 +91,10 @@ export function clearCliSession(entry: SessionEntry, provider: string): void { delete next[normalized]; entry.cliSessionBindings = Object.keys(next).length > 0 ? next : undefined; } - if (entry.cliSessionIds?.[normalized] !== undefined) { - const next = { ...entry.cliSessionIds }; - delete next[normalized]; - entry.cliSessionIds = Object.keys(next).length > 0 ? next : undefined; - } - if (normalized === CLAUDE_CLI_BACKEND_ID) { - entry.claudeCliSessionId = undefined; - } } export function clearAllCliSessions(entry: SessionEntry): void { entry.cliSessionBindings = undefined; - entry.cliSessionIds = undefined; - entry.claudeCliSessionId = undefined; } export function resolveCliSessionReuse(params: { diff --git a/src/agents/command/attempt-execution.cli.test.ts b/src/agents/command/attempt-execution.cli.test.ts index e5fc8302fe4..cc57e879eca 100644 --- a/src/agents/command/attempt-execution.cli.test.ts +++ b/src/agents/command/attempt-execution.cli.test.ts @@ -206,8 +206,12 @@ describe("CLI attempt execution", () => { const sessionEntry: SessionEntry = { sessionId: "session-cli-123", updatedAt: Date.now(), - cliSessionIds: { "claude-cli": "stale-cli-session" }, - claudeCliSessionId: "stale-legacy-session", + cliSessionBindings: { + "claude-cli": { + sessionId: "stale-cli-session", + authProfileId: "anthropic:claude-cli", + }, + }, }; const sessionStore: Record = { [sessionKey]: sessionEntry }; await writeStore(sessionStore); @@ -254,12 +258,10 @@ describe("CLI attempt execution", () => { expect(runCliAgentMock).toHaveBeenCalledTimes(2); expect(runCliAgentMock.mock.calls[0]?.[0]?.cliSessionId).toBe("stale-cli-session"); expect(runCliAgentMock.mock.calls[1]?.[0]?.cliSessionId).toBeUndefined(); - expect(sessionStore[sessionKey]?.cliSessionIds?.["claude-cli"]).toBeUndefined(); - expect(sessionStore[sessionKey]?.claudeCliSessionId).toBeUndefined(); + expect(sessionStore[sessionKey]?.cliSessionBindings?.["claude-cli"]).toBeUndefined(); const persisted = readStore(); - expect(persisted[sessionKey]?.cliSessionIds?.["claude-cli"]).toBeUndefined(); - expect(persisted[sessionKey]?.claudeCliSessionId).toBeUndefined(); + expect(persisted[sessionKey]?.cliSessionBindings?.["claude-cli"]).toBeUndefined(); }); it("does not pass --resume when the stored Claude CLI transcript is missing", async () => { @@ -275,8 +277,6 @@ describe("CLI attempt execution", () => { authProfileId: "anthropic:claude-cli", }, }, - cliSessionIds: { "claude-cli": "phantom-claude-session" }, - claudeCliSessionId: "phantom-claude-session", }; const sessionStore: Record = { [sessionKey]: sessionEntry }; await writeStore(sessionStore); @@ -294,13 +294,9 @@ describe("CLI attempt execution", () => { expect(runCliAgentMock.mock.calls[0]?.[0]?.cliSessionId).toBeUndefined(); expect(runCliAgentMock.mock.calls[0]?.[0]?.cliSessionBinding).toBeUndefined(); expect(sessionStore[sessionKey]?.cliSessionBindings?.["claude-cli"]).toBeUndefined(); - expect(sessionStore[sessionKey]?.cliSessionIds?.["claude-cli"]).toBeUndefined(); - expect(sessionStore[sessionKey]?.claudeCliSessionId).toBeUndefined(); const persisted = readStore(); expect(persisted[sessionKey]?.cliSessionBindings?.["claude-cli"]).toBeUndefined(); - expect(persisted[sessionKey]?.cliSessionIds?.["claude-cli"]).toBeUndefined(); - expect(persisted[sessionKey]?.claudeCliSessionId).toBeUndefined(); }); it("keeps Claude CLI resume when the stored transcript has assistant content", async () => { @@ -330,8 +326,6 @@ describe("CLI attempt execution", () => { authProfileId: "anthropic:claude-cli", }, }, - cliSessionIds: { "claude-cli": cliSessionId }, - claudeCliSessionId: cliSessionId, }; const sessionStore: Record = { [sessionKey]: sessionEntry }; await writeStore(sessionStore); @@ -351,8 +345,9 @@ describe("CLI attempt execution", () => { sessionId: cliSessionId, authProfileId: "anthropic:claude-cli", }); - expect(sessionStore[sessionKey]?.cliSessionIds?.["claude-cli"]).toBe(cliSessionId); - expect(sessionStore[sessionKey]?.claudeCliSessionId).toBe(cliSessionId); + expect(sessionStore[sessionKey]?.cliSessionBindings?.["claude-cli"]?.sessionId).toBe( + cliSessionId, + ); }); it("passes session-bound OpenAI Codex auth profile to codex-cli aliases", async () => { diff --git a/src/agents/command/cli-compaction.test.ts b/src/agents/command/cli-compaction.test.ts index eeb676e0a89..03e130d8164 100644 --- a/src/agents/command/cli-compaction.test.ts +++ b/src/agents/command/cli-compaction.test.ts @@ -107,10 +107,6 @@ describe("runCliTurnCompactionLifecycle", () => { cliSessionBindings: { "claude-cli": { sessionId: "claude-session" }, }, - cliSessionIds: { - "claude-cli": "claude-session", - }, - claudeCliSessionId: "claude-session", }; const sessionStore: Record = { [sessionKey]: sessionEntry }; upsertSessionEntry({ agentId: "main", sessionKey, entry: sessionEntry }); @@ -170,8 +166,6 @@ describe("runCliTurnCompactionLifecycle", () => { ); expect(updatedEntry?.compactionCount).toBe(1); expect(updatedEntry?.cliSessionBindings?.["claude-cli"]).toBeUndefined(); - expect(updatedEntry?.cliSessionIds?.["claude-cli"]).toBeUndefined(); - expect(updatedEntry?.claudeCliSessionId).toBeUndefined(); }); it("initializes built-in context engines before resolving CLI compaction engine", async () => { diff --git a/src/agents/command/session-entry-updates.test.ts b/src/agents/command/session-entry-updates.test.ts index 1af4d30e016..239c10ebf03 100644 --- a/src/agents/command/session-entry-updates.test.ts +++ b/src/agents/command/session-entry-updates.test.ts @@ -317,15 +317,11 @@ describe("updateSessionEntryAfterAgentRun", () => { expect(sessionStore[sessionKey]?.cliSessionBindings?.["claude-cli"]).toEqual({ sessionId: "cli-session-123", }); - expect(sessionStore[sessionKey]?.cliSessionIds?.["claude-cli"]).toBe("cli-session-123"); - expect(sessionStore[sessionKey]?.claudeCliSessionId).toBe("cli-session-123"); const persisted = readMockSessionEntries(agentId); expect(persisted[sessionKey]?.cliSessionBindings?.["claude-cli"]).toEqual({ sessionId: "cli-session-123", }); - expect(persisted[sessionKey]?.cliSessionIds?.["claude-cli"]).toBe("cli-session-123"); - expect(persisted[sessionKey]?.claudeCliSessionId).toBe("cli-session-123"); }); }); @@ -1163,11 +1159,6 @@ describe("clearCliSessionEntry", () => { sessionId: "codex-session-1", }, }, - cliSessionIds: { - "claude-cli": "claude-session-1", - "codex-cli": "codex-session-1", - }, - claudeCliSessionId: "claude-session-1", }; const sessionStore: Record = { [sessionKey]: entry }; await replaceMockSessionEntries(agentId, sessionStore); @@ -1182,9 +1173,6 @@ describe("clearCliSessionEntry", () => { expect(cleared?.cliSessionBindings?.["codex-cli"]).toEqual({ sessionId: "codex-session-1", }); - expect(cleared?.cliSessionIds?.["claude-cli"]).toBeUndefined(); - expect(cleared?.cliSessionIds?.["codex-cli"]).toBe("codex-session-1"); - expect(cleared?.claudeCliSessionId).toBeUndefined(); expect(sessionStore[sessionKey]).toEqual(cleared); const persisted = readMockSessionEntries(agentId)[sessionKey]; @@ -1192,9 +1180,6 @@ describe("clearCliSessionEntry", () => { expect(persisted?.cliSessionBindings?.["codex-cli"]).toEqual({ sessionId: "codex-session-1", }); - expect(persisted?.cliSessionIds?.["claude-cli"]).toBeUndefined(); - expect(persisted?.cliSessionIds?.["codex-cli"]).toBe("codex-session-1"); - expect(persisted?.claudeCliSessionId).toBeUndefined(); }); }); @@ -1205,7 +1190,7 @@ describe("clearCliSessionEntry", () => { [existingKey]: { sessionId: "openclaw-session-1", updatedAt: 1, - claudeCliSessionId: "claude-session-1", + cliSessionBindings: { "claude-cli": { sessionId: "claude-session-1" } }, }, }; await replaceMockSessionEntries(agentId, sessionStore); @@ -1217,10 +1202,12 @@ describe("clearCliSessionEntry", () => { }); expect(cleared).toBeUndefined(); - expect(sessionStore[existingKey]?.claudeCliSessionId).toBe("claude-session-1"); - expect(readMockSessionEntries(agentId)[existingKey]?.claudeCliSessionId).toBe( + expect(sessionStore[existingKey]?.cliSessionBindings?.["claude-cli"]?.sessionId).toBe( "claude-session-1", ); + expect( + readMockSessionEntries(agentId)[existingKey]?.cliSessionBindings?.["claude-cli"]?.sessionId, + ).toBe("claude-session-1"); }); }); }); diff --git a/src/auto-reply/reply/commands-reset-hooks.test.ts b/src/auto-reply/reply/commands-reset-hooks.test.ts index 6e476daefec..7a71886db12 100644 --- a/src/auto-reply/reply/commands-reset-hooks.test.ts +++ b/src/auto-reply/reply/commands-reset-hooks.test.ts @@ -331,14 +331,12 @@ describe("handleCommands reset hooks", () => { params.sessionEntry = { sessionId: "session-1", updatedAt: Date.now(), - cliSessionIds: { "claude-cli": "cli-session-1" }, cliSessionBindings: { "claude-cli": { sessionId: "cli-session-1", extraSystemPromptHash: "prompt-hash", }, }, - claudeCliSessionId: "cli-session-1", } as HandleCommandsParams["sessionEntry"]; const result = await maybeHandleResetCommand(params); @@ -351,9 +349,7 @@ describe("handleCommands reset hooks", () => { expect(params.command.resetHookTriggered).toBe(true); expect(params.command.softResetTriggered).toBe(true); expect(params.command.softResetTail).toBe(""); - expect(params.sessionEntry?.cliSessionIds).toBeUndefined(); expect(params.sessionEntry?.cliSessionBindings).toBeUndefined(); - expect(params.sessionEntry?.claudeCliSessionId).toBeUndefined(); expect(clearBootstrapSnapshotSpy).toHaveBeenCalledWith("agent:main:main"); }); @@ -392,39 +388,31 @@ describe("handleCommands reset hooks", () => { params.sessionEntry = { sessionId: "session-direct", updatedAt: 1, - cliSessionIds: { "claude-cli": "cli-session-direct" }, cliSessionBindings: { "claude-cli": { sessionId: "cli-session-direct", extraSystemPromptHash: "prompt-hash-direct", }, }, - claudeCliSessionId: "cli-session-direct", } as HandleCommandsParams["sessionEntry"]; params.sessionStore = { [params.sessionKey]: { sessionId: "session-store", updatedAt: 2, - cliSessionIds: { "claude-cli": "cli-session-store" }, cliSessionBindings: { "claude-cli": { sessionId: "cli-session-store", extraSystemPromptHash: "prompt-hash-store", }, }, - claudeCliSessionId: "cli-session-store", }, } as Record>; const result = await maybeHandleResetCommand(params); expect(result).toBeNull(); - expect(params.sessionEntry?.cliSessionIds).toBeUndefined(); expect(params.sessionEntry?.cliSessionBindings).toBeUndefined(); - expect(params.sessionEntry?.claudeCliSessionId).toBeUndefined(); - expect(params.sessionStore?.[params.sessionKey]?.cliSessionIds).toBeUndefined(); expect(params.sessionStore?.[params.sessionKey]?.cliSessionBindings).toBeUndefined(); - expect(params.sessionStore?.[params.sessionKey]?.claudeCliSessionId).toBeUndefined(); }); it("rejects soft reset for bound ACP sessions", async () => { diff --git a/src/auto-reply/reply/commands-reset.ts b/src/auto-reply/reply/commands-reset.ts index 31b6b504bf0..ba3ae0e572c 100644 --- a/src/auto-reply/reply/commands-reset.ts +++ b/src/auto-reply/reply/commands-reset.ts @@ -82,8 +82,6 @@ export async function maybeHandleResetCommand( clearAllCliSessions(next); return { cliSessionBindings: next.cliSessionBindings, - cliSessionIds: next.cliSessionIds, - claudeCliSessionId: next.claudeCliSessionId, updatedAt: now, lastInteractionAt: now, }; diff --git a/src/auto-reply/reply/session-usage.ts b/src/auto-reply/reply/session-usage.ts index 38fc62eae1c..0cd9583c216 100644 --- a/src/auto-reply/reply/session-usage.ts +++ b/src/auto-reply/reply/session-usage.ts @@ -26,9 +26,7 @@ function applyCliSessionIdToSessionPatch( setCliSessionBinding(nextEntry, cliProvider, params.cliSessionBinding); return { ...patch, - cliSessionIds: nextEntry.cliSessionIds, cliSessionBindings: nextEntry.cliSessionBindings, - claudeCliSessionId: nextEntry.claudeCliSessionId, }; } if (params.cliSessionId && cliProvider) { @@ -36,9 +34,7 @@ function applyCliSessionIdToSessionPatch( setCliSessionId(nextEntry, cliProvider, params.cliSessionId); return { ...patch, - cliSessionIds: nextEntry.cliSessionIds, cliSessionBindings: nextEntry.cliSessionBindings, - claudeCliSessionId: nextEntry.claudeCliSessionId, }; } return patch; diff --git a/src/auto-reply/reply/session.test.ts b/src/auto-reply/reply/session.test.ts index 2507408d482..fd05fa1aea6 100644 --- a/src/auto-reply/reply/session.test.ts +++ b/src/auto-reply/reply/session.test.ts @@ -2217,14 +2217,12 @@ describe("initSessionState preserves behavior overrides across /new and /reset", authProfileOverride: "20251001", authProfileOverrideSource: "user", authProfileOverrideCompactionCount: 2, - cliSessionIds: { "claude-cli": "cli-session-123" }, cliSessionBindings: { "claude-cli": { sessionId: "cli-session-123", authProfileId: "anthropic:default", }, }, - claudeCliSessionId: "cli-session-123", } as const; const cases = [ { @@ -2274,14 +2272,10 @@ describe("initSessionState preserves behavior overrides across /new and /reset", authProfileOverrideSource: overrides.authProfileOverrideSource, authProfileOverrideCompactionCount: overrides.authProfileOverrideCompactionCount, }); - expect(result.sessionEntry.cliSessionIds).toBeUndefined(); expect(result.sessionEntry.cliSessionBindings).toBeUndefined(); - expect(result.sessionEntry.claudeCliSessionId).toBeUndefined(); const stored = readSessionRowsForFixtureTarget(sessionRowsTarget); - expect(stored[sessionKey].cliSessionIds).toBeUndefined(); expect(stored[sessionKey].cliSessionBindings).toBeUndefined(); - expect(stored[sessionKey].claudeCliSessionId).toBeUndefined(); } }); @@ -2442,7 +2436,6 @@ describe("initSessionState preserves behavior overrides across /new and /reset", sessionKey, sessionId: existingSessionId, overrides: { - cliSessionIds: { "claude-cli": "cli-session-1" }, cliSessionBindings: { "claude-cli": { sessionId: "cli-session-1", @@ -2625,10 +2618,6 @@ describe("initSessionState preserves behavior overrides across /new and /reset", cliSessionBindings: { "claude-cli": cliBinding, }, - cliSessionIds: { - "claude-cli": cliBinding.sessionId, - }, - claudeCliSessionId: cliBinding.sessionId, }, }); replaceSqliteSessionTranscriptEvents({ @@ -2961,7 +2950,6 @@ describe("persistSessionUsageUpdate", () => { const stored = readSessionRowsForFixtureTarget(sessionRowsTarget); expect(stored[sessionKey].totalTokens).toBe(32_000); expect(stored[sessionKey].totalTokensFresh).toBe(true); - expect(stored[sessionKey].cliSessionIds?.["claude-cli"]).toBe("cli-session-1"); expect(stored[sessionKey].cliSessionBindings?.["claude-cli"]).toEqual({ sessionId: "cli-session-1", authProfileId: "anthropic:default", diff --git a/src/auto-reply/reply/session.ts b/src/auto-reply/reply/session.ts index 3eaebeaac87..a39dc66c9d4 100644 --- a/src/auto-reply/reply/session.ts +++ b/src/auto-reply/reply/session.ts @@ -615,9 +615,7 @@ export async function initSessionState(params: { persistedAuthProfileOverrideSource ?? baseEntry?.authProfileOverrideSource, authProfileOverrideCompactionCount: persistedAuthProfileOverrideCompactionCount ?? baseEntry?.authProfileOverrideCompactionCount, - cliSessionIds: baseEntry?.cliSessionIds, cliSessionBindings: baseEntry?.cliSessionBindings, - claudeCliSessionId: baseEntry?.claudeCliSessionId, label: persistedLabel ?? baseEntry?.label, spawnedBy: persistedSpawnedBy ?? baseEntry?.spawnedBy, spawnedWorkspaceDir: persistedSpawnedWorkspaceDir ?? baseEntry?.spawnedWorkspaceDir, diff --git a/src/config/sessions/types.ts b/src/config/sessions/types.ts index b70b8706505..0c5c03a407e 100644 --- a/src/config/sessions/types.ts +++ b/src/config/sessions/types.ts @@ -342,9 +342,7 @@ export type SessionEntry = { memoryFlushAt?: number; memoryFlushCompactionCount?: number; memoryFlushContextHash?: string; - cliSessionIds?: Record; cliSessionBindings?: Record; - claudeCliSessionId?: string; label?: string; displayName?: string; channel?: string; diff --git a/src/cron/isolated-agent/run-session-state.ts b/src/cron/isolated-agent/run-session-state.ts index b596ee0620e..8625b2e2330 100644 --- a/src/cron/isolated-agent/run-session-state.ts +++ b/src/cron/isolated-agent/run-session-state.ts @@ -35,9 +35,7 @@ function toNonResumableCronSessionEntry(entry: SessionEntry): SessionEntry { delete next.sessionId; delete next.sessionStartedAt; delete next.lastInteractionAt; - delete next.cliSessionIds; delete next.cliSessionBindings; - delete next.claudeCliSessionId; return next as SessionEntry; } diff --git a/src/cron/isolated-agent/run.skill-filter.test.ts b/src/cron/isolated-agent/run.skill-filter.test.ts index 2bc1f847d86..d38d78bf7ff 100644 --- a/src/cron/isolated-agent/run.skill-filter.test.ts +++ b/src/cron/isolated-agent/run.skill-filter.test.ts @@ -318,7 +318,7 @@ describe("runCronIsolatedAgentTurn — skill filter", () => { systemSent: false, skillsSnapshot: undefined, // A stored CLI session ID that should NOT be reused on fresh runs. - cliSessionIds: { "claude-cli": "prev-cli-session-abc" }, + cliSessionBindings: { "claude-cli": { sessionId: "prev-cli-session-abc" } }, }, systemSent: false, isNewSession: true, @@ -348,7 +348,7 @@ describe("runCronIsolatedAgentTurn — skill filter", () => { updatedAt: 0, systemSent: false, skillsSnapshot: undefined, - cliSessionIds: { "claude-cli": "existing-cli-session-def" }, + cliSessionBindings: { "claude-cli": { sessionId: "existing-cli-session-def" } }, }, systemSent: false, isNewSession: false, diff --git a/src/cron/isolated-agent/session.test.ts b/src/cron/isolated-agent/session.test.ts index 046d4870828..e691c4d67f9 100644 --- a/src/cron/isolated-agent/session.test.ts +++ b/src/cron/isolated-agent/session.test.ts @@ -223,9 +223,7 @@ describe("resolveCronSession", () => { modelProvider: "anthropic", agentHarnessId: "claude-cli", agentRuntimeOverride: "claude-cli", - cliSessionIds: { anthropic: "old-cli-session" }, - cliSessionBindings: {}, - claudeCliSessionId: "old-claude-session", + cliSessionBindings: { anthropic: { sessionId: "old-cli-session" } }, liveModelSwitchPending: true, fallbackNoticeSelectedModel: "anthropic/claude-opus-4-6", fallbackNoticeActiveModel: "anthropic/claude-sonnet-4-6", @@ -311,9 +309,7 @@ describe("resolveCronSession", () => { expect(result.sessionEntry.modelProvider).toBeUndefined(); expect(result.sessionEntry.agentHarnessId).toBeUndefined(); expect(result.sessionEntry.agentRuntimeOverride).toBeUndefined(); - expect(result.sessionEntry.cliSessionIds).toBeUndefined(); expect(result.sessionEntry.cliSessionBindings).toBeUndefined(); - expect(result.sessionEntry.claudeCliSessionId).toBeUndefined(); expect(result.sessionEntry.liveModelSwitchPending).toBeUndefined(); expect(result.sessionEntry.fallbackNoticeSelectedModel).toBeUndefined(); expect(result.sessionEntry.fallbackNoticeActiveModel).toBeUndefined(); diff --git a/src/gateway/cli-session-history.claude.ts b/src/gateway/cli-session-history.claude.ts index ae2532dd8aa..bcaa435aafc 100644 --- a/src/gateway/cli-session-history.claude.ts +++ b/src/gateway/cli-session-history.claude.ts @@ -49,18 +49,7 @@ function resolveClaudeProjectsDir(homeDir?: string): string { export function resolveClaudeCliBindingSessionId( entry: SessionEntry | undefined, ): string | undefined { - const bindingSessionId = normalizeOptionalString( - entry?.cliSessionBindings?.[CLAUDE_CLI_PROVIDER]?.sessionId, - ); - if (bindingSessionId) { - return bindingSessionId; - } - const legacyMapSessionId = normalizeOptionalString(entry?.cliSessionIds?.[CLAUDE_CLI_PROVIDER]); - if (legacyMapSessionId) { - return legacyMapSessionId; - } - const legacyClaudeSessionId = normalizeOptionalString(entry?.claudeCliSessionId); - return legacyClaudeSessionId || undefined; + return normalizeOptionalString(entry?.cliSessionBindings?.[CLAUDE_CLI_PROVIDER]?.sessionId); } function resolveFiniteNumber(value: unknown): number | undefined { diff --git a/src/gateway/cli-session-history.test.ts b/src/gateway/cli-session-history.test.ts index e8cf68c9bfc..b3687b31903 100644 --- a/src/gateway/cli-session-history.test.ts +++ b/src/gateway/cli-session-history.test.ts @@ -351,48 +351,6 @@ describe("cli session history", () => { expect(messages).toBe(localMessages); }); }); - - it("falls back to legacy cliSessionIds when bindings are absent", async () => { - await withClaudeProjectsDir(async ({ homeDir, sessionId }) => { - const messages = augmentChatHistoryWithCliSessionImports({ - entry: { - sessionId: "openclaw-session", - updatedAt: Date.now(), - cliSessionIds: { - "claude-cli": sessionId, - }, - }, - provider: "claude-cli", - localMessages: [], - homeDir, - }); - expect(messages).toHaveLength(3); - expectFields(messages[1], { - role: "assistant", - }); - expectFields(readRecord(messages[1])["__openclaw"], { cliSessionId: sessionId }); - }); - }); - - it("falls back to legacy claudeCliSessionId when newer fields are absent", async () => { - await withClaudeProjectsDir(async ({ homeDir, sessionId }) => { - const messages = augmentChatHistoryWithCliSessionImports({ - entry: { - sessionId: "openclaw-session", - updatedAt: Date.now(), - claudeCliSessionId: sessionId, - }, - provider: "claude-cli", - localMessages: [], - homeDir, - }); - expect(messages).toHaveLength(3); - expectFields(messages[0], { - role: "user", - }); - expectFields(readRecord(messages[0])["__openclaw"], { cliSessionId: sessionId }); - }); - }); }); describe("readClaudeCliFallbackSeed", () => { diff --git a/src/gateway/server-methods/agent.test.ts b/src/gateway/server-methods/agent.test.ts index 195c2f11e19..2b162b7e9ce 100644 --- a/src/gateway/server-methods/agent.test.ts +++ b/src/gateway/server-methods/agent.test.ts @@ -868,18 +868,15 @@ describe("gateway agent handler", () => { }); }); - it("preserves cliSessionIds from existing session entry", async () => { - const existingCliSessionIds = { "claude-cli": "abc-123-def" }; - const existingClaudeCliSessionId = "abc-123-def"; + it("preserves CLI session bindings from existing session entry", async () => { + const existingCliSessionBindings = { "claude-cli": { sessionId: "abc-123-def" } }; mockMainSessionEntry({ - cliSessionIds: existingCliSessionIds, - claudeCliSessionId: existingClaudeCliSessionId, + cliSessionBindings: existingCliSessionBindings, }); const capturedEntry = await runMainAgentAndCaptureEntry("test-idem"); - expect(capturedEntry.cliSessionIds).toEqual(existingCliSessionIds); - expect(capturedEntry.claudeCliSessionId).toBe(existingClaudeCliSessionId); + expect(capturedEntry.cliSessionBindings).toEqual(existingCliSessionBindings); }); it("reactivates completed subagent sessions and broadcasts send updates", async () => { const childSessionKey = "agent:main:subagent:followup"; @@ -2544,13 +2541,12 @@ describe("gateway agent handler", () => { expect(mocks.resolveVoiceWakeRouteByTrigger).not.toHaveBeenCalled(); }); - it("handles missing cliSessionIds gracefully", async () => { + it("handles missing CLI session bindings gracefully", async () => { mockMainSessionEntry({}); const capturedEntry = await runMainAgentAndCaptureEntry("test-idem-2"); // Should be undefined, not cause an error - expect(capturedEntry.cliSessionIds).toBeUndefined(); - expect(capturedEntry.claudeCliSessionId).toBeUndefined(); + expect(capturedEntry.cliSessionBindings).toBeUndefined(); }); it("leaves noncanonical main row cleanup to doctor when writing a canonical session entry", async () => { mocks.loadSessionEntry.mockReturnValue({ diff --git a/src/gateway/server-methods/agent.ts b/src/gateway/server-methods/agent.ts index 3b270149fc7..1dff6d33db3 100644 --- a/src/gateway/server-methods/agent.ts +++ b/src/gateway/server-methods/agent.ts @@ -1103,9 +1103,7 @@ export const agentHandlers: GatewayRequestHandlers = { groupChannel: resolvedGroupChannel, space: resolvedGroupSpace, ...(pluginOwnerId ? { pluginOwnerId } : {}), - cliSessionIds: entry?.cliSessionIds, cliSessionBindings: entry?.cliSessionBindings, - claudeCliSessionId: entry?.claudeCliSessionId, }; sessionEntry = mergeSessionEntry(entry, nextEntryPatch); if (request.deliver === true) { diff --git a/src/gateway/server.agent.gateway-server-agent-b.test.ts b/src/gateway/server.agent.gateway-server-agent-b.test.ts index 33c93d2dc52..27f167f0adc 100644 --- a/src/gateway/server.agent.gateway-server-agent-b.test.ts +++ b/src/gateway/server.agent.gateway-server-agent-b.test.ts @@ -234,9 +234,6 @@ describe("gateway server agent", () => { updatedAt: Date.now(), modelProvider: "claude-cli", model: "claude-opus-4-6", - cliSessionIds: { - "claude-cli": "cli-session-123", - }, cliSessionBindings: { "claude-cli": { sessionId: "cli-session-123", @@ -245,7 +242,6 @@ describe("gateway server agent", () => { mcpResumeHash: "mcp-resume-hash", }, }, - claudeCliSessionId: "cli-session-123", }, }, }); @@ -267,10 +263,6 @@ describe("gateway server agent", () => { mcpResumeHash: "mcp-resume-hash", }, }); - expect(stored?.cliSessionIds).toEqual({ - "claude-cli": "cli-session-123", - }); - expect(stored?.claudeCliSessionId).toBe("cli-session-123"); }); test("agent accepts built-in channel alias (imsg)", async () => { diff --git a/src/gateway/server.sessions.reset-models.test.ts b/src/gateway/server.sessions.reset-models.test.ts index 5ad72dac205..50e9d3ed18c 100644 --- a/src/gateway/server.sessions.reset-models.test.ts +++ b/src/gateway/server.sessions.reset-models.test.ts @@ -266,9 +266,6 @@ test("sessions.reset preserves spawned session ownership metadata", async () => execAsk: "on-miss", execNode: "mac-mini", displayName: "Ops Child", - cliSessionIds: { - "claude-cli": "cli-session-123", - }, cliSessionBindings: { "claude-cli": { sessionId: "cli-session-123", @@ -276,7 +273,6 @@ test("sessions.reset preserves spawned session ownership metadata", async () => extraSystemPromptHash: "prompt-hash", }, }, - claudeCliSessionId: "cli-session-123", deliveryContext: { channel: "discord", to: "discord:child", @@ -334,8 +330,6 @@ test("sessions.reset preserves spawned session ownership metadata", async () => mcpConfigHash?: string; } >; - cliSessionIds?: Record; - claudeCliSessionId?: string; deliveryContext?: { channel?: string; to?: string; @@ -388,10 +382,6 @@ test("sessions.reset preserves spawned session ownership metadata", async () => extraSystemPromptHash: "prompt-hash", }, }); - expect(reset.payload?.entry.cliSessionIds).toEqual({ - "claude-cli": "cli-session-123", - }); - expect(reset.payload?.entry.claudeCliSessionId).toBe("cli-session-123"); expect(reset.payload?.entry.deliveryContext).toEqual({ channel: "discord", to: "discord:child", @@ -440,10 +430,6 @@ test("sessions.reset preserves spawned session ownership metadata", async () => extraSystemPromptHash: "prompt-hash", }, }); - expect(stored?.cliSessionIds).toEqual({ - "claude-cli": "cli-session-123", - }); - expect(stored?.claudeCliSessionId).toBe("cli-session-123"); expect(stored?.deliveryContext).toEqual({ channel: "discord", to: "discord:child", diff --git a/src/gateway/session-reset-service.ts b/src/gateway/session-reset-service.ts index d501d1ca9cd..96d18f8a16a 100644 --- a/src/gateway/session-reset-service.ts +++ b/src/gateway/session-reset-service.ts @@ -592,8 +592,6 @@ export async function performGatewaySessionReset(params: { origin: snapshotSessionOrigin(currentEntry), deliveryContext: currentEntry?.deliveryContext, cliSessionBindings: currentEntry?.cliSessionBindings, - cliSessionIds: currentEntry?.cliSessionIds, - claudeCliSessionId: currentEntry?.claudeCliSessionId, lastChannel: currentEntry?.lastChannel, lastTo: currentEntry?.lastTo, lastAccountId: currentEntry?.lastAccountId, diff --git a/src/plugins/session-entry-slot-keys.ts b/src/plugins/session-entry-slot-keys.ts index 449d60e10ae..a54addada9d 100644 --- a/src/plugins/session-entry-slot-keys.ts +++ b/src/plugins/session-entry-slot-keys.ts @@ -93,9 +93,7 @@ const SESSION_ENTRY_RESERVED_SLOT_KEY_LIST = [ "memoryFlushAt", "memoryFlushCompactionCount", "memoryFlushContextHash", - "cliSessionIds", "cliSessionBindings", - "claudeCliSessionId", "label", "displayName", "channel",