| ... | @@ -304,6 +304,64 @@ describe("ProviderCommandReactor", () => { | ... | @@ -304,6 +304,64 @@ describe("ProviderCommandReactor", () => { |
| 304 | expect(thread?.session?.sandboxMode).toBe("workspace-write"); | 304 | expect(thread?.session?.sandboxMode).toBe("workspace-write"); |
| 305 | }); | 305 | }); |
| 306 | | 306 | |
| | 307 | it("does not stop the active session when restart fails before rebind", async () => { |
| | 308 | const harness = await createHarness(); |
| | 309 | const now = new Date().toISOString(); |
| | 310 | |
| | 311 | await Effect.runPromise( |
| | 312 | harness.engine.dispatch({ |
| | 313 | type: "thread.turn.start", |
| | 314 | commandId: CommandId.makeUnsafe("cmd-turn-start-restart-failure-1"), |
| | 315 | threadId: ThreadId.makeUnsafe("thread-1"), |
| | 316 | message: { |
| | 317 | messageId: asMessageId("user-message-restart-failure-1"), |
| | 318 | role: "user", |
| | 319 | text: "first", |
| | 320 | attachments: [], |
| | 321 | }, |
| | 322 | approvalPolicy: "never", |
| | 323 | sandboxMode: "danger-full-access", |
| | 324 | createdAt: now, |
| | 325 | }), |
| | 326 | ); |
| | 327 | |
| | 328 | await waitFor(() => harness.startSession.mock.calls.length === 1); |
| | 329 | await waitFor(() => harness.sendTurn.mock.calls.length === 1); |
| | 330 | |
| | 331 | harness.startSession.mockImplementationOnce((_: unknown, __: unknown) => |
| | 332 | Effect.fail(new Error("simulated restart failure")) as never, |
| | 333 | ); |
| | 334 | |
| | 335 | await Effect.runPromise( |
| | 336 | harness.engine.dispatch({ |
| | 337 | type: "thread.turn.start", |
| | 338 | commandId: CommandId.makeUnsafe("cmd-turn-start-restart-failure-2"), |
| | 339 | threadId: ThreadId.makeUnsafe("thread-1"), |
| | 340 | message: { |
| | 341 | messageId: asMessageId("user-message-restart-failure-2"), |
| | 342 | role: "user", |
| | 343 | text: "second", |
| | 344 | attachments: [], |
| | 345 | }, |
| | 346 | approvalPolicy: "on-request", |
| | 347 | sandboxMode: "workspace-write", |
| | 348 | createdAt: now, |
| | 349 | }), |
| | 350 | ); |
| | 351 | |
| | 352 | await waitFor(() => harness.startSession.mock.calls.length === 2); |
| | 353 | await Effect.runPromise(Effect.sleep("30 millis")); |
| | 354 | |
| | 355 | expect(harness.stopSession.mock.calls.length).toBe(0); |
| | 356 | expect(harness.sendTurn.mock.calls.length).toBe(1); |
| | 357 | |
| | 358 | const readModel = await Effect.runPromise(harness.engine.getReadModel()); |
| | 359 | const thread = readModel.threads.find((entry) => entry.id === ThreadId.makeUnsafe("thread-1")); |
| | 360 | expect(thread?.session?.providerSessionId).toBe("sess-1"); |
| | 361 | expect(thread?.session?.approvalPolicy).toBe("never"); |
| | 362 | expect(thread?.session?.sandboxMode).toBe("danger-full-access"); |
| | 363 | }); |
| | 364 | |
| 307 | it("reacts to thread.turn.interrupt-requested by calling provider interrupt", async () => { | 365 | it("reacts to thread.turn.interrupt-requested by calling provider interrupt", async () => { |
| 308 | const harness = await createHarness(); | 366 | const harness = await createHarness(); |
| 309 | const now = new Date().toISOString(); | 367 | const now = new Date().toISOString(); |