@the-open-engine/zeroshot 6.34.1 → 6.34.2
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/cli/index.js +34 -8
- package/cli/json-export.js +77 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/src/agent/agent-task-executor.js +380 -99
- package/src/ledger.js +364 -31
- package/src/template-resolver.js +5 -0
- package/task-lib/watcher-output-runtime.js +128 -43
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
|
+
import { createHash } from 'crypto';
|
|
2
3
|
import { StringDecoder } from 'string_decoder';
|
|
3
4
|
import {
|
|
4
5
|
detectProviderFatalError,
|
|
@@ -11,6 +12,7 @@ import { terminateProcess } from './process-termination.js';
|
|
|
11
12
|
export const COMMAND_CLEANUP_UNINITIALIZED = Symbol('command-cleanup-uninitialized');
|
|
12
13
|
|
|
13
14
|
const MAX_CODEX_CONTROL_RECORD_BYTES = 64 * 1024;
|
|
15
|
+
const MAX_WATCHER_CONTROL_RECORD_BYTES = 1024 * 1024;
|
|
14
16
|
|
|
15
17
|
export function spawnWatcherProvider(command, finalArgs, options) {
|
|
16
18
|
return spawn(command, finalArgs, {
|
|
@@ -35,12 +37,6 @@ export async function terminateWatcherProvider(providerProcess, options = {}) {
|
|
|
35
37
|
return result.terminated;
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
function splitBufferLines(buffer, chunk) {
|
|
39
|
-
const nextBuffer = buffer + chunk;
|
|
40
|
-
const lines = nextBuffer.split('\n');
|
|
41
|
-
return { lines: lines.slice(0, -1), remaining: lines.at(-1) || '' };
|
|
42
|
-
}
|
|
43
|
-
|
|
44
40
|
function createCodexOutputPassthrough({ log, captureProviderSession }) {
|
|
45
41
|
const decoder = new StringDecoder('utf8');
|
|
46
42
|
let atLineStart = true;
|
|
@@ -107,6 +103,98 @@ function createCodexOutputPassthrough({ log, captureProviderSession }) {
|
|
|
107
103
|
};
|
|
108
104
|
}
|
|
109
105
|
|
|
106
|
+
function createBoundedLinePassthrough({ log, handleLine, deferRawUntilOverflow = false }) {
|
|
107
|
+
const decoder = new StringDecoder('utf8');
|
|
108
|
+
let atLineStart = true;
|
|
109
|
+
let lineTimestamp = null;
|
|
110
|
+
let byteLength = 0;
|
|
111
|
+
let inspectable = true;
|
|
112
|
+
let inspectionParts = [];
|
|
113
|
+
let digest = createHash('sha256');
|
|
114
|
+
let rawOverflowStreaming = false;
|
|
115
|
+
|
|
116
|
+
function inspectPart(part) {
|
|
117
|
+
if (!part) return;
|
|
118
|
+
byteLength += Buffer.byteLength(part);
|
|
119
|
+
digest.update(part);
|
|
120
|
+
if (!inspectable) {
|
|
121
|
+
if (deferRawUntilOverflow && log) log(part);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (byteLength > MAX_WATCHER_CONTROL_RECORD_BYTES) {
|
|
125
|
+
if (deferRawUntilOverflow && log) {
|
|
126
|
+
log(`[${lineTimestamp}]${inspectionParts.join('')}${part}`);
|
|
127
|
+
rawOverflowStreaming = true;
|
|
128
|
+
}
|
|
129
|
+
inspectable = false;
|
|
130
|
+
inspectionParts = [];
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
inspectionParts.push(part);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function finishLine() {
|
|
137
|
+
const oversized = !inspectable;
|
|
138
|
+
const line = inspectable
|
|
139
|
+
? inspectionParts.join('')
|
|
140
|
+
: `[ZEROSHOT] Provider output record retained in task log but omitted from watcher inspection ` +
|
|
141
|
+
`(byte_length=${byteLength}, sha256=${digest.digest('hex')})`;
|
|
142
|
+
handleLine(line, lineTimestamp || Date.now(), { oversized });
|
|
143
|
+
atLineStart = true;
|
|
144
|
+
lineTimestamp = null;
|
|
145
|
+
byteLength = 0;
|
|
146
|
+
inspectable = true;
|
|
147
|
+
inspectionParts = [];
|
|
148
|
+
digest = createHash('sha256');
|
|
149
|
+
rawOverflowStreaming = false;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function appendRaw(logged, ...parts) {
|
|
153
|
+
if (log && !deferRawUntilOverflow) logged.push(...parts);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function writeText(text) {
|
|
157
|
+
if (!text) return;
|
|
158
|
+
const logged = [];
|
|
159
|
+
let offset = 0;
|
|
160
|
+
while (offset < text.length) {
|
|
161
|
+
if (atLineStart) {
|
|
162
|
+
lineTimestamp = Date.now();
|
|
163
|
+
appendRaw(logged, `[${lineTimestamp}]`);
|
|
164
|
+
atLineStart = false;
|
|
165
|
+
}
|
|
166
|
+
const newline = text.indexOf('\n', offset);
|
|
167
|
+
if (newline === -1) {
|
|
168
|
+
const part = text.slice(offset);
|
|
169
|
+
inspectPart(part);
|
|
170
|
+
appendRaw(logged, part);
|
|
171
|
+
break;
|
|
172
|
+
}
|
|
173
|
+
const part = text.slice(offset, newline);
|
|
174
|
+
inspectPart(part);
|
|
175
|
+
appendRaw(logged, part, '\n');
|
|
176
|
+
if (deferRawUntilOverflow && rawOverflowStreaming && log) log('\n');
|
|
177
|
+
finishLine();
|
|
178
|
+
offset = newline + 1;
|
|
179
|
+
}
|
|
180
|
+
if (log && logged.length > 0) log(logged.join(''));
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
consume(chunk) {
|
|
185
|
+
writeText(typeof chunk === 'string' ? chunk : decoder.write(chunk));
|
|
186
|
+
},
|
|
187
|
+
flush() {
|
|
188
|
+
writeText(decoder.end());
|
|
189
|
+
if (!atLineStart) {
|
|
190
|
+
if (deferRawUntilOverflow && rawOverflowStreaming && log) log('\n');
|
|
191
|
+
finishLine();
|
|
192
|
+
if (log && !deferRawUntilOverflow) log('\n');
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
110
198
|
export function resolveWatcherCommand(config, commandSpec, fallbackArgs, normalizeProviderName) {
|
|
111
199
|
return {
|
|
112
200
|
providerName: normalizeProviderName(config.provider || 'claude'),
|
|
@@ -260,6 +348,21 @@ export function createWatcherOutputRuntime({
|
|
|
260
348
|
const captureProviderSession = providerSessionCapture?.captureLine || (() => {});
|
|
261
349
|
const codexOutputPassthrough =
|
|
262
350
|
providerName === 'codex' ? createCodexOutputPassthrough({ log, captureProviderSession }) : null;
|
|
351
|
+
const outputPassthrough = codexOutputPassthrough
|
|
352
|
+
? null
|
|
353
|
+
: createBoundedLinePassthrough({
|
|
354
|
+
log,
|
|
355
|
+
deferRawUntilOverflow: silentJsonMode,
|
|
356
|
+
handleLine: (line, timestamp, { oversized }) =>
|
|
357
|
+
handleOutputLine(line, timestamp, {
|
|
358
|
+
alreadyLogged: !silentJsonMode,
|
|
359
|
+
oversized,
|
|
360
|
+
}),
|
|
361
|
+
});
|
|
362
|
+
const stderrPassthrough = createBoundedLinePassthrough({
|
|
363
|
+
log,
|
|
364
|
+
handleLine: (line, timestamp) => maybeHandleFatalError(line, timestamp),
|
|
365
|
+
});
|
|
263
366
|
|
|
264
367
|
function maybeHandleFatalError(line, timestamp) {
|
|
265
368
|
if (fatalError) return false;
|
|
@@ -288,56 +391,38 @@ export function createWatcherOutputRuntime({
|
|
|
288
391
|
}
|
|
289
392
|
}
|
|
290
393
|
|
|
291
|
-
function handleOutputLine(line, timestamp) {
|
|
394
|
+
function handleOutputLine(line, timestamp, { alreadyLogged = false, oversized = false } = {}) {
|
|
395
|
+
if (silentJsonMode && oversized) {
|
|
396
|
+
fatalError =
|
|
397
|
+
`Provider structured output exceeded the ${MAX_WATCHER_CONTROL_RECORD_BYTES}-byte ` +
|
|
398
|
+
'watcher inspection limit; complete output remains in the task log';
|
|
399
|
+
log(`[${timestamp}][FATAL] ${fatalError}\n`);
|
|
400
|
+
stopProvider(timestamp);
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
292
403
|
captureProviderSession(line);
|
|
293
404
|
if (silentJsonMode && !line.trim()) return;
|
|
294
405
|
maybeHandleFatalError(line, timestamp);
|
|
295
406
|
if (captureStreamingError(line, timestamp)) return;
|
|
296
407
|
if (silentJsonMode) {
|
|
297
408
|
maybeCaptureStructuredOutput(line);
|
|
298
|
-
} else {
|
|
409
|
+
} else if (!alreadyLogged) {
|
|
299
410
|
log(`[${timestamp}]${line}\n`);
|
|
300
411
|
}
|
|
301
412
|
}
|
|
302
413
|
|
|
303
|
-
function consumeOutput(
|
|
414
|
+
function consumeOutput(_buffer, chunk) {
|
|
304
415
|
if (codexOutputPassthrough) {
|
|
305
416
|
codexOutputPassthrough.consume(chunk);
|
|
306
|
-
return '';
|
|
307
|
-
}
|
|
308
|
-
const timestamp = Date.now();
|
|
309
|
-
const { lines, remaining } = splitBufferLines(buffer, chunk.toString());
|
|
310
|
-
for (const line of lines) handleOutputLine(line, timestamp);
|
|
311
|
-
return remaining;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
function consumeStderr(buffer, chunk) {
|
|
315
|
-
const timestamp = Date.now();
|
|
316
|
-
const { lines, remaining } = splitBufferLines(buffer, chunk.toString());
|
|
317
|
-
for (const line of lines) log(`[${timestamp}]${line}\n`);
|
|
318
|
-
return remaining;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
function flushOutput(buffer, timestamp) {
|
|
322
|
-
if (!buffer.trim()) return;
|
|
323
|
-
captureProviderSession(buffer);
|
|
324
|
-
if (!enableRecovery) {
|
|
325
|
-
if (!silentJsonMode) log(`[${timestamp}]${buffer}\n`);
|
|
326
|
-
return;
|
|
327
|
-
}
|
|
328
|
-
maybeHandleFatalError(buffer, timestamp);
|
|
329
|
-
if (captureStreamingError(buffer, timestamp)) return;
|
|
330
|
-
if (silentJsonMode) {
|
|
331
|
-
maybeCaptureStructuredOutput(buffer);
|
|
332
417
|
} else {
|
|
333
|
-
|
|
418
|
+
outputPassthrough.consume(chunk);
|
|
334
419
|
}
|
|
420
|
+
return '';
|
|
335
421
|
}
|
|
336
422
|
|
|
337
|
-
function
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
log(`[${timestamp}]${buffer}\n`);
|
|
423
|
+
function consumeStderr(_buffer, chunk) {
|
|
424
|
+
stderrPassthrough.consume(chunk);
|
|
425
|
+
return '';
|
|
341
426
|
}
|
|
342
427
|
|
|
343
428
|
function attemptRecovery(code, timestamp) {
|
|
@@ -357,14 +442,14 @@ export function createWatcherOutputRuntime({
|
|
|
357
442
|
return recovered;
|
|
358
443
|
}
|
|
359
444
|
|
|
360
|
-
function complete({ code, signal,
|
|
445
|
+
function complete({ code, signal, stderrBuffer = null }) {
|
|
361
446
|
const timestamp = Date.now();
|
|
362
447
|
if (codexOutputPassthrough) {
|
|
363
448
|
codexOutputPassthrough.flush();
|
|
364
449
|
} else {
|
|
365
|
-
|
|
450
|
+
outputPassthrough.flush();
|
|
366
451
|
}
|
|
367
|
-
if (stderrBuffer !== null)
|
|
452
|
+
if (stderrBuffer !== null) stderrPassthrough.flush();
|
|
368
453
|
const recovered = attemptRecovery(code, timestamp);
|
|
369
454
|
const sessionIdentityError = providerSessionCapture?.getCompletionError() || null;
|
|
370
455
|
if (silentJsonMode && finalResultJson) log(`${finalResultJson}\n`);
|