import { type AssistantMessageFrame, createModels, fauxAssistantMessage, fauxProvider } from "@earendil-works/pi-ai"; import { afterEach, describe, expect, it, vi } from "vitest"; import { type HarnessEvent, HarnessFault, type WatchHandle } from "../../../src/harness/agent-harness.ts"; import { DEFAULT_COMPACTION_SETTINGS } from "../../../src/harness/compaction/compaction.ts"; import { BACKGROUND_CONTEXT, type Context, createContextKey, withContextValue } from "../../../src/harness/context.ts"; import { createAgentHarness, Harness } from "../../../src/harness/runtime/harness.ts"; import { Lane } from "../../../src/harness/runtime/lane.ts"; import * as sessionWrites from "../../../src/harness/session/commit.ts"; import { MemoryStorage } from "../../../src/harness/session/memory.ts"; import { StorageBackedSession } from "../../../src/harness/session/session.ts"; import type { OperationMeta, OperationScope, OperationState, Session, StorageBranchScan, Write, } from "../../../src/harness/session/types.ts"; import * as storedValues from "../../../src/harness/session/values.ts"; import { deferred } from "./test-utils.ts"; const sessions: Session[] = []; const configuration = { model: { provider: "configured", modelId: "model" }, thinkingLevel: "off" as const, activeToolNames: [], }; async function createSession(storage: MemoryStorage = new MemoryStorage()): Promise { const session = new StorageBackedSession( { id: `watch-${sessions.length}`, createdAt: 1, storageVersion: 1 }, storage, ); sessions.push(session); await session.mutate( (mutator) => mutator.commit( [ storedValues.setValue(storedValues.branchTip("main"), null), storedValues.setValue(storedValues.laneConfig("main"), configuration), storedValues.setValue(storedValues.laneState("main"), { currentOperationId: null, lastOperationId: null, inbox: [], }), ], BACKGROUND_CONTEXT, ), BACKGROUND_CONTEXT, ); return session; } async function commit(session: Session, writes: Write[]): Promise { await session.mutate((mutator) => mutator.commit(writes, BACKGROUND_CONTEXT), BACKGROUND_CONTEXT); } function runScope(control: OperationScope["control"] = { status: "running" }): OperationScope { return { control, settings: { compaction: DEFAULT_COMPACTION_SETTINGS, steeringMode: "all", followUpMode: "all", toolExecution: "parallel", }, latestAssistantEntryId: null, }; } async function attach( session: Session, ): Promise & Pick, "events">> { const provider = fauxProvider(); const models = createModels(); models.setProvider(provider.provider); const { harness } = await createAgentHarness({ session, models, model: provider.getModel() }, BACKGROUND_CONTEXT); if (!(harness instanceof Harness)) throw new Error("Expected runtime Harness"); const lane = await harness.lane("main", BACKGROUND_CONTEXT); if (!(lane instanceof Lane)) throw new Error("Expected runtime Lane"); return Object.assign(lane, { events: harness.events }); } class BlockingScanStorage extends MemoryStorage { block = false; readonly started = deferred(); readonly release = deferred(); override async scanBranch(query: StorageBranchScan, context: Context) { if (this.block) { this.started.resolve(); await this.release.promise; } return super.scanBranch(query, context); } } afterEach(async () => { for (const session of sessions.splice(0)) await session.close(BACKGROUND_CONTEXT); }); describe("runtime lane watch", () => { it("captures a compaction-bounded transcript and isolates the returned snapshot", async () => { const session = await createSession(); await commit(session, [ sessionWrites.insertEntry({ id: "root", parentId: null, type: "custom", customType: "root" }), sessionWrites.insertEntry({ id: "compact", parentId: "root", type: "compaction", summary: "summary", retainedTail: [], tokensBefore: 10, fromHook: false, }), sessionWrites.insertEntry({ id: "after", parentId: "compact", type: "message", message: { role: "user", content: "after", timestamp: 2 }, }), storedValues.setValue(storedValues.branchTip("main"), "after"), ]); const harness = await attach(session); const first = await harness.watch(BACKGROUND_CONTEXT); expect(first.snapshot.transcript.map(({ id }) => id)).toEqual(["compact", "after"]); expect(first.snapshot).toMatchObject({ lane: "main", tipId: "after", configuration, stats: { messageCount: 1 }, operation: null, queues: [], faulted: false, }); first.snapshot.transcript.length = 0; first.snapshot.tipId = null; const second = await harness.watch(BACKGROUND_CONTEXT); expect(second.snapshot.transcript.map(({ id }) => id)).toEqual(["compact", "after"]); expect(second.snapshot.tipId).toBe("after"); first.unsubscribe(); second.unsubscribe(); }); it("dereferences queues, pending writes, and deferred handles", async () => { const session = await createSession(); const handle = { provider: "provider", modelId: "model", api: "test", id: "deferred" }; const sourceMessage = fauxAssistantMessage([], { stopReason: "deferred", deferred: handle }); const ids = { next: "next", steer: "steer", follow: "follow", write: "write", }; const payload = (content: string) => ({ type: "message" as const, payload: { role: "user" as const, content, timestamp: 1 }, }); const operationId = session.idGenerator.next(); const state: OperationState = { ...runScope({ status: "cancel_requested", requestedAt: 2 }), at: "deferred.suspended", stepId: "step", sourceEntryId: "source", poll: 3, configuration, streamOptions: {}, }; const meta: OperationMeta = { operationId, lane: "main", sourceTipId: null, startedAt: 1, intent: { kind: "run", promptEntryIds: [] }, }; await commit(session, [ sessionWrites.insertEntry({ id: "source", parentId: null, type: "message", message: sourceMessage }), ...Object.values(ids).map((id) => storedValues.setValue( storedValues.pendingEntry(id), id === ids.write ? { type: "custom", customType: "note", payload: { id } } : payload(id), ), ), storedValues.setValue(storedValues.operationMeta(operationId), meta), storedValues.setValue(storedValues.operationState(operationId), state), storedValues.setValue(storedValues.branchTip("main"), "source"), storedValues.setValue(storedValues.laneState("main"), { currentOperationId: operationId, lastOperationId: null, inbox: [ { entryId: ids.next, kind: "nextRun" }, { entryId: ids.steer, kind: "steer" }, { entryId: ids.follow, kind: "followUp" }, { entryId: ids.write, kind: "write" }, ], }), ]); const harness = await attach(session); const watch = await harness.watch(BACKGROUND_CONTEXT); expect(watch.snapshot.queues).toMatchObject([ { entryId: ids.next, kind: "nextRun", type: "message", message: { content: ids.next } }, { entryId: ids.steer, kind: "steer", type: "message", message: { content: ids.steer } }, { entryId: ids.follow, kind: "followUp", type: "message", message: { content: ids.follow } }, { entryId: ids.write, kind: "write", type: "custom", customType: "note", data: { id: ids.write } }, ]); expect(watch.snapshot.operation).toMatchObject({ id: operationId, status: "aborting", deferred: { handle, poll: 3 }, runningTools: [], }); watch.unsubscribe(); }); it("omits streaming presentation when an effect-pending response has no frames", async () => { const session = await createSession(); const operationId = session.idGenerator.next(); await commit(session, [ storedValues.setValue(storedValues.operationMeta(operationId), { operationId, lane: "main", sourceTipId: null, startedAt: 1, intent: { kind: "run", promptEntryIds: [] }, }), storedValues.setValue(storedValues.operationState(operationId), { ...runScope(), at: "assistant.effect_pending", generationContext: { stepId: "step", triggerEntryId: "trigger", configuration, streamOptions: {}, retryPolicy: { maxAttempts: 2, baseDelayMs: 0 }, overflowRecoveryUsed: false, }, attempt: 1, responseEntryId: "response-without-frames", usageId: "usage", intendedOutputLimit: 100, contextWindow: 1000, }), storedValues.setValue(storedValues.laneState("main"), { currentOperationId: operationId, lastOperationId: null, inbox: [], }), ]); const harness = await attach(session); const watch = await harness.watch(BACKGROUND_CONTEXT); expect(watch.snapshot.operation).not.toHaveProperty("streamingMessage"); watch.unsubscribe(); }); it("reduces assistant frames and projects running and settled tools with full-content indexes", async () => { const frameSession = await createSession(); const partial = fauxAssistantMessage([], { stopReason: "pending" }); const frames: AssistantMessageFrame[] = [ { type: "start", partial }, { type: "text_start", contentIndex: 0, content: { type: "text", text: "" } }, { type: "text_delta", contentIndex: 0, delta: "partial" }, ]; const frameOperationId = frameSession.idGenerator.next(); await commit(frameSession, [ storedValues.setValue(storedValues.operationMeta(frameOperationId), { operationId: frameOperationId, lane: "main", sourceTipId: null, startedAt: 1, intent: { kind: "run", promptEntryIds: [] }, }), storedValues.setValue(storedValues.operationState(frameOperationId), { ...runScope(), at: "assistant.effect_pending", generationContext: { stepId: "step", triggerEntryId: "trigger", configuration, streamOptions: {}, retryPolicy: { maxAttempts: 2, baseDelayMs: 0 }, overflowRecoveryUsed: false, }, attempt: 1, responseEntryId: "response", usageId: "usage", intendedOutputLimit: 100, contextWindow: 1000, }), ...frames.map((frame) => storedValues.appendList(storedValues.pendingAssistantFrames(frameOperationId, "response"), frame), ), storedValues.setValue(storedValues.laneState("main"), { currentOperationId: frameOperationId, lastOperationId: null, inbox: [], }), ]); const frameHarness = await attach(frameSession); const frameWatch = await frameHarness.watch(BACKGROUND_CONTEXT); expect(frameWatch.snapshot.operation?.streamingMessage?.content).toEqual([{ type: "text", text: "partial" }]); frameWatch.unsubscribe(); const toolSession = await createSession(); const toolUsage = { input: 1, output: 2, cacheRead: 3, cacheWrite: 4, totalTokens: 10, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }, }; const assistant = fauxAssistantMessage([ { type: "text", text: "before" }, { type: "toolCall", id: "call-completed", name: "completed", arguments: { source: "completed" } }, { type: "toolCall", id: "call-running", name: "read", arguments: { source: "running" } }, { type: "toolCall", id: "call-without-checkpoint", name: "write", arguments: { source: "without-checkpoint" }, }, { type: "toolCall", id: "call-ready", name: "real", arguments: { source: "real" } }, { type: "toolCall", id: "call-synthetic", name: "missing", arguments: { source: "synthetic" } }, { type: "toolCall", id: "call-planned", name: "planned", arguments: { source: "planned" } }, ]); const toolOperationId = toolSession.idGenerator.next(); await commit(toolSession, [ sessionWrites.insertEntry({ id: "assistant", parentId: null, type: "message", message: assistant }), storedValues.setValue(storedValues.operationMeta(toolOperationId), { operationId: toolOperationId, lane: "main", sourceTipId: null, startedAt: 1, intent: { kind: "run", promptEntryIds: [] }, }), storedValues.setValue(storedValues.operationState(toolOperationId), { ...runScope(), at: "tools", batch: { assistantEntryId: "assistant", configuration, turnId: "turn", calls: [ { status: "completed", sourceIndex: 1, resultEntryId: "completed", terminate: false }, { status: "effect_pending", sourceIndex: 2, resultEntryId: "result", replay: "safe" }, { status: "effect_pending", sourceIndex: 3, resultEntryId: "without-checkpoint", replay: "never", }, { status: "outcome_ready", sourceIndex: 4, resultEntryId: "ready", terminate: true }, { status: "outcome_ready", sourceIndex: 5, resultEntryId: "synthetic", terminate: false }, { status: "planned", sourceIndex: 6, resultEntryId: "planned" }, ], }, }), sessionWrites.insertEntry({ id: "completed", parentId: "assistant", type: "message", message: { role: "toolResult", toolCallId: "call-completed", toolName: "completed", content: [{ type: "text", text: "completed" }], isError: false, timestamp: 2, }, }), storedValues.setValue(storedValues.operationToolArgs(toolOperationId, "turn", 2), { path: "file" }), storedValues.setValue(storedValues.operationToolArgs(toolOperationId, "turn", 3), { path: "output" }), storedValues.setValue(storedValues.operationToolArgs(toolOperationId, "turn", 4), { path: "settled" }), storedValues.setValue(storedValues.pendingToolOutput(toolOperationId, "result"), { content: [{ type: "text", text: "partial" }], details: { bytes: 1 }, }), storedValues.setValue(storedValues.pendingEntry("ready"), { type: "message", payload: { role: "toolResult", toolCallId: "call-ready", toolName: "real", content: [{ type: "text", text: "settled" }], details: { kind: "real" }, usage: toolUsage, addedToolNames: ["later"], isError: false, timestamp: 3, }, }), storedValues.setValue(storedValues.pendingEntry("synthetic"), { type: "message", payload: { role: "toolResult", toolCallId: "call-synthetic", toolName: "missing", content: [{ type: "text", text: "unavailable" }], isError: true, timestamp: 4, }, }), storedValues.setValue(storedValues.branchTip("main"), "completed"), storedValues.setValue(storedValues.laneState("main"), { currentOperationId: toolOperationId, lastOperationId: null, inbox: [], }), ]); const toolHarness = await attach(toolSession); const toolWatch = await toolHarness.watch(BACKGROUND_CONTEXT); expect(toolWatch.snapshot.transcript.map(({ id }) => id)).toEqual(["assistant", "completed"]); expect(toolWatch.snapshot.operation?.runningTools).toEqual([ { status: "running", toolCallId: "call-running", toolName: "read", args: { path: "file" }, result: { content: [{ type: "text", text: "partial" }], details: { bytes: 1 } }, }, { status: "running", toolCallId: "call-without-checkpoint", toolName: "write", args: { path: "output" }, }, { status: "settled", toolCallId: "call-ready", toolName: "real", args: { path: "settled" }, result: { content: [{ type: "text", text: "settled" }], details: { kind: "real" }, usage: toolUsage, addedToolNames: ["later"], terminate: true, }, isError: false, }, { status: "settled", toolCallId: "call-synthetic", toolName: "missing", args: { source: "synthetic" }, result: { content: [{ type: "text", text: "unavailable" }], details: undefined, }, isError: true, }, ]); expect(toolWatch.snapshot.operation?.runningTools[1]).not.toHaveProperty("result"); toolWatch.unsubscribe(); }); it("faults missing or mismatched staged outcome-ready results", async () => { for (const corruption of ["missing", "mismatched"] as const) { const session = await createSession(); const operationId = session.idGenerator.next(); const writes: Write[] = [ sessionWrites.insertEntry({ id: "assistant", parentId: null, type: "message", message: fauxAssistantMessage([ { type: "toolCall", id: "call", name: "read", arguments: { path: "file" } }, ]), }), storedValues.setValue(storedValues.operationMeta(operationId), { operationId, lane: "main", sourceTipId: null, startedAt: 1, intent: { kind: "run", promptEntryIds: [] }, }), storedValues.setValue(storedValues.operationState(operationId), { ...runScope(), at: "tools", batch: { assistantEntryId: "assistant", configuration, turnId: "turn", calls: [{ status: "outcome_ready", sourceIndex: 0, resultEntryId: "result", terminate: false }], }, }), storedValues.setValue(storedValues.branchTip("main"), "assistant"), storedValues.setValue(storedValues.laneState("main"), { currentOperationId: operationId, lastOperationId: null, inbox: [], }), ]; if (corruption === "mismatched") { writes.push( storedValues.setValue(storedValues.pendingEntry("result"), { type: "message", payload: { role: "toolResult", toolCallId: "other-call", toolName: "read", content: [], isError: false, timestamp: 2, }, }), ); } await commit(session, writes); const harness = await attach(session); await expect(harness.watch(BACKGROUND_CONTEXT)).rejects.toBeInstanceOf(HarnessFault); } }); it("faults required payload corruption and unsubscribes the incomplete watcher", async () => { const session = await createSession(); await commit(session, [ storedValues.setValue(storedValues.laneState("main"), { currentOperationId: null, lastOperationId: null, inbox: [{ entryId: "missing", kind: "nextRun" }], }), ]); const harness = await attach(session); let unsubscribed = false; const originalWatch = harness.events.watch.bind(harness.events); vi.spyOn(harness.events, "watch").mockImplementation( (snapshot: T, filter: (event: HarnessEvent) => boolean, context: Context): WatchHandle => { const handle = originalWatch(snapshot, filter, context); return { ...handle, unsubscribe: () => { unsubscribed = true; handle.unsubscribe(); }, }; }, ); await expect(harness.watch(BACKGROUND_CONTEXT)).rejects.toBeInstanceOf(HarnessFault); expect(unsubscribed).toBe(true); }); it("returns snapshot-before plus buffered events when watch wins the lane line", async () => { const storage = new BlockingScanStorage(); const session = await createSession(storage); await commit(session, [ sessionWrites.insertEntry({ id: "root", parentId: null, type: "custom", customType: "root" }), storedValues.setValue(storedValues.branchTip("main"), "root"), ]); const harness = await attach(session); storage.block = true; const watchPromise = harness.watch(BACKGROUND_CONTEXT); await storage.started.promise; const sourceKey = createContextKey("watch.event.source"); const sourceContext = withContextValue(sourceKey, "append", BACKGROUND_CONTEXT); const append = harness.appendMessage({ role: "user", content: "later", timestamp: 2 }, sourceContext); storage.release.resolve(); const watch = await watchPromise; expect(watch.snapshot.transcript.map(({ id }) => id)).toEqual(["root"]); const seen: Array<{ type: string; sameContext: boolean }> = []; watch.start((event, eventContext) => { seen.push({ type: event.type, sameContext: eventContext === sourceContext }); }); await append; await new Promise((resolve) => setTimeout(resolve, 0)); expect(seen.map(({ type }) => type)).toEqual(["message_start", "message_end", "entry_added"]); expect(seen.every(({ sameContext }) => sameContext)).toBe(true); watch.unsubscribe(); }); it("returns snapshot-after without replay when publication wins", async () => { const session = await createSession(); const harness = await attach(session); const entryId = await harness.appendMessage( { role: "user", content: "existing", timestamp: 1 }, BACKGROUND_CONTEXT, ); const watch = await harness.watch(BACKGROUND_CONTEXT); const seen = vi.fn(); watch.start(seen); await new Promise((resolve) => setTimeout(resolve, 0)); expect(watch.snapshot.transcript.map(({ id }) => id)).toEqual([entryId]); expect(seen).not.toHaveBeenCalled(); watch.unsubscribe(); }); });