(
opts: {
/** adapter.run throws (simulates agent backend crash). */
adapterThrow?: string
/** adapter.run return value (default ok). */
adapterResult?: AgentRunResult
/** agentRunner.runAgentToResult return value (fallback path, default throws). */
runnerResult?: AgentRunResult
} = {},
)
| 37 | | { kind: 'killAgent'; runId: string; agentId: number } |
| 38 | |
| 39 | function fakePorts( |
| 40 | opts: { |
| 41 | /** adapter.run throws (simulates agent backend crash). */ |
| 42 | adapterThrow?: string |
| 43 | /** adapter.run return value (default ok). */ |
| 44 | adapterResult?: AgentRunResult |
| 45 | /** agentRunner.runAgentToResult return value (fallback path, default throws). */ |
| 46 | runnerResult?: AgentRunResult |
| 47 | } = {}, |
| 48 | ): { |
| 49 | ports: WorkflowPorts |
| 50 | store: ReturnType<typeof createProgressStoreFromBus> |
| 51 | killed: string[] |
| 52 | /** taskRegistrar call records (complete/fail/kill/registerAgentAbort/...). */ |
| 53 | calls: RegistrarCall[] |
| 54 | /** runId → (agentId → AbortController). Used by tests to simulate backend registration. */ |
| 55 | agentBindings: Map<string, Map<number, AbortController>> |
| 56 | /** adapter.run call count (accumulates on retry). holder reference, tests read adapterCalls.value. */ |
| 57 | adapterCallsRef: { value: number } |
| 58 | } { |
| 59 | const bus = createProgressBus() |
| 60 | const store = createProgressStoreFromBus(bus) |
| 61 | const killed: string[] = [] |
| 62 | const calls: RegistrarCall[] = [] |
| 63 | const bindings = new Map<string, { abort: AbortController }>() |
| 64 | // agentId → AbortController (per runId). killAgent uses this to abort precisely. |
| 65 | const agentBindings = new Map<string, Map<number, AbortController>>() |
| 66 | // adapter.run call count (accumulates on retry). Use holder object to avoid closure/getter |
| 67 | // snapshot semantics issues in Bun test runner — when returning, shorthand takes the current value (=0), |
| 68 | // subsequent outer variable ++ does not reflect into the returned object field. holder reference is stable. |
| 69 | const adapterCallsRef = { value: 0 } |
| 70 | let seq = 0 |
| 71 | const ports = { |
| 72 | // hostFactory is not actually called by the service.launch path (service builds its own host handle), |
| 73 | // but the WorkflowPorts type requires it to exist; keep a minimal impl. |
| 74 | hostFactory: () => ({ |
| 75 | handle: {} as never, |
| 76 | cwd: '/tmp', |
| 77 | budgetTotal: null, |
| 78 | toolUseId: 'tu', |
| 79 | }), |
| 80 | agentAdapterRegistry: { |
| 81 | resolve: () => ({ |
| 82 | id: 'claude-code', |
| 83 | capabilities: { structuredOutput: true }, |
| 84 | run: |
| 85 | opts.adapterThrow !== undefined |
| 86 | ? async (): Promise<AgentRunResult> => { |
| 87 | adapterCallsRef.value++ |
| 88 | throw new Error(opts.adapterThrow) |
| 89 | } |
| 90 | : async (): Promise<AgentRunResult> => { |
| 91 | adapterCallsRef.value++ |
| 92 | return ( |
| 93 | opts.adapterResult ?? { |
| 94 | kind: 'ok', |
| 95 | output: 'mock-out', |
| 96 | usage: { outputTokens: 1 }, |
no test coverage detected