| ... | ... | @@ -559,17 +559,98 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => { |
| 559 | 559 | }), |
| 560 | 560 | ); |
| 561 | 561 | |
| 562 | | it.effect("deduplicates overlapping assistant text deltas after part updates", () => |
| 562 | it.effect("appends raw assistant text deltas and reconciles part update snapshots", () => |
| 563 | 563 | Effect.sync(() => { |
| 564 | 564 | const firstUpdate = mergeOpenCodeAssistantText(undefined, "Hello"); |
| 565 | 565 | const overlapDelta = appendOpenCodeAssistantTextDelta(firstUpdate.latestText, "lo world"); |
| 566 | | const secondUpdate = mergeOpenCodeAssistantText(overlapDelta.nextText, "Hello world!"); |
| 566 | const secondUpdate = mergeOpenCodeAssistantText(overlapDelta.nextText, "Hellolo world"); |
| 567 | 567 | |
| 568 | 568 | assert.deepEqual( |
| 569 | 569 | [firstUpdate.deltaToEmit, overlapDelta.deltaToEmit, secondUpdate.deltaToEmit], |
| 570 | | ["Hello", " world", "!"], |
| 570 | ["Hello", "lo world", ""], |
| 571 | 571 | ); |
| 572 | | assert.equal(secondUpdate.latestText, "Hello world!"); |
| 572 | assert.equal(secondUpdate.latestText, "Hellolo world"); |
| 573 | }), |
| 574 | ); |
| 575 | |
| 576 | it.effect("does not strip coincidental prefix overlap from OpenCode part deltas", () => |
| 577 | Effect.gen(function* () { |
| 578 | const adapter = yield* OpenCodeAdapter; |
| 579 | const threadId = asThreadId("thread-opencode-raw-delta"); |
| 580 | const part = { |
| 581 | id: "part-raw-delta", |
| 582 | sessionID: "http://127.0.0.1:9999/session", |
| 583 | messageID: "msg-raw-delta", |
| 584 | type: "text", |
| 585 | text: "A B", |
| 586 | time: { start: 1 }, |
| 587 | }; |
| 588 | runtimeMock.state.subscribedEvents = [ |
| 589 | { |
| 590 | type: "message.updated", |
| 591 | properties: { |
| 592 | sessionID: "http://127.0.0.1:9999/session", |
| 593 | info: { |
| 594 | id: "msg-raw-delta", |
| 595 | role: "assistant", |
| 596 | }, |
| 597 | }, |
| 598 | }, |
| 599 | { |
| 600 | type: "message.part.updated", |
| 601 | properties: { |
| 602 | sessionID: "http://127.0.0.1:9999/session", |
| 603 | part, |
| 604 | time: 1, |
| 605 | }, |
| 606 | }, |
| 607 | { |
| 608 | type: "message.part.delta", |
| 609 | properties: { |
| 610 | sessionID: "http://127.0.0.1:9999/session", |
| 611 | messageID: "msg-raw-delta", |
| 612 | partID: "part-raw-delta", |
| 613 | field: "text", |
| 614 | delta: "Bonus", |
| 615 | }, |
| 616 | }, |
| 617 | { |
| 618 | type: "message.part.updated", |
| 619 | properties: { |
| 620 | sessionID: "http://127.0.0.1:9999/session", |
| 621 | part: { |
| 622 | ...part, |
| 623 | text: "A BBonus", |
| 624 | time: { start: 1, end: 2 }, |
| 625 | }, |
| 626 | time: 2, |
| 627 | }, |
| 628 | }, |
| 629 | ]; |
| 630 | const eventsFiber = yield* adapter.streamEvents.pipe( |
| 631 | Stream.filter((event) => event.threadId === threadId), |
| 632 | Stream.take(5), |
| 633 | Stream.runCollect, |
| 634 | Effect.forkChild, |
| 635 | ); |
| 636 | |
| 637 | yield* adapter.startSession({ |
| 638 | provider: ProviderDriverKind.make("opencode"), |
| 639 | threadId, |
| 640 | runtimeMode: "full-access", |
| 641 | }); |
| 642 | |
| 643 | const events = Array.from(yield* Fiber.join(eventsFiber).pipe(Effect.timeout("1 second"))); |
| 644 | const deltas = events.filter((event) => event.type === "content.delta"); |
| 645 | assert.deepEqual( |
| 646 | deltas.map((event) => (event.type === "content.delta" ? event.payload.delta : "")), |
| 647 | ["A B", "Bonus"], |
| 648 | ); |
| 649 | assert.equal(events.at(-1)?.type, "item.completed"); |
| 650 | const completed = events.at(-1); |
| 651 | if (completed?.type === "item.completed") { |
| 652 | assert.equal(completed.payload.detail, "A BBonus"); |
| 653 | } |
| 573 | 654 | }), |
| 574 | 655 | ); |
| 575 | 656 | |