| 1 | import { initialState, reducer } from "../lib/useController"; |
| 2 | import { interactionInstanceKey } from "../lib/interactionTarget"; |
| 3 | import { sessionIdentityStableKey } from "../lib/sessionIdentity"; |
| 4 | |
| 5 | let passed = 0; |
| 6 | let failed = 0; |
| 7 | |
| 8 | function eq(actual: unknown, expected: unknown, label: string) { |
| 9 | if (actual === expected) { |
| 10 | passed += 1; |
| 11 | process.stdout.write(` PASS ${label}\n`); |
| 12 | } else { |
| 13 | failed += 1; |
| 14 | process.stdout.write(` FAIL ${label}: expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}\n`); |
| 15 | } |
| 16 | } |
| 17 | |
| 18 | const state = reducer({ |
| 19 | ...initialState, |
| 20 | promptEpoch: 7, |
| 21 | meta: { label: "", ready: true, eventChannel: "agent:event", cwd: "", session: { hostId: "local", sessionId: "session-a" }, sessionGeneration: 1 }, |
| 22 | ask: { id: "ask-old", questions: [] }, |
| 23 | mcpInteraction: { id: "mcp-new", server: "server", mode: "form", message: "new" }, |
| 24 | pendingPrompt: true, |
| 25 | }, { type: "expire_prompt", target: { tabId: "tab-a", sessionKey: sessionIdentityStableKey({ session: { hostId: "local", sessionId: "session-a" }, sessionGeneration: 1 }), hostId: "local", sessionId: "session-a", sessionGeneration: 1, |
| 26 | promptId: "ask-old", kind: "ask", instanceKey: "ask-old" }, epoch: 7 }); |
| 27 | |
| 28 | eq(state.ask, undefined, "stale Ask expiry removes only its matching card"); |
| 29 | eq(state.mcpInteraction?.id, "mcp-new", "stale Ask expiry preserves a newer MCP card"); |
| 30 | |
| 31 | const promptMeta = { label: "", ready: true, eventChannel: "agent:event", cwd: "", session: { hostId: "host-a", sessionId: "session-a" }, sessionGeneration: 3 }; |
| 32 | const ask = { id: "ask-replay", turnId: "turn-a", runtimeEpoch: "runtime-a", questions: [] }; |
| 33 | const sessionKey = sessionIdentityStableKey(promptMeta); |
| 34 | const targetBase = { tabId: "tab-a", sessionKey, hostId: "host-a", sessionId: "session-a", sessionGeneration: 3, |
| 35 | promptId: ask.id, turnId: ask.turnId, runtimeEpoch: ask.runtimeEpoch, kind: "ask" as const }; |
| 36 | const target = { ...targetBase, instanceKey: interactionInstanceKey(targetBase) }; |
| 37 | const answered = reducer({ ...initialState, promptEpoch: 8, meta: promptMeta, ask, pendingPrompt: true }, |
| 38 | { type: "ask_submit_succeeded", target, epoch: 8 }); |
| 39 | const replayed = reducer(answered, { type: "event", e: { kind: "ask_request", turnId: ask.turnId, runtimeEpoch: ask.runtimeEpoch, ask } }); |
| 40 | eq(replayed.ask, undefined, "a delayed replay with the same host and session identity cannot resurrect an answered Ask"); |
| 41 | |
| 42 | console.log(`prompt expiry: ${passed} passed, ${failed} failed`); |
| 43 | if (failed > 0) process.exit(1); |
| 44 |