import test from "node:test"; import assert from "node:assert/strict"; import { intercomReleaseBlockers, stopIntercom, type IntercomStopState } from "./release-readiness.ts"; import { ReplyTracker } from "./reply-tracker.ts"; import type { Message, SessionInfo } from "./types.ts"; function createSession(id: string, name?: string): SessionInfo { return { id, name, cwd: "/tmp/project", model: "test-model", pid: 1, startedAt: 1, lastActivity: 1 }; } function createMessage(id: string, expectsReply: boolean): Message { return { id, timestamp: 1, expectsReply, content: { text: "hello" } }; } const idle = { pendingAsks: [], outboundAsk: null, pendingOutboxRequests: 0 }; test("an idle intercom has no release blockers", () => { assert.deepEqual(intercomReleaseBlockers(idle), []); }); test("unanswered inbound asks block; plain messages and answered asks do not", () => { const tracker = new ReplyTracker(); tracker.recordIncomingMessage(createSession("planner-id", "planner"), createMessage("ask-1", true), 1000); tracker.recordIncomingMessage(createSession("other-id"), createMessage("ask-2", true), 2000); tracker.recordIncomingMessage(createSession("fyi-id", "fyi"), createMessage("note-1", false), 1500); assert.deepEqual(intercomReleaseBlockers({ ...idle, pendingAsks: tracker.listPending(2500) }), [ { source: "intercom", kind: "inbound_ask", id: "ask-1", detail: "Ask from planner is awaiting our reply", since: 1000 }, { source: "intercom", kind: "inbound_ask", id: "ask-2", detail: "Ask from other-id is awaiting our reply", since: 2000 }, ]); tracker.markReplied("ask-1"); assert.deepEqual(intercomReleaseBlockers({ ...idle, pendingAsks: tracker.listPending(2500) }).map(blocker => blocker.id), ["ask-2"]); }); test("a blocking outbound ask is reported with its reply id and start time", () => { assert.deepEqual(intercomReleaseBlockers({ ...idle, outboundAsk: { from: "planner", replyTo: "reply-9", startedAt: 4242 } }), [ { source: "intercom", kind: "outbound_ask", id: "reply-9", detail: "Blocking ask to planner is awaiting a reply", since: 4242 }, ]); // A missing start time is omitted rather than invented. const [blocker] = intercomReleaseBlockers({ ...idle, outboundAsk: { from: "planner", replyTo: "reply-9" } }); assert.equal("since" in blocker!, false); }); test("unsettled outbox requests block", () => { assert.deepEqual(intercomReleaseBlockers({ ...idle, pendingOutboxRequests: 2 }), [ { source: "intercom", kind: "outbound_message", detail: "2 outbound message request(s) have not settled" }, ]); }); test("blockers are bounded and never carry invalid timestamps", () => { const long = "x".repeat(600); const [blocker] = intercomReleaseBlockers({ ...idle, pendingAsks: [{ from: createSession("id", long), message: createMessage(long, true), receivedAt: Number.NaN }], }); assert.equal(blocker!.id!.length, 256); assert.equal(blocker!.detail.length, 512); assert.equal("since" in blocker!, false); }); const farDeadline = () => Date.now() + 5_000; const stopState = (overrides: Partial = {}): IntercomStopState => ({ pendingAsks: [], outboundAsk: null, cancelOutboundAsk: () => undefined, ...overrides }); test("stop: an idle intercom has nothing to stop", () => { assert.deepEqual(stopIntercom(stopState(), { deadline: farDeadline() }), []); }); test("stop: cancels the blocking outbound ask through the client cancel and reports it", () => { const cancelled: Array<{ from: string; replyTo: string }> = []; const items = stopIntercom( stopState({ outboundAsk: { from: "planner", replyTo: "reply-9", startedAt: 1 }, cancelOutboundAsk: ask => { cancelled.push(ask); } }), { deadline: farDeadline() }, ); assert.deepEqual(cancelled, [{ from: "planner", replyTo: "reply-9" }]); assert.deepEqual(items, [{ source: "intercom", kind: "outbound_ask", id: "reply-9", detail: "Cancelled blocking ask to planner", ok: true }]); }); test("stop: reports a failing cancel as ok:false instead of throwing", () => { const items = stopIntercom( stopState({ outboundAsk: { from: "planner", replyTo: "reply-9" }, cancelOutboundAsk: () => { throw new Error("socket closed"); } }), { deadline: farDeadline() }, ); assert.deepEqual(items, [{ source: "intercom", kind: "outbound_ask", id: "reply-9", detail: "Failed to cancel blocking ask to planner: socket closed", ok: false, error: "socket closed" }]); }); test("stop: inbound asks cannot be cancelled by the receiver and are reported ok:false, left untouched", () => { const tracker = new ReplyTracker(); tracker.recordIncomingMessage(createSession("planner-id", "planner"), createMessage("ask-1", true), 1000); tracker.recordIncomingMessage(createSession("fyi-id", "fyi"), createMessage("note-1", false), 1500); const items = stopIntercom(stopState({ pendingAsks: tracker.listPending(2000) }), { deadline: farDeadline() }); assert.deepEqual(items, [{ source: "intercom", kind: "inbound_ask", id: "ask-1", detail: "cannot cancel inbound: ask from planner is awaiting our reply and keeps waiting", ok: false, error: "cannot cancel inbound", }]); assert.equal(tracker.listPending(2000).length, 1, "the ask is still pending"); }); test("stop: appends the recipient-side outcome when the best-effort message cancel settles within the deadline", async () => { const items = await stopIntercom( stopState({ outboundAsk: { from: "planner", replyTo: "reply-9" }, cancelOutboundAsk: () => Promise.resolve("recipient asked to drop it") }), { deadline: farDeadline() }, ); assert.deepEqual(items, [{ source: "intercom", kind: "outbound_ask", id: "reply-9", detail: "Cancelled blocking ask to planner; recipient asked to drop it", ok: true }]); }); test("stop: a hanging or rejecting recipient-side cancel does not change the result or outlive the deadline", async () => { const hanging = await stopIntercom( stopState({ outboundAsk: { from: "planner", replyTo: "reply-9" }, cancelOutboundAsk: () => new Promise(() => undefined) }), { deadline: Date.now() + 40 }, ); assert.deepEqual(hanging.map(item => [item.kind, item.ok, item.detail]), [["outbound_ask", true, "Cancelled blocking ask to planner"]]); const rejecting = await stopIntercom( stopState({ outboundAsk: { from: "planner", replyTo: "reply-9" }, cancelOutboundAsk: () => Promise.reject(new Error("Cancel timeout")) }), { deadline: farDeadline() }, ); assert.deepEqual(rejecting.map(item => [item.kind, item.ok]), [["outbound_ask", true]]); });