@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.
@@ -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(buffer, chunk) {
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
- log(`[${timestamp}]${buffer}\n`);
418
+ outputPassthrough.consume(chunk);
334
419
  }
420
+ return '';
335
421
  }
336
422
 
337
- function flushStderr(buffer, timestamp) {
338
- if (!buffer.trim()) return;
339
- maybeHandleFatalError(buffer, timestamp);
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, outputBuffer, stderrBuffer = null }) {
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
- flushOutput(outputBuffer, timestamp);
450
+ outputPassthrough.flush();
366
451
  }
367
- if (stderrBuffer !== null) flushStderr(stderrBuffer, timestamp);
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`);