fix: preserve manual cli session attachments

This commit is contained in:
Peter Steinberger
2026-05-02 04:56:45 +01:00
parent 096b91cb3b
commit e93ff249b0
4 changed files with 35 additions and 0 deletions

View File

@@ -22,6 +22,7 @@ Docs: https://docs.openclaw.ai
### Fixes
- Discord/reactions: skip reaction listener registration when DMs and group DMs are disabled and every configured guild has `reactionNotifications: "off"`, avoiding needless reaction-event queue work. Fixes #47516. Thanks @x4v13r1120.
- CLI sessions: preserve explicit manual-attach reuse bindings so trusted CLI sessions are not invalidated on the first turn when auth, prompt, or MCP fingerprints drift. Fixes #75849. Thanks @alfredjbclaw.
- Telegram/streaming: keep partial preview streaming enabled for plain reply-to replies, disabling drafts only for real native quote excerpts that require Telegram quote parameters. Fixes #73505. Thanks @choury.
- Config: log the "newer OpenClaw" version warning once per process instead of once per config snapshot read. (#75927) Thanks @romneyda.
- Telegram/message actions: treat benign delete-message 400s as no-op warnings instead of runtime errors, so stale or already-removed messages do not create noisy delete failures. Fixes #73726. Thanks @Avicennasis.

View File

@@ -18,6 +18,7 @@ describe("cli-session helpers", () => {
setCliSessionBinding(entry, "claude-cli", {
sessionId: "cli-session-1",
forceReuse: true,
authProfileId: "anthropic:work",
authEpoch: "auth-epoch",
authEpochVersion: 2,
@@ -30,6 +31,7 @@ describe("cli-session helpers", () => {
expect(entry.claudeCliSessionId).toBe("cli-session-1");
expect(getCliSessionBinding(entry, "claude-cli")).toEqual({
sessionId: "cli-session-1",
forceReuse: true,
authProfileId: "anthropic:work",
authEpoch: "auth-epoch",
authEpochVersion: 2,
@@ -39,6 +41,31 @@ describe("cli-session helpers", () => {
});
});
it("force-reuses explicitly attached CLI sessions despite metadata drift", () => {
const binding = {
sessionId: "cli-session-1",
forceReuse: true,
authProfileId: "anthropic:work",
authEpoch: "auth-epoch-a",
authEpochVersion: 2,
extraSystemPromptHash: "prompt-a",
mcpConfigHash: "mcp-config-a",
mcpResumeHash: "mcp-resume-a",
};
expect(
resolveCliSessionReuse({
binding,
authProfileId: "anthropic:personal",
authEpoch: "auth-epoch-b",
authEpochVersion: 2,
extraSystemPromptHash: "prompt-b",
mcpConfigHash: "mcp-config-b",
mcpResumeHash: "mcp-resume-b",
}),
).toEqual({ sessionId: "cli-session-1" });
});
it("keeps legacy bindings reusable until richer metadata is persisted", () => {
const entry: SessionEntry = {
sessionId: "openclaw-session",

View File

@@ -26,6 +26,7 @@ export function getCliSessionBinding(
if (bindingSessionId) {
return {
sessionId: bindingSessionId,
...(fromBindings?.forceReuse === true ? { forceReuse: true } : {}),
authProfileId: normalizeOptionalString(fromBindings?.authProfileId),
authEpoch: normalizeOptionalString(fromBindings?.authEpoch),
authEpochVersion: fromBindings?.authEpochVersion,
@@ -73,6 +74,7 @@ export function setCliSessionBinding(
...entry.cliSessionBindings,
[normalized]: {
sessionId: trimmed,
...(binding.forceReuse === true ? { forceReuse: true } : {}),
...(normalizeOptionalString(binding.authProfileId)
? { authProfileId: normalizeOptionalString(binding.authProfileId) }
: {}),
@@ -139,6 +141,9 @@ export function resolveCliSessionReuse(params: {
if (!sessionId) {
return {};
}
if (binding?.forceReuse === true) {
return { sessionId };
}
const currentAuthProfileId = normalizeOptionalString(params.authProfileId);
const currentAuthEpoch = normalizeOptionalString(params.authEpoch);
const currentExtraSystemPromptHash = normalizeOptionalString(params.extraSystemPromptHash);

View File

@@ -72,6 +72,8 @@ export type AcpSessionRuntimeOptions = {
export type CliSessionBinding = {
sessionId: string;
/** Trust an explicitly attached CLI session even when auth, prompt, or MCP fingerprints drift. */
forceReuse?: boolean;
authProfileId?: string;
authEpoch?: string;
authEpochVersion?: number;