@tangle-network/harness-sessions 0.1.6 → 0.1.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/formats/codex.js +51 -4
- package/package.json +1 -1
package/dist/formats/codex.js
CHANGED
|
@@ -66,14 +66,59 @@ function contentParts(content) {
|
|
|
66
66
|
}
|
|
67
67
|
return parts;
|
|
68
68
|
}
|
|
69
|
-
|
|
69
|
+
// `exec_command` and `write_stdin` answer with a text header (`Chunk ID`, `Wall time`, then
|
|
70
|
+
// `Process exited with code N` once the process is done) before `Output:`; a still-running process
|
|
71
|
+
// says `Process running with session ID` instead and has no status yet.
|
|
72
|
+
const PROCESS_EXIT = /^Process exited with code (-?\d+)\s*$/mu;
|
|
73
|
+
function processFailed(text) {
|
|
74
|
+
const header = text.split(/^Output:/mu, 1)[0] ?? '';
|
|
75
|
+
const match = PROCESS_EXIT.exec(header);
|
|
76
|
+
return match !== null && Number(match[1]) !== 0;
|
|
77
|
+
}
|
|
78
|
+
/** A nonzero numeric `exit_code` anywhere in a parsed JSON value (code-mode tool results nest it). */
|
|
79
|
+
function jsonExitFailed(value, depth = 0) {
|
|
80
|
+
if (depth > 4)
|
|
81
|
+
return false;
|
|
82
|
+
if (Array.isArray(value))
|
|
83
|
+
return value.some((v) => jsonExitFailed(v, depth + 1));
|
|
84
|
+
if (!isRecord(value))
|
|
85
|
+
return false;
|
|
86
|
+
if (typeof value.exit_code === 'number' && value.exit_code !== 0)
|
|
87
|
+
return true;
|
|
88
|
+
return Object.values(value).some((v) => jsonExitFailed(v, depth + 1));
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Code mode runs JavaScript in an `exec` call; its `tools.exec_command(...)` results are printed
|
|
92
|
+
* as JSON items carrying `exit_code`. The first item is `Script failed` when the script threw.
|
|
93
|
+
*/
|
|
94
|
+
function codeModeFailed(output) {
|
|
95
|
+
if (!Array.isArray(output))
|
|
96
|
+
return false;
|
|
97
|
+
return output.some((item, i) => {
|
|
98
|
+
const text = isRecord(item) && typeof item.text === 'string' ? item.text : '';
|
|
99
|
+
if (i === 0 && /^Script failed\b/u.test(text))
|
|
100
|
+
return true;
|
|
101
|
+
if (!text.includes('exit_code'))
|
|
102
|
+
return false;
|
|
103
|
+
try {
|
|
104
|
+
return jsonExitFailed(JSON.parse(text));
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
function outputOf(output, tool) {
|
|
112
|
+
const execFailed = () => tool.name === 'exec_command' || tool.name === 'write_stdin'
|
|
113
|
+
? typeof output === 'string' && processFailed(output)
|
|
114
|
+
: tool.name === 'exec' && tool.input.includes('exec_command') && codeModeFailed(output);
|
|
70
115
|
if (typeof output === 'string')
|
|
71
|
-
return { output, text: output, isError:
|
|
116
|
+
return { output, text: output, isError: execFailed() };
|
|
72
117
|
if (isRecord(output)) {
|
|
73
118
|
const content = output.content ?? output.output;
|
|
74
119
|
return { output, text: textOf(content) ?? (typeof content === 'string' ? content : null), isError: output.success === false };
|
|
75
120
|
}
|
|
76
|
-
return { output: output ?? null, text: textOf(output), isError:
|
|
121
|
+
return { output: output ?? null, text: textOf(output), isError: execFailed() };
|
|
77
122
|
}
|
|
78
123
|
const MODEL_OUTPUT = new Set(['message', 'reasoning']);
|
|
79
124
|
export function createFold(ref, mode, stats) {
|
|
@@ -97,6 +142,7 @@ export function createFold(ref, mode, stats) {
|
|
|
97
142
|
let inherited = false;
|
|
98
143
|
let inheritedRecords = 0;
|
|
99
144
|
const foreignThreads = new Set();
|
|
145
|
+
const tools = new Map();
|
|
100
146
|
const openCall = (at) => {
|
|
101
147
|
if (open)
|
|
102
148
|
return open;
|
|
@@ -196,6 +242,7 @@ export function createFold(ref, mode, stats) {
|
|
|
196
242
|
const raw = kind === 'function_call' ? payload.arguments : kind === 'custom_tool_call' ? payload.input : (payload.action ?? payload.arguments ?? payload.input);
|
|
197
243
|
const call = openCall(at);
|
|
198
244
|
assistantMessage(at, call, [{ type: 'tool-call', toolCallId: callId }]);
|
|
245
|
+
tools.set(callId, { name, input: typeof raw === 'string' ? raw : '' });
|
|
199
246
|
builder.toolCall({ id: callId, name, ...argumentsOf(raw), at, messageId: openMessage });
|
|
200
247
|
// A web search is answered inside the response; there is no output item.
|
|
201
248
|
if (kind === 'web_search_call')
|
|
@@ -203,7 +250,7 @@ export function createFold(ref, mode, stats) {
|
|
|
203
250
|
}
|
|
204
251
|
else if (kind.endsWith('_call_output')) {
|
|
205
252
|
const callId = str(payload.call_id) ?? '';
|
|
206
|
-
const result = outputOf(payload.output);
|
|
253
|
+
const result = outputOf(payload.output, tools.get(callId) ?? { name: '', input: '' });
|
|
207
254
|
const id = `${callId}:output`;
|
|
208
255
|
builder.message({ id, role: 'tool', actor: 'tool-result', at, modelCallId: null, parts: [{ type: 'tool-result', toolCallId: callId }] });
|
|
209
256
|
builder.toolResult(callId, { ...result, at, messageId: id });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tangle-network/harness-sessions",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "One reader for every coding-agent harness's native session (Claude Code, Codex, OpenCode, Pi): store layouts, a normalized session with messages, tool calls, served models, usage and ending errors, and projections to chat messages, OTLP spans and turns",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|