@@ -478,6 +478,82 @@ describe("updateSessionStoreAfterAgentRun", () => {
|
478 | 478 | }); |
479 | 479 | }); |
480 | 480 | |
| 481 | +it("clears stale CLI bindings when a successful run reports an unflushed replacement", async () => { |
| 482 | +await withTempSessionStore(async ({ storePath }) => { |
| 483 | +const cfg = { |
| 484 | +agents: { |
| 485 | +defaults: { |
| 486 | +cliBackends: { |
| 487 | +"claude-cli": { |
| 488 | +command: "claude", |
| 489 | +}, |
| 490 | +}, |
| 491 | +}, |
| 492 | +}, |
| 493 | +} as OpenClawConfig; |
| 494 | +const sessionKey = "agent:main:explicit:test-clear-unflushed-cli"; |
| 495 | +const sessionId = "test-openclaw-session"; |
| 496 | +const sessionStore: Record<string, SessionEntry> = { |
| 497 | +[sessionKey]: { |
| 498 | + sessionId, |
| 499 | +updatedAt: 1, |
| 500 | +cliSessionBindings: { |
| 501 | +"claude-cli": { |
| 502 | +sessionId: "stale-cli-session", |
| 503 | +authEpoch: "old-epoch", |
| 504 | +}, |
| 505 | +"codex-cli": { |
| 506 | +sessionId: "codex-session", |
| 507 | +}, |
| 508 | +}, |
| 509 | +cliSessionIds: { |
| 510 | +"claude-cli": "stale-cli-session", |
| 511 | +"codex-cli": "codex-session", |
| 512 | +}, |
| 513 | +claudeCliSessionId: "stale-cli-session", |
| 514 | +}, |
| 515 | +}; |
| 516 | +await fs.writeFile(storePath, JSON.stringify(sessionStore, null, 2)); |
| 517 | + |
| 518 | +const result: EmbeddedAgentRunResult = { |
| 519 | +meta: { |
| 520 | +durationMs: 1, |
| 521 | +agentMeta: { |
| 522 | +sessionId: "", |
| 523 | +provider: "claude-cli", |
| 524 | +model: "claude-sonnet-4-6", |
| 525 | +clearCliSessionBinding: true, |
| 526 | +}, |
| 527 | +}, |
| 528 | +}; |
| 529 | + |
| 530 | +await updateSessionStoreAfterAgentRun({ |
| 531 | + cfg, |
| 532 | + sessionId, |
| 533 | + sessionKey, |
| 534 | + storePath, |
| 535 | + sessionStore, |
| 536 | +contextTokensOverride: 200_000, |
| 537 | +defaultProvider: "claude-cli", |
| 538 | +defaultModel: "claude-sonnet-4-6", |
| 539 | + result, |
| 540 | +}); |
| 541 | + |
| 542 | +expect(sessionStore[sessionKey]?.cliSessionBindings?.["claude-cli"]).toBeUndefined(); |
| 543 | +expect(sessionStore[sessionKey]?.cliSessionBindings?.["codex-cli"]).toEqual({ |
| 544 | +sessionId: "codex-session", |
| 545 | +}); |
| 546 | +expect(sessionStore[sessionKey]?.cliSessionIds?.["claude-cli"]).toBeUndefined(); |
| 547 | +expect(sessionStore[sessionKey]?.cliSessionIds?.["codex-cli"]).toBe("codex-session"); |
| 548 | +expect(sessionStore[sessionKey]?.claudeCliSessionId).toBeUndefined(); |
| 549 | + |
| 550 | +const persisted = loadSessionStore(storePath, { skipCache: true }); |
| 551 | +expect(persisted[sessionKey]?.cliSessionBindings?.["claude-cli"]).toBeUndefined(); |
| 552 | +expect(persisted[sessionKey]?.cliSessionIds?.["claude-cli"]).toBeUndefined(); |
| 553 | +expect(persisted[sessionKey]?.claudeCliSessionId).toBeUndefined(); |
| 554 | +}); |
| 555 | +}); |
| 556 | + |
481 | 557 | it("preserves ACP metadata when caller has a stale session snapshot", async () => { |
482 | 558 | await withTempSessionStore(async ({ storePath }) => { |
483 | 559 | const sessionKey = "agent:codex:acp:test-acp-preserve"; |
|