| ... | ... | @@ -1557,6 +1557,73 @@ describe("ClaudeAdapterLive", () => { |
| 1557 | 1557 | ); |
| 1558 | 1558 | }); |
| 1559 | 1559 | |
| 1560 | it.effect("keeps a turn open past the result of a Claude-initiated turn", () => { |
| 1561 | const harness = makeHarness(); |
| 1562 | return Effect.gen(function* () { |
| 1563 | const adapter = yield* ClaudeAdapter; |
| 1564 | const runtimeEventsFiber = yield* adapter.streamEvents.pipe( |
| 1565 | Stream.takeUntil((event) => event.type === "session.exited"), |
| 1566 | Stream.runCollect, |
| 1567 | Effect.forkChild, |
| 1568 | ); |
| 1569 | |
| 1570 | const session = yield* adapter.startSession({ |
| 1571 | threadId: THREAD_ID, |
| 1572 | provider: ProviderDriverKind.make("claudeAgent"), |
| 1573 | runtimeMode: "full-access", |
| 1574 | }); |
| 1575 | const turn = yield* adapter.sendTurn({ |
| 1576 | threadId: session.threadId, |
| 1577 | input: "/compact", |
| 1578 | attachments: [], |
| 1579 | }); |
| 1580 | |
| 1581 | // Recorded order after a resume: Claude first reports a background task |
| 1582 | // the previous process left behind, then runs the queued `/compact`. |
| 1583 | harness.query.emit({ |
| 1584 | type: "result", |
| 1585 | subtype: "success", |
| 1586 | is_error: false, |
| 1587 | num_turns: 0, |
| 1588 | origin: { kind: "task-notification" }, |
| 1589 | session_id: "sdk-session-1", |
| 1590 | uuid: "result-task-notification", |
| 1591 | } as unknown as SDKMessage); |
| 1592 | harness.query.emit({ |
| 1593 | type: "system", |
| 1594 | subtype: "compact_boundary", |
| 1595 | compact_metadata: { trigger: "manual", pre_tokens: 959489, post_tokens: 10107 }, |
| 1596 | session_id: "sdk-session-1", |
| 1597 | uuid: "compact-boundary", |
| 1598 | } as unknown as SDKMessage); |
| 1599 | harness.query.emit({ |
| 1600 | type: "result", |
| 1601 | subtype: "success", |
| 1602 | is_error: false, |
| 1603 | num_turns: 0, |
| 1604 | user_message_uuid: turn.turnId, |
| 1605 | user_message_uuids: [turn.turnId], |
| 1606 | local_command: "compact", |
| 1607 | session_id: "sdk-session-1", |
| 1608 | uuid: "result-compact", |
| 1609 | } as unknown as SDKMessage); |
| 1610 | harness.query.finish(); |
| 1611 | |
| 1612 | const runtimeEvents = Array.from(yield* Fiber.join(runtimeEventsFiber)); |
| 1613 | const compactedIndex = runtimeEvents.findIndex( |
| 1614 | (event) => event.type === "thread.state.changed" && event.payload.state === "compacted", |
| 1615 | ); |
| 1616 | const completedIndex = runtimeEvents.findIndex((event) => event.type === "turn.completed"); |
| 1617 | assert.equal(runtimeEvents.filter((event) => event.type === "turn.completed").length, 1); |
| 1618 | assert.equal(String(runtimeEvents[completedIndex]?.turnId), String(turn.turnId)); |
| 1619 | assert.equal(String(runtimeEvents[compactedIndex]?.turnId), String(turn.turnId)); |
| 1620 | assert.isAbove(completedIndex, compactedIndex); |
| 1621 | }).pipe( |
| 1622 | Effect.provideService(Random.Random, makeDeterministicRandomService()), |
| 1623 | Effect.provide(harness.layer), |
| 1624 | ); |
| 1625 | }); |
| 1626 | |
| 1560 | 1627 | it.effect("steers a running turn instead of opening a new one on mid-turn sendTurn", () => { |
| 1561 | 1628 | const harness = makeHarness(); |
| 1562 | 1629 | return Effect.gen(function* () { |