blun-king-cli 9.1.536 → 9.1.561

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.
Files changed (129) hide show
  1. package/LIESMICH.txt +13 -869
  2. package/README.md +41 -833
  3. package/bin/assistant-message-offload-policy.cjs +3 -1
  4. package/bin/compaction-transaction-policy.cjs +122 -0
  5. package/bin/context-performance-policy.cjs +2 -5
  6. package/bin/context-pressure-policy.cjs +20 -0
  7. package/bin/cron-run-output.cjs +45 -0
  8. package/bin/cron-run-store.cjs +145 -0
  9. package/bin/default-model-output-budget-policy.cjs +28 -0
  10. package/bin/durable-task-resume-policy.cjs +130 -0
  11. package/bin/durable-task-resume-runtime.cjs +117 -0
  12. package/bin/durable-task-resume-store.cjs +88 -0
  13. package/bin/editable-tool-approval-policy.cjs +540 -0
  14. package/bin/editable-tool-approval-runtime.cjs +99 -0
  15. package/bin/file-observation-policy.cjs +133 -0
  16. package/bin/html-to-research-markdown.cjs +146 -0
  17. package/bin/launcher-runtime.js +0 -1
  18. package/bin/micro-compaction-policy.cjs +64 -0
  19. package/bin/mnemo-connect-heartbeat.cjs +1 -3
  20. package/bin/programmatic-tool-runtime.mjs +330 -4
  21. package/bin/read-continuation-policy.cjs +36 -5
  22. package/bin/retry-checkpoint-policy.cjs +13 -0
  23. package/bin/scoped-cron-run-policy.cjs +358 -0
  24. package/bin/session-checkpoint-policy.cjs +25 -0
  25. package/bin/startup-preferences.cjs +4 -3
  26. package/bin/structured-agent-swarm-output.cjs +325 -0
  27. package/bin/subagent-context-fork-policy.cjs +155 -0
  28. package/bin/subagent-skill-policy.cjs +204 -0
  29. package/bin/telegram-approval-relay.cjs +2 -1
  30. package/bin/tool-file-persistence.cjs +141 -0
  31. package/bin/tool-result-offload-policy.cjs +25 -33
  32. package/bin/turn-thinking-policy.cjs +2 -26
  33. package/bin/update-notice.js +30 -18
  34. package/bin/user-message-offload-policy.cjs +3 -1
  35. package/blun.mjs +1564 -640
  36. package/codebase-index/README.md +12 -0
  37. package/codebase-index/codebase_index.py +129 -18
  38. package/package.json +23 -58
  39. package/telegram-plugin/bin/telegram-mnemo-capture.cjs +1 -3
  40. package/telegram-plugin/bin/telegram-typing-keepalive.cjs +89 -0
  41. package/telegram-plugin/dist/bridge.mjs +8 -1
  42. package/CHANGELOG.md +0 -321
  43. package/agent-spine-plugin/CHANGELOG.md +0 -406
  44. package/agent-spine-plugin/CONTRIBUTING.md +0 -52
  45. package/agent-spine-plugin/README.md +0 -344
  46. package/agent-spine-plugin/SECURITY.md +0 -47
  47. package/agent-spine-plugin/docs/acceptance.md +0 -61
  48. package/agent-spine-plugin/docs/architecture.md +0 -183
  49. package/agent-spine-plugin/docs/attention.md +0 -121
  50. package/agent-spine-plugin/docs/automatic-continuity.md +0 -79
  51. package/agent-spine-plugin/docs/channel-runtime.md +0 -92
  52. package/agent-spine-plugin/docs/coordination.md +0 -138
  53. package/agent-spine-plugin/docs/feed-transport.md +0 -99
  54. package/agent-spine-plugin/docs/gateway-runtime.md +0 -116
  55. package/agent-spine-plugin/docs/harness-reference.md +0 -45
  56. package/agent-spine-plugin/docs/host-integration.md +0 -129
  57. package/agent-spine-plugin/docs/https-transport.md +0 -116
  58. package/agent-spine-plugin/docs/learning.md +0 -133
  59. package/agent-spine-plugin/docs/object-transport.md +0 -93
  60. package/agent-spine-plugin/docs/peer-transport.md +0 -88
  61. package/agent-spine-plugin/docs/preflight-recall.md +0 -69
  62. package/agent-spine-plugin/docs/preservation-contract.md +0 -53
  63. package/agent-spine-plugin/docs/quality-gates.md +0 -50
  64. package/agent-spine-plugin/docs/relationships.md +0 -73
  65. package/agent-spine-plugin/docs/releasing.md +0 -83
  66. package/agent-spine-plugin/docs/roadmap.md +0 -307
  67. package/agent-spine-plugin/docs/selfstarter.md +0 -88
  68. package/agent-spine-plugin/docs/session-briefing.md +0 -74
  69. package/agent-spine-plugin/docs/shared-memory.md +0 -259
  70. package/agent-spine-plugin/docs/source-roots.md +0 -86
  71. package/agent-spine-plugin/docs/sqlite-transport.md +0 -76
  72. package/agent-spine-plugin/scripts/check-hosts.js +0 -195
  73. package/agent-spine-plugin/scripts/check-install.js +0 -569
  74. package/agent-spine-plugin/scripts/check-syntax.js +0 -29
  75. package/agent-spine-plugin/scripts/github-actions.js +0 -11
  76. package/agent-spine-plugin/scripts/release-check.js +0 -128
  77. package/agent-spine-plugin/scripts/run-acceptance.js +0 -19
  78. package/agent-spine-plugin/scripts/run-checks.js +0 -46
  79. package/agent-spine-plugin/scripts/run-tests-hermetic.js +0 -73
  80. package/agent-spine-plugin/spine-example/1-identity.md +0 -12
  81. package/agent-spine-plugin/spine-example/2-voice.md +0 -6
  82. package/agent-spine-plugin/spine-example/3-conduct.md +0 -8
  83. package/agent-spine-plugin/spine-example/4-history.md +0 -4
  84. package/bin/empty-response-retry-policy.cjs +0 -29
  85. package/bin/fredrik-glm-provider.cjs +0 -256
  86. package/bin/package-regression-policy.cjs +0 -77
  87. package/fredrik-glm-profile.toml.example +0 -26
  88. package/release-planned-removals.json +0 -15
  89. package/scripts/check-active-profile-plugin-startup.js +0 -36
  90. package/scripts/check-active-work-steer-regression.js +0 -46
  91. package/scripts/check-approval-observability-regression.js +0 -111
  92. package/scripts/check-approval-queue-shortcuts-regression.js +0 -65
  93. package/scripts/check-bundled-agent-spine-regression.js +0 -48
  94. package/scripts/check-codebase-search-packaging-regression.js +0 -92
  95. package/scripts/check-copy-command-regression.js +0 -74
  96. package/scripts/check-current-turn-read-pin-mutation-regression.js +0 -72
  97. package/scripts/check-current-turn-read-pin-regression.js +0 -94
  98. package/scripts/check-deepseek-native-max-regression.js +0 -49
  99. package/scripts/check-empty-response-effort-downgrade-regression.js +0 -48
  100. package/scripts/check-fredrik-glm-mutation-regression.js +0 -18
  101. package/scripts/check-fredrik-glm-regression.js +0 -169
  102. package/scripts/check-historical-tool-result-preview-regression.js +0 -77
  103. package/scripts/check-history-pressure-offload-regression.js +0 -77
  104. package/scripts/check-mcp-startup-wait-budget.js +0 -48
  105. package/scripts/check-package-regression.js +0 -38
  106. package/scripts/check-plugin-startup-regression.js +0 -53
  107. package/scripts/check-programmatic-context-isolation-regression.js +0 -193
  108. package/scripts/check-programmatic-tool-regression.js +0 -294
  109. package/scripts/check-queue-controls-regression.js +0 -189
  110. package/scripts/check-release-metadata.js +0 -103
  111. package/scripts/check-reload-agent-spine-regression.js +0 -76
  112. package/scripts/check-resume-replay-regression.js +0 -102
  113. package/scripts/check-session-cancel-regression.js +0 -43
  114. package/scripts/check-session-picker-resume-metrics-regression.js +0 -97
  115. package/scripts/check-session-start-hook-context-regression.js +0 -228
  116. package/scripts/check-shell-terminal-isolation-regression.js +0 -81
  117. package/scripts/check-slash-escape-regression.js +0 -89
  118. package/scripts/check-startup-swarm-command-regression.js +0 -24
  119. package/scripts/check-structured-subagent-output-regression.js +0 -331
  120. package/scripts/check-telegram-bridge-watchdog.js +0 -60
  121. package/scripts/check-telegram-direct-work-resume-regression.js +0 -53
  122. package/scripts/check-telegram-loop-exactly-once-regression.js +0 -71
  123. package/scripts/check-todo-loop-regression.js +0 -78
  124. package/scripts/check-todo-progress-regression.js +0 -416
  125. package/scripts/check-todo-recovery-catalog-regression.js +0 -50
  126. package/scripts/check-tool-schema-capacity-regression.js +0 -40
  127. package/scripts/programmatic-tool-runtime.test.mjs +0 -365
  128. package/scripts/structured-subagent-output.test.cjs +0 -170
  129. /package/{scripts → bin}/fix-node-pty-perms.js +0 -0
@@ -16,8 +16,24 @@ const DEFAULT_LIMITS = Object.freeze({
16
16
  maxToolArgumentChars: 32_768,
17
17
  maxToolResultChars: 131_072,
18
18
  maxToolCalls: 64,
19
+ maxConsoleChars: 4_096,
20
+ maxConsoleRecords: 128,
21
+ maxConsoleArgs: 16,
22
+ maxConsoleRecordChars: 1_024,
23
+ maxConsoleDepth: 4,
24
+ maxConsoleEntries: 64,
19
25
  });
20
26
 
27
+ const CONSOLE_LIMIT_NAMES = Object.freeze([
28
+ 'maxConsoleChars',
29
+ 'maxConsoleRecords',
30
+ 'maxConsoleArgs',
31
+ 'maxConsoleRecordChars',
32
+ 'maxConsoleDepth',
33
+ 'maxConsoleEntries',
34
+ ]);
35
+ const CONSOLE_TRUNCATION_MARKER = '...[truncated]';
36
+
21
37
  let callSequence = 0;
22
38
 
23
39
  export function createProgrammaticToolRuntime(options) {
@@ -48,6 +64,7 @@ export function createProgrammaticToolRuntime(options) {
48
64
  let callCount = 0;
49
65
  let module;
50
66
  let context;
67
+ const consoleCapture = createConsoleCapture();
51
68
  emit({ type: 'interpreter.started', toolCount: allowedTools.length });
52
69
 
53
70
  try {
@@ -60,6 +77,16 @@ export function createProgrammaticToolRuntime(options) {
60
77
  runtime.setInterruptHandler(() => (
61
78
  controller.signal.aborted || shouldInterruptAfterDeadline(guestDeadline)()
62
79
  ));
80
+ const consoleBridgeName = '__blun_ptc_console';
81
+ const consoleBridge = context.newFunction(consoleBridgeName, (levelHandle, textHandle, truncatedHandle) => {
82
+ const level = context.getString(levelHandle);
83
+ const text = context.getString(textHandle);
84
+ const truncated = context.getNumber(truncatedHandle) === 1;
85
+ captureConsoleRecord(consoleCapture, { level, text, truncated }, limits);
86
+ return context.undefined;
87
+ });
88
+ context.setProp(context.global, consoleBridgeName, consoleBridge);
89
+ consoleBridge.dispose();
63
90
  for (const [index, tool] of allowedTools.entries()) {
64
91
  const bridgeName = `__blun_ptc_${index}`;
65
92
  const bridge = context.newAsyncifiedFunction(bridgeName, async (rawHandle) => {
@@ -123,7 +150,7 @@ export function createProgrammaticToolRuntime(options) {
123
150
  bridge.dispose();
124
151
  }
125
152
 
126
- evaluateBootstrap(context, renderBootstrap(allowedTools, initialStateJson));
153
+ evaluateBootstrap(context, renderBootstrap(allowedTools, initialStateJson, limits));
127
154
  const evaluated = await context.evalCodeAsync(renderProgram(code), 'blun-ptc.js');
128
155
  if (evaluated.error) {
129
156
  const dumped = context.dump(evaluated.error);
@@ -142,16 +169,30 @@ export function createProgrammaticToolRuntime(options) {
142
169
  if (stateJson.length > limits.maxStateChars) {
143
170
  throw new Error(`state exceeds ${limits.maxStateChars} characters`);
144
171
  }
145
- const outcome = { result: payload.result, state: payload.state, callCount };
146
- emit({ type: 'interpreter.completed', callCount, durationMs: Date.now() - startedAt });
172
+ const legacyOutcome = { result: payload.result, state: payload.state, callCount };
173
+ const fittedConsole = fitConsoleToBudget(legacyOutcome, consoleCapture, limits.maxOutputChars);
174
+ const outcome = fittedConsole ? { ...legacyOutcome, console: fittedConsole } : legacyOutcome;
175
+ emit({
176
+ type: 'interpreter.completed',
177
+ callCount,
178
+ durationMs: Date.now() - startedAt,
179
+ ...consoleEventMetadata(fittedConsole ?? consoleCapture),
180
+ });
147
181
  return outcome;
148
182
  } catch (error) {
149
183
  const normalized = normalizeExecutionError(error, controller.signal);
184
+ const fittedConsole = fitConsoleToBudget(
185
+ { error: normalized.message },
186
+ consoleCapture,
187
+ limits.maxOutputChars,
188
+ );
189
+ if (fittedConsole) attachProgrammaticConsole(normalized, fittedConsole);
150
190
  emit({
151
191
  type: 'interpreter.failed',
152
192
  callCount,
153
193
  durationMs: Date.now() - startedAt,
154
194
  error: normalized.message,
195
+ ...consoleEventMetadata(fittedConsole ?? consoleCapture),
155
196
  });
156
197
  throw normalized;
157
198
  } finally {
@@ -185,6 +226,11 @@ function normalizeLimits(value = {}) {
185
226
  for (const [name, number] of Object.entries(limits)) {
186
227
  if (!Number.isSafeInteger(number) || number <= 0) throw new Error(`${name} must be a positive integer`);
187
228
  }
229
+ for (const name of CONSOLE_LIMIT_NAMES) {
230
+ if (limits[name] > DEFAULT_LIMITS[name]) {
231
+ throw new Error(`${name} cannot exceed ${DEFAULT_LIMITS[name]}`);
232
+ }
233
+ }
188
234
  return Object.freeze(limits);
189
235
  }
190
236
 
@@ -205,7 +251,7 @@ function jsonStringify(value, label) {
205
251
  }
206
252
  }
207
253
 
208
- function renderBootstrap(tools, stateJson) {
254
+ function renderBootstrap(tools, stateJson, limits) {
209
255
  const entries = tools.map((tool, index) => {
210
256
  const bridge = `globalThis.__blun_ptc_${index}`;
211
257
  return `${JSON.stringify(tool.alias)}: (() => { const call = ${bridge}; delete ${bridge}; return (args = {}) => { const envelope = JSON.parse(call(JSON.stringify(args))); if (!envelope.ok) throw new Error(envelope.error); return envelope.value; }; })()`;
@@ -213,6 +259,183 @@ function renderBootstrap(tools, stateJson) {
213
259
  return `
214
260
  globalThis.state = JSON.parse(${JSON.stringify(stateJson)});
215
261
  globalThis.tools = Object.freeze({ ${entries.join(',')} });
262
+ (() => {
263
+ "use strict";
264
+ const bridge = globalThis.__blun_ptc_console;
265
+ delete globalThis.__blun_ptc_console;
266
+ const ownKeys = Reflect.ownKeys;
267
+ const apply = Reflect.apply;
268
+ const getDescriptor = Object.getOwnPropertyDescriptor;
269
+ const getPrototype = Object.getPrototypeOf;
270
+ const isArray = Array.isArray;
271
+ const isNaNNumber = Number.isNaN;
272
+ const isSame = Object.is;
273
+ const primitiveString = String;
274
+ const primitiveNumber = Number;
275
+ const minimum = Math.min;
276
+ const arraySort = Array.prototype.sort;
277
+ const arrayJoin = Array.prototype.join;
278
+ const stringCharCodeAt = String.prototype.charCodeAt;
279
+ const stringSlice = String.prototype.slice;
280
+ const numberToString = Number.prototype.toString;
281
+ const errorPrototype = Error.prototype;
282
+ const objectPrototype = Object.prototype;
283
+ const maxArgs = ${limits.maxConsoleArgs};
284
+ const maxRecordChars = ${limits.maxConsoleRecordChars};
285
+ const maxDepth = ${limits.maxConsoleDepth};
286
+ const maxEntries = ${limits.maxConsoleEntries};
287
+
288
+ const escapeText = (value) => {
289
+ let output = "";
290
+ for (let index = 0; index < value.length; index += 1) {
291
+ const code = apply(stringCharCodeAt, value, [index]);
292
+ if (code === 10) output += "\\\\n";
293
+ else if (code === 13) output += "\\\\r";
294
+ else if (code === 9) output += "\\\\t";
295
+ else if (code < 32 || code === 127) {
296
+ const hex = apply(numberToString, code, [16]);
297
+ output += "\\\\x" + (hex.length < 2 ? "0" + hex : hex);
298
+ }
299
+ else output += value[index];
300
+ }
301
+ return output;
302
+ };
303
+ const quote = (value) => {
304
+ const escaped = escapeText(value);
305
+ let output = '"';
306
+ for (let index = 0; index < escaped.length; index += 1) {
307
+ const character = escaped[index];
308
+ output += character === "\\\\" ? "\\\\\\\\" : character === '"' ? '\\\\"' : character;
309
+ }
310
+ return output + '"';
311
+ };
312
+ const isIndexKey = (key) => {
313
+ if (typeof key !== "string" || key.length === 0) return false;
314
+ for (let index = 0; index < key.length; index += 1) {
315
+ const code = apply(stringCharCodeAt, key, [index]);
316
+ if (code < 48 || code > 57) return false;
317
+ }
318
+ return true;
319
+ };
320
+ const keyText = (key) => typeof key === "string" ? quote(key) : "[Symbol]";
321
+ const markerForAccessor = (descriptor) => descriptor.get && descriptor.set
322
+ ? "[Getter/Setter]"
323
+ : descriptor.get ? "[Getter]" : "[Setter]";
324
+ const isErrorObject = (value) => {
325
+ let prototype;
326
+ try { prototype = getPrototype(value); } catch { return false; }
327
+ for (let depth = 0; prototype && depth < 8; depth += 1) {
328
+ if (prototype === errorPrototype) return true;
329
+ try { prototype = getPrototype(prototype); } catch { return false; }
330
+ }
331
+ return false;
332
+ };
333
+ const dataProperty = (value, key, fallback) => {
334
+ let current = value;
335
+ for (let depth = 0; current && depth < 8; depth += 1) {
336
+ try {
337
+ const descriptor = getDescriptor(current, key);
338
+ if (descriptor) return "value" in descriptor ? descriptor.value : fallback;
339
+ current = getPrototype(current);
340
+ } catch {
341
+ return fallback;
342
+ }
343
+ }
344
+ return fallback;
345
+ };
346
+ const format = (value, depth, ancestors, nested) => {
347
+ if (value === null) return "null";
348
+ const type = typeof value;
349
+ if (type === "string") return nested ? quote(value) : escapeText(value);
350
+ if (type === "undefined") return "undefined";
351
+ if (type === "boolean") return value ? "true" : "false";
352
+ if (type === "number") {
353
+ if (isNaNNumber(value)) return "NaN";
354
+ if (value === Infinity) return "Infinity";
355
+ if (value === -Infinity) return "-Infinity";
356
+ if (isSame(value, -0)) return "-0";
357
+ return primitiveString(value);
358
+ }
359
+ if (type === "bigint") return primitiveString(value) + "n";
360
+ if (type === "symbol") return "[Symbol]";
361
+ if (type === "function") return "[Function]";
362
+ for (let index = 0; index < ancestors.length; index += 1) {
363
+ if (ancestors[index] === value) return "[Circular]";
364
+ }
365
+ if (depth >= maxDepth) return "[MaxDepth]";
366
+ const nextAncestors = [];
367
+ for (let index = 0; index < ancestors.length; index += 1) nextAncestors[index] = ancestors[index];
368
+ nextAncestors[ancestors.length] = value;
369
+ if (isErrorObject(value)) {
370
+ const rawName = dataProperty(value, "name", "Error");
371
+ const rawMessage = dataProperty(value, "message", "");
372
+ const name = typeof rawName === "string" ? escapeText(rawName) : "Error";
373
+ const message = typeof rawMessage === "string" ? escapeText(rawMessage) : "";
374
+ return message ? name + ": " + message : name;
375
+ }
376
+ let prototype;
377
+ try { prototype = getPrototype(value); } catch { return "[Object]"; }
378
+ const array = isArray(value);
379
+ if (!array && prototype !== objectPrototype && prototype !== null) return "[Object]";
380
+ let keys;
381
+ try { keys = ownKeys(value); } catch { return "[Object]"; }
382
+ apply(arraySort, keys, [(left, right) => {
383
+ if (array && isIndexKey(left) && isIndexKey(right)) return primitiveNumber(left) - primitiveNumber(right);
384
+ const a = typeof left === "string" ? "0:" + left : "1:";
385
+ const b = typeof right === "string" ? "0:" + right : "1:";
386
+ return a < b ? -1 : a > b ? 1 : 0;
387
+ }]);
388
+ const parts = [];
389
+ const visibleKeys = [];
390
+ for (let index = 0; index < keys.length; index += 1) {
391
+ if (!array || keys[index] !== "length") visibleKeys[visibleKeys.length] = keys[index];
392
+ }
393
+ const retained = minimum(visibleKeys.length, maxEntries);
394
+ for (let index = 0; index < retained; index += 1) {
395
+ const key = visibleKeys[index];
396
+ let descriptor;
397
+ try { descriptor = getDescriptor(value, key); } catch {
398
+ parts[parts.length] = keyText(key) + ": [Unavailable]";
399
+ continue;
400
+ }
401
+ const formatted = descriptor && "value" in descriptor
402
+ ? format(descriptor.value, depth + 1, nextAncestors, true)
403
+ : markerForAccessor(descriptor || {});
404
+ parts[parts.length] = array && isIndexKey(key)
405
+ ? formatted
406
+ : keyText(key) + ": " + formatted;
407
+ }
408
+ if (visibleKeys.length > retained) parts[parts.length] = "[+" + (visibleKeys.length - retained) + " entries]";
409
+ return (array ? "[" : "{") + apply(arrayJoin, parts, [", "]) + (array ? "]" : "}");
410
+ };
411
+ const emit = (level, values) => {
412
+ const retained = minimum(values.length, maxArgs);
413
+ const parts = [];
414
+ for (let index = 0; index < retained; index += 1) {
415
+ parts[parts.length] = format(values[index], 0, [], false);
416
+ }
417
+ let truncated = values.length > retained;
418
+ if (truncated) parts[parts.length] = "[+" + (values.length - retained) + " args]";
419
+ let text = apply(arrayJoin, parts, [" "]);
420
+ if (text.length > maxRecordChars) {
421
+ text = apply(stringSlice, text, [0, maxRecordChars]);
422
+ truncated = true;
423
+ }
424
+ bridge(level, text, truncated ? 1 : 0);
425
+ };
426
+ const makeMethod = (level) => Object.freeze((...values) => emit(level, values));
427
+ const consoleObject = Object.freeze({
428
+ log: makeMethod("log"),
429
+ warn: makeMethod("warn"),
430
+ error: makeMethod("error"),
431
+ });
432
+ Object.defineProperty(globalThis, "console", {
433
+ value: consoleObject,
434
+ writable: false,
435
+ configurable: false,
436
+ enumerable: false,
437
+ });
438
+ })();
216
439
  delete globalThis.process;
217
440
  delete globalThis.require;
218
441
  delete globalThis.fetch;
@@ -228,6 +451,109 @@ function renderProgram(code) {
228
451
  return `JSON.stringify({ result: (() => { "use strict"; ${code}\n})(), state: globalThis.state })`;
229
452
  }
230
453
 
454
+ function createConsoleCapture() {
455
+ return {
456
+ called: false,
457
+ records: [],
458
+ chars: 0,
459
+ truncated: false,
460
+ droppedRecords: 0,
461
+ markerAdded: false,
462
+ };
463
+ }
464
+
465
+ function captureConsoleRecord(capture, record, limits) {
466
+ capture.called = true;
467
+ const level = record.level;
468
+ if (level !== 'log' && level !== 'warn' && level !== 'error') {
469
+ capture.truncated = true;
470
+ capture.droppedRecords += 1;
471
+ markConsoleTruncated(capture, limits, 'log');
472
+ return;
473
+ }
474
+ if (capture.records.length >= limits.maxConsoleRecords || capture.chars >= limits.maxConsoleChars) {
475
+ capture.truncated = true;
476
+ capture.droppedRecords += 1;
477
+ markConsoleTruncated(capture, limits, level);
478
+ return;
479
+ }
480
+ let text = record.text.slice(0, limits.maxConsoleRecordChars);
481
+ let truncated = record.truncated || record.text.length > text.length;
482
+ const remaining = limits.maxConsoleChars - capture.chars;
483
+ if (text.length > remaining) {
484
+ text = text.slice(0, remaining);
485
+ truncated = true;
486
+ }
487
+ if (text.length === 0 && record.text.length > 0) {
488
+ capture.truncated = true;
489
+ capture.droppedRecords += 1;
490
+ markConsoleTruncated(capture, limits, level);
491
+ return;
492
+ }
493
+ capture.records.push({ level, text });
494
+ capture.chars += text.length;
495
+ if (truncated) {
496
+ capture.truncated = true;
497
+ markConsoleTruncated(capture, limits, level);
498
+ }
499
+ }
500
+
501
+ function markConsoleTruncated(capture, limits, level) {
502
+ if (capture.markerAdded) return;
503
+ const marker = CONSOLE_TRUNCATION_MARKER;
504
+ let record = capture.records.at(-1);
505
+ if (!record && capture.records.length < limits.maxConsoleRecords && limits.maxConsoleChars >= marker.length) {
506
+ record = { level, text: '' };
507
+ capture.records.push(record);
508
+ }
509
+ if (!record) return;
510
+ const previousLength = record.text.length;
511
+ const availableForRecord = Math.min(limits.maxConsoleRecordChars, limits.maxConsoleChars - (capture.chars - previousLength));
512
+ if (availableForRecord < marker.length) return;
513
+ record.text = record.text.slice(0, availableForRecord - marker.length) + marker;
514
+ capture.chars += record.text.length - previousLength;
515
+ capture.markerAdded = true;
516
+ }
517
+
518
+ function fitConsoleToBudget(base, capture, maxOutputChars) {
519
+ if (!capture.called) return undefined;
520
+ if (JSON.stringify(base).length >= maxOutputChars) return undefined;
521
+ const fitted = {
522
+ records: capture.records.map((record) => ({ ...record })),
523
+ truncated: capture.truncated,
524
+ droppedRecords: capture.droppedRecords,
525
+ };
526
+ const fits = () => JSON.stringify({ ...base, console: fitted }).length <= maxOutputChars;
527
+ while (!fits() && fitted.records.length > 0) {
528
+ const removed = fitted.records.pop();
529
+ fitted.droppedRecords += 1;
530
+ fitted.truncated = true;
531
+ if (removed?.text?.includes(CONSOLE_TRUNCATION_MARKER)) {
532
+ const last = fitted.records.at(-1);
533
+ if (last) last.text = last.text.slice(0, Math.max(0, last.text.length - CONSOLE_TRUNCATION_MARKER.length)) + CONSOLE_TRUNCATION_MARKER;
534
+ }
535
+ }
536
+ return fits() ? fitted : undefined;
537
+ }
538
+
539
+ function consoleEventMetadata(consoleValue) {
540
+ const records = Array.isArray(consoleValue?.records) ? consoleValue.records : [];
541
+ return {
542
+ consoleRecordCount: records.length,
543
+ consoleChars: records.reduce((total, record) => total + String(record.text ?? '').length, 0),
544
+ consoleTruncated: consoleValue?.truncated === true,
545
+ };
546
+ }
547
+
548
+ function attachProgrammaticConsole(error, consoleValue) {
549
+ Object.defineProperty(error, 'programmaticConsole', {
550
+ value: consoleValue,
551
+ enumerable: false,
552
+ configurable: false,
553
+ writable: false,
554
+ });
555
+ }
556
+
231
557
  function evaluateBootstrap(context, source) {
232
558
  const result = context.evalCode(source, 'blun-ptc-bootstrap.js');
233
559
  if (result.error) {
@@ -1,7 +1,35 @@
1
1
  "use strict";
2
2
 
3
- const READ_LINE_CAP = 1000;
4
- const READ_MODEL_VISIBLE_BYTES = 10 * 1024;
3
+ const READ_PRESSURE_RATIO = 0.75;
4
+ const READ_PRESSURE_CHUNK_BYTES = 10 * 1024;
5
+ const READ_PRESSURE_LINE_CAP = 1000;
6
+ const READ_ESTIMATED_BYTES_PER_TOKEN = 4;
7
+
8
+ function availableReadContextTokens(input = {}) {
9
+ const contextTokens = Number(input.contextTokens);
10
+ const maxContextTokens = Number(input.maxContextTokens);
11
+ if (!Number.isFinite(contextTokens) || contextTokens < 0) return undefined;
12
+ if (!Number.isFinite(maxContextTokens) || maxContextTokens <= 0) return undefined;
13
+
14
+ const pressureTokens = Math.floor(maxContextTokens * READ_PRESSURE_RATIO);
15
+ return Math.max(0, pressureTokens - contextTokens);
16
+ }
17
+
18
+ function readModelVisibleBytes(input = {}) {
19
+ const availableTokens = availableReadContextTokens(input);
20
+ if (availableTokens === undefined) return READ_PRESSURE_CHUNK_BYTES;
21
+ const availableBytes = Math.min(
22
+ Number.MAX_SAFE_INTEGER,
23
+ availableTokens * READ_ESTIMATED_BYTES_PER_TOKEN,
24
+ );
25
+ return Math.max(READ_PRESSURE_CHUNK_BYTES, availableBytes);
26
+ }
27
+
28
+ function readModelVisibleLines(input = {}) {
29
+ const availableTokens = availableReadContextTokens(input);
30
+ if (availableTokens === undefined) return READ_PRESSURE_LINE_CAP;
31
+ return Math.max(READ_PRESSURE_LINE_CAP, availableTokens);
32
+ }
5
33
 
6
34
  function readContinuationNotice(input) {
7
35
  if (!Number.isSafeInteger(input.lineOffset) || input.lineOffset < 1) return undefined;
@@ -10,7 +38,7 @@ function readContinuationNotice(input) {
10
38
  if (!Number.isSafeInteger(input.renderedLineCount) || input.renderedLineCount < 1) return undefined;
11
39
  readLines = input.renderedLineCount;
12
40
  } else if (input?.maxLinesReached === true) {
13
- if (!Number.isSafeInteger(input.effectiveLimit) || input.effectiveLimit < READ_LINE_CAP) return undefined;
41
+ if (!Number.isSafeInteger(input.effectiveLimit) || input.effectiveLimit < input.maxLines) return undefined;
14
42
  readLines = input.effectiveLimit;
15
43
  } else {
16
44
  return undefined;
@@ -22,7 +50,10 @@ function readContinuationNotice(input) {
22
50
  }
23
51
 
24
52
  module.exports = {
25
- READ_LINE_CAP,
26
- READ_MODEL_VISIBLE_BYTES,
53
+ READ_PRESSURE_RATIO,
54
+ READ_PRESSURE_CHUNK_BYTES,
55
+ READ_PRESSURE_LINE_CAP,
56
+ readModelVisibleBytes,
57
+ readModelVisibleLines,
27
58
  readContinuationNotice,
28
59
  };
@@ -0,0 +1,13 @@
1
+ 'use strict';
2
+
3
+ async function persistRetrySchedule(input = {}) {
4
+ const { dispatchRetrying, flush, event, signal } = input;
5
+ if (typeof dispatchRetrying !== 'function') throw new TypeError('persistRetrySchedule requires dispatchRetrying');
6
+ if (typeof flush !== 'function') throw new TypeError('persistRetrySchedule requires flush');
7
+ signal?.throwIfAborted?.();
8
+ await dispatchRetrying(event);
9
+ await flush();
10
+ signal?.throwIfAborted?.();
11
+ }
12
+
13
+ module.exports = { persistRetrySchedule };