@the-open-engine/zeroshot 6.34.0 → 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.
@@ -1,4 +1,6 @@
1
1
  import { spawn } from 'child_process';
2
+ import { createHash } from 'crypto';
3
+ import { StringDecoder } from 'string_decoder';
2
4
  import {
3
5
  detectProviderFatalError,
4
6
  detectProviderStreamingModeError,
@@ -9,6 +11,9 @@ import { terminateProcess } from './process-termination.js';
9
11
 
10
12
  export const COMMAND_CLEANUP_UNINITIALIZED = Symbol('command-cleanup-uninitialized');
11
13
 
14
+ const MAX_CODEX_CONTROL_RECORD_BYTES = 64 * 1024;
15
+ const MAX_WATCHER_CONTROL_RECORD_BYTES = 1024 * 1024;
16
+
12
17
  export function spawnWatcherProvider(command, finalArgs, options) {
13
18
  return spawn(command, finalArgs, {
14
19
  ...options,
@@ -32,10 +37,162 @@ export async function terminateWatcherProvider(providerProcess, options = {}) {
32
37
  return result.terminated;
33
38
  }
34
39
 
35
- function splitBufferLines(buffer, chunk) {
36
- const nextBuffer = buffer + chunk;
37
- const lines = nextBuffer.split('\n');
38
- return { lines: lines.slice(0, -1), remaining: lines.at(-1) || '' };
40
+ function createCodexOutputPassthrough({ log, captureProviderSession }) {
41
+ const decoder = new StringDecoder('utf8');
42
+ let atLineStart = true;
43
+ let inspectable = true;
44
+ let inspectionBytes = 0;
45
+ let inspectionParts = [];
46
+
47
+ function inspectPart(part) {
48
+ if (!inspectable || !part) return;
49
+ inspectionBytes += Buffer.byteLength(part);
50
+ if (inspectionBytes > MAX_CODEX_CONTROL_RECORD_BYTES) {
51
+ inspectable = false;
52
+ inspectionParts = [];
53
+ return;
54
+ }
55
+ inspectionParts.push(part);
56
+ }
57
+
58
+ function finishLine() {
59
+ if (inspectable) captureProviderSession(inspectionParts.join(''));
60
+ atLineStart = true;
61
+ inspectable = true;
62
+ inspectionBytes = 0;
63
+ inspectionParts = [];
64
+ }
65
+
66
+ function writeText(text, timestamp) {
67
+ if (!text) return;
68
+ const logged = [];
69
+ let offset = 0;
70
+ while (offset < text.length) {
71
+ if (atLineStart) {
72
+ logged.push(`[${timestamp}]`);
73
+ atLineStart = false;
74
+ }
75
+ const newline = text.indexOf('\n', offset);
76
+ if (newline === -1) {
77
+ const part = text.slice(offset);
78
+ inspectPart(part);
79
+ logged.push(part);
80
+ break;
81
+ }
82
+ const part = text.slice(offset, newline);
83
+ inspectPart(part);
84
+ logged.push(part, '\n');
85
+ finishLine();
86
+ offset = newline + 1;
87
+ }
88
+ log(logged.join(''));
89
+ }
90
+
91
+ return {
92
+ consume(chunk) {
93
+ const text = typeof chunk === 'string' ? chunk : decoder.write(chunk);
94
+ writeText(text, Date.now());
95
+ },
96
+ flush() {
97
+ writeText(decoder.end(), Date.now());
98
+ if (!atLineStart) {
99
+ finishLine();
100
+ log('\n');
101
+ }
102
+ },
103
+ };
104
+ }
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
+ };
39
196
  }
40
197
 
41
198
  export function resolveWatcherCommand(config, commandSpec, fallbackArgs, normalizeProviderName) {
@@ -189,6 +346,23 @@ export function createWatcherOutputRuntime({
189
346
  let streamingModeError = null;
190
347
  let fatalError = null;
191
348
  const captureProviderSession = providerSessionCapture?.captureLine || (() => {});
349
+ const codexOutputPassthrough =
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
+ });
192
366
 
193
367
  function maybeHandleFatalError(line, timestamp) {
194
368
  if (fatalError) return false;
@@ -217,52 +391,38 @@ export function createWatcherOutputRuntime({
217
391
  }
218
392
  }
219
393
 
220
- 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
+ }
221
403
  captureProviderSession(line);
222
404
  if (silentJsonMode && !line.trim()) return;
223
405
  maybeHandleFatalError(line, timestamp);
224
406
  if (captureStreamingError(line, timestamp)) return;
225
407
  if (silentJsonMode) {
226
408
  maybeCaptureStructuredOutput(line);
227
- } else {
409
+ } else if (!alreadyLogged) {
228
410
  log(`[${timestamp}]${line}\n`);
229
411
  }
230
412
  }
231
413
 
232
- function consumeOutput(buffer, chunk) {
233
- const timestamp = Date.now();
234
- const { lines, remaining } = splitBufferLines(buffer, chunk.toString());
235
- for (const line of lines) handleOutputLine(line, timestamp);
236
- return remaining;
237
- }
238
-
239
- function consumeStderr(buffer, chunk) {
240
- const timestamp = Date.now();
241
- const { lines, remaining } = splitBufferLines(buffer, chunk.toString());
242
- for (const line of lines) log(`[${timestamp}]${line}\n`);
243
- return remaining;
244
- }
245
-
246
- function flushOutput(buffer, timestamp) {
247
- if (!buffer.trim()) return;
248
- captureProviderSession(buffer);
249
- if (!enableRecovery) {
250
- if (!silentJsonMode) log(`[${timestamp}]${buffer}\n`);
251
- return;
252
- }
253
- maybeHandleFatalError(buffer, timestamp);
254
- if (captureStreamingError(buffer, timestamp)) return;
255
- if (silentJsonMode) {
256
- maybeCaptureStructuredOutput(buffer);
414
+ function consumeOutput(_buffer, chunk) {
415
+ if (codexOutputPassthrough) {
416
+ codexOutputPassthrough.consume(chunk);
257
417
  } else {
258
- log(`[${timestamp}]${buffer}\n`);
418
+ outputPassthrough.consume(chunk);
259
419
  }
420
+ return '';
260
421
  }
261
422
 
262
- function flushStderr(buffer, timestamp) {
263
- if (!buffer.trim()) return;
264
- maybeHandleFatalError(buffer, timestamp);
265
- log(`[${timestamp}]${buffer}\n`);
423
+ function consumeStderr(_buffer, chunk) {
424
+ stderrPassthrough.consume(chunk);
425
+ return '';
266
426
  }
267
427
 
268
428
  function attemptRecovery(code, timestamp) {
@@ -282,10 +442,14 @@ export function createWatcherOutputRuntime({
282
442
  return recovered;
283
443
  }
284
444
 
285
- function complete({ code, signal, outputBuffer, stderrBuffer = null }) {
445
+ function complete({ code, signal, stderrBuffer = null }) {
286
446
  const timestamp = Date.now();
287
- flushOutput(outputBuffer, timestamp);
288
- if (stderrBuffer !== null) flushStderr(stderrBuffer, timestamp);
447
+ if (codexOutputPassthrough) {
448
+ codexOutputPassthrough.flush();
449
+ } else {
450
+ outputPassthrough.flush();
451
+ }
452
+ if (stderrBuffer !== null) stderrPassthrough.flush();
289
453
  const recovered = attemptRecovery(code, timestamp);
290
454
  const sessionIdentityError = providerSessionCapture?.getCompletionError() || null;
291
455
  if (silentJsonMode && finalResultJson) log(`${finalResultJson}\n`);