@spexcode/transcript 0.7.0-next.5 → 0.7.0-next.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/parsers.d.ts +3 -0
- package/dist/parsers.js +41 -8
- package/dist/turns.d.ts +1 -0
- package/package.json +1 -1
package/dist/parsers.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export type MutableTool = {
|
|
|
8
8
|
output?: string;
|
|
9
9
|
outputLines: number;
|
|
10
10
|
outputBytes: number;
|
|
11
|
+
outcome?: ToolOutcome;
|
|
11
12
|
};
|
|
12
13
|
export type MutableTurn = {
|
|
13
14
|
id: string | null;
|
|
@@ -16,12 +17,14 @@ export type MutableTurn = {
|
|
|
16
17
|
text?: string;
|
|
17
18
|
tools: MutableTool[];
|
|
18
19
|
};
|
|
20
|
+
export type ToolOutcome = 'failed' | 'rejected';
|
|
19
21
|
export type ParsedEvent = {
|
|
20
22
|
at: number | null;
|
|
21
23
|
turn: MutableTurn | null;
|
|
22
24
|
toolOutputs?: readonly {
|
|
23
25
|
id: string;
|
|
24
26
|
text: string;
|
|
27
|
+
outcome?: ToolOutcome;
|
|
25
28
|
}[];
|
|
26
29
|
};
|
|
27
30
|
export type Parse = (value: unknown) => ParsedEvent | null;
|
package/dist/parsers.js
CHANGED
|
@@ -50,6 +50,30 @@ const compact = (value) => {
|
|
|
50
50
|
return String(value);
|
|
51
51
|
}
|
|
52
52
|
};
|
|
53
|
+
// A RESULT IS WHAT THE TOOL SAID, NOT ITS WIRE SHAPE. Every harness that records a result as content blocks
|
|
54
|
+
// (Claude's tool_result content, Codex's input_text output blocks, MCP results everywhere) means the text of
|
|
55
|
+
// those blocks, with their line breaks; encoding the block list itself as JSON would show the reader escaped
|
|
56
|
+
// newlines inside a JSON shell. A block that is not text — an image, a reference — is named, not dumped.
|
|
57
|
+
const resultText = (value) => {
|
|
58
|
+
if (typeof value === 'string')
|
|
59
|
+
return value;
|
|
60
|
+
if (!Array.isArray(value))
|
|
61
|
+
return compact(value);
|
|
62
|
+
return value.map((blockValue) => {
|
|
63
|
+
const block = object(blockValue);
|
|
64
|
+
if (!block)
|
|
65
|
+
return compact(blockValue);
|
|
66
|
+
const text = string(block.text);
|
|
67
|
+
if (text !== null)
|
|
68
|
+
return text;
|
|
69
|
+
const type = string(block.type);
|
|
70
|
+
if (type === 'image')
|
|
71
|
+
return '[image]';
|
|
72
|
+
if (type)
|
|
73
|
+
return `[${type}]`;
|
|
74
|
+
return compact(blockValue);
|
|
75
|
+
}).join('\n');
|
|
76
|
+
};
|
|
53
77
|
const lineCount = (value) => value ? value.split(/\r?\n/).length : 0;
|
|
54
78
|
// --- the seven native shapes -------------------------------------------------------------------------------
|
|
55
79
|
export function claudeEvent(value) {
|
|
@@ -82,7 +106,7 @@ export function claudeEvent(value) {
|
|
|
82
106
|
const outputs = blocks.flatMap((block) => {
|
|
83
107
|
const b = object(block);
|
|
84
108
|
const id = string(b?.tool_use_id);
|
|
85
|
-
return b?.type === 'tool_result' && id ? [{ id, text:
|
|
109
|
+
return b?.type === 'tool_result' && id ? [{ id, text: resultText(b?.content ?? ''), ...(b?.is_error === true ? { outcome: 'failed' } : {}) }] : [];
|
|
86
110
|
});
|
|
87
111
|
if (outputs.length)
|
|
88
112
|
return { at: eventAt, turn: null, toolOutputs: outputs };
|
|
@@ -142,7 +166,7 @@ export function codexEvent(value) {
|
|
|
142
166
|
if (entry.type === 'response_item' && (type === 'custom_tool_call_output' || type === 'function_call_output')) {
|
|
143
167
|
const id = string(payload.call_id ?? payload.id);
|
|
144
168
|
const output = payload.output ?? payload.result ?? '';
|
|
145
|
-
return id ? { at: eventAt, turn: null, toolOutputs: [{ id, text:
|
|
169
|
+
return id ? { at: eventAt, turn: null, toolOutputs: [{ id, text: resultText(output) }] } : null;
|
|
146
170
|
}
|
|
147
171
|
return null;
|
|
148
172
|
}
|
|
@@ -204,9 +228,14 @@ export function codexAppServerEvent(value) {
|
|
|
204
228
|
output = item.result ?? item.error;
|
|
205
229
|
else if (type === 'dynamicToolCall')
|
|
206
230
|
output = item.contentItems ?? item.output ?? item.error;
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
231
|
+
// the item status is the app-server's own verdict: `failed`, or `declined` when the person refused the call —
|
|
232
|
+
// a declined call has no output, so the empty result is what ends its "running"
|
|
233
|
+
const status = string(item.status);
|
|
234
|
+
const outcome = status === 'failed' ? 'failed' : status === 'declined' ? 'rejected' : undefined;
|
|
235
|
+
if (output === undefined || output === null) {
|
|
236
|
+
return outcome ? { at: eventAt, turn: null, toolOutputs: [{ id, text: '', outcome }] } : { at: eventAt, turn: null };
|
|
237
|
+
}
|
|
238
|
+
return { at: eventAt, turn: null, toolOutputs: [{ id, text: resultText(output), ...(outcome ? { outcome } : {}) }] };
|
|
210
239
|
}
|
|
211
240
|
// Agent-message deltas are fragments of one native item. The closure remembers only that item's text and
|
|
212
241
|
// re-emits its native id, allowing IntervalCollector to replace the earlier turn in place.
|
|
@@ -268,7 +297,7 @@ export function piEvent(value) {
|
|
|
268
297
|
}
|
|
269
298
|
if (message.role === 'toolResult') {
|
|
270
299
|
const id = string(message.toolCallId);
|
|
271
|
-
return id ? { at: eventAt, turn: null, toolOutputs: [{ id, text: blockText(message.content) ?? compact(message.content ?? '') }] } : null;
|
|
300
|
+
return id ? { at: eventAt, turn: null, toolOutputs: [{ id, text: blockText(message.content) ?? compact(message.content ?? ''), ...(message.isError === true ? { outcome: 'failed' } : {}) }] } : null;
|
|
272
301
|
}
|
|
273
302
|
return null;
|
|
274
303
|
}
|
|
@@ -303,7 +332,7 @@ export function geminiEvent(value) {
|
|
|
303
332
|
for (const callValue of items(message.toolCalls)) {
|
|
304
333
|
const call = object(callValue);
|
|
305
334
|
const id = string(call?.id) ?? `tool-${turn.tools.length}`;
|
|
306
|
-
turn.tools.push({ id, name: string(call?.name) ?? 'tool', input: call?.args === undefined ? undefined : compact(call.args), outputLines: 0, outputBytes: 0 });
|
|
335
|
+
turn.tools.push({ id, name: string(call?.name) ?? 'tool', input: call?.args === undefined ? undefined : compact(call.args), outputLines: 0, outputBytes: 0, ...(call?.status === 'error' ? { outcome: 'failed' } : {}) });
|
|
307
336
|
}
|
|
308
337
|
if (!turn.text && !turn.tools.length)
|
|
309
338
|
return null;
|
|
@@ -325,7 +354,7 @@ export function openclawEvent(value) {
|
|
|
325
354
|
}
|
|
326
355
|
if (message.role === 'toolResult') {
|
|
327
356
|
const id = string(message.toolCallId);
|
|
328
|
-
return id ? { at: eventAt, turn: null, toolOutputs: [{ id, text: blockText(message.content) ?? compact(message.content ?? '') }] } : null;
|
|
357
|
+
return id ? { at: eventAt, turn: null, toolOutputs: [{ id, text: blockText(message.content) ?? compact(message.content ?? ''), ...(message.isError === true ? { outcome: 'failed' } : {}) }] } : null;
|
|
329
358
|
}
|
|
330
359
|
if (message.role === 'assistant') {
|
|
331
360
|
const turn = { id: idOf(entry) ?? idOf(message), at: eventAt, role: 'assistant', tools: [] };
|
|
@@ -414,6 +443,8 @@ export function opencodeEvents(value) {
|
|
|
414
443
|
tool.output = output.slice(0, MAX_OUTPUT_BYTES);
|
|
415
444
|
tool.outputBytes = Buffer.byteLength(output);
|
|
416
445
|
tool.outputLines = lineCount(output);
|
|
446
|
+
if (status === 'error')
|
|
447
|
+
tool.outcome = 'failed';
|
|
417
448
|
}
|
|
418
449
|
turn.tools.push(tool);
|
|
419
450
|
}
|
|
@@ -466,6 +497,8 @@ export class IntervalCollector {
|
|
|
466
497
|
tool.outputLines += lineCount(output.text);
|
|
467
498
|
if (tool.output === undefined)
|
|
468
499
|
tool.output = '';
|
|
500
|
+
if (output.outcome)
|
|
501
|
+
tool.outcome = output.outcome;
|
|
469
502
|
const remaining = Math.max(0, MAX_OUTPUT_BYTES - Buffer.byteLength(tool.output));
|
|
470
503
|
tool.output += output.text.slice(0, remaining);
|
|
471
504
|
if (bytes > remaining)
|
package/dist/turns.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spexcode/transcript",
|
|
3
|
-
"version": "0.7.0-next.
|
|
3
|
+
"version": "0.7.0-next.7",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Normalized agent transcripts: one parser per harness, a bounded interval reader over a native thread file or an in-memory event stream, and the full/delta frame protocol every transport and renderer share.",
|
|
6
6
|
"files": [
|