Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,7 @@ The first version stabilizes at least the following diagnostic codes. Codes are
| malformed_part | true | A recognized malformed part was skipped |
| unknown_record_or_part | true | An unknown extension may contain visible data |
| ambiguous_tool_call_correlation | true | A tool result cannot be correlated uniquely |
| missing_tool_result | true | A tool call has no persisted result |
| presentation_fallback | false | Presentation adapter failed; fallback used |
| transcript_blocks_truncated | true | `maxBlocks` removed older blocks |
| transcript_text_truncated | true | A text block exceeded the character limit |
Expand Down
10 changes: 9 additions & 1 deletion packages/acp-bridge/src/transcript-replay.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ describe('createTranscriptReplayMachine', () => {
});

it('uses stable synthetic ids and finalizes dangling calls once', () => {
const machine = createTranscriptReplayMachine();
const onDiagnostic = vi.fn();
const machine = createTranscriptReplayMachine({ onDiagnostic });
const projected = updates(
machine,
record('assistant-1', 'assistant', {
Expand Down Expand Up @@ -88,6 +89,13 @@ describe('createTranscriptReplayMachine', () => {
},
],
});
expect(onDiagnostic).toHaveBeenCalledWith(
expect.objectContaining({
code: 'missing_tool_result',
affectsCompleteness: true,
recordId: 'assistant-1',
}),
);
expect([...machine.finalize()]).toEqual([]);
});

Expand Down
5 changes: 5 additions & 0 deletions packages/acp-bridge/src/transcript-replay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,11 @@ class DefaultTranscriptReplayMachine implements TranscriptReplayMachine {
let ordinal = 0;
for (const pending of [...this.pendingToolCalls.values()]) {
this.pendingToolCalls.delete(pending.callId);
this.report(
'missing_tool_result',
'A transcript tool call has no persisted result.',
pending.sourceRecordId,
);
yield {
sourceRecordId: pending.sourceRecordId,
...(pending.sourceTimestamp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ describe('projectChatRecordsToDaemonTranscript', () => {
expect(projection.complete).toBe(true);
});

it('finalizes dangling tools as failed', () => {
it('finalizes dangling tools as failed and marks the projection incomplete', () => {
const projection = projectChatRecordsToDaemonTranscript([
record('tool-start', null, {
type: 'assistant',
Expand All @@ -293,6 +293,14 @@ describe('projectChatRecordsToDaemonTranscript', () => {
status: 'failed',
toolCallId: 'qwen-replay-tool:tool-start:0',
});
expect(projection.complete).toBe(false);
expect(projection.diagnostics).toContainEqual(
expect.objectContaining({
code: 'missing_tool_result',
affectsCompleteness: true,
recordId: 'tool-start',
}),
);
});

it('preserves assistant usage when the record ends with a tool call', () => {
Expand Down
Loading