| ... | @@ -559,17 +559,98 @@ it.layer(OpenCodeAdapterTestLayer)("OpenCodeAdapterLive", (it) => { | ... | @@ -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 | Effect.sync(() => { | 563 | Effect.sync(() => { |
| 564 | const firstUpdate = mergeOpenCodeAssistantText(undefined, "Hello"); | 564 | const firstUpdate = mergeOpenCodeAssistantText(undefined, "Hello"); |
| 565 | const overlapDelta = appendOpenCodeAssistantTextDelta(firstUpdate.latestText, "lo world"); | 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 | assert.deepEqual( | 568 | assert.deepEqual( |
| 569 | [firstUpdate.deltaToEmit, overlapDelta.deltaToEmit, secondUpdate.deltaToEmit], | 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 | |