blun-king-cli 9.1.536 → 9.1.550
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/LIESMICH.txt +13 -869
- package/README.md +41 -833
- package/bin/assistant-message-offload-policy.cjs +3 -1
- package/bin/context-performance-policy.cjs +2 -5
- package/bin/context-pressure-policy.cjs +20 -0
- package/bin/cron-run-output.cjs +45 -0
- package/bin/cron-run-store.cjs +145 -0
- package/bin/durable-task-resume-policy.cjs +130 -0
- package/bin/durable-task-resume-runtime.cjs +117 -0
- package/bin/durable-task-resume-store.cjs +88 -0
- package/bin/editable-tool-approval-policy.cjs +540 -0
- package/bin/editable-tool-approval-runtime.cjs +99 -0
- package/bin/html-to-research-markdown.cjs +146 -0
- package/bin/programmatic-tool-runtime.mjs +330 -4
- package/bin/read-continuation-policy.cjs +36 -5
- package/bin/scoped-cron-run-policy.cjs +358 -0
- package/bin/startup-preferences.cjs +3 -3
- package/bin/structured-agent-swarm-output.cjs +325 -0
- package/bin/subagent-context-fork-policy.cjs +155 -0
- package/bin/subagent-skill-policy.cjs +204 -0
- package/bin/telegram-approval-relay.cjs +2 -1
- package/bin/tool-result-offload-policy.cjs +14 -1
- package/bin/update-notice.js +14 -18
- package/bin/user-message-offload-policy.cjs +3 -1
- package/blun.mjs +1089 -477
- package/codebase-index/README.md +12 -0
- package/codebase-index/codebase_index.py +129 -18
- package/package.json +23 -58
- package/telegram-plugin/bin/telegram-typing-keepalive.cjs +89 -0
- package/telegram-plugin/dist/bridge.mjs +8 -1
- package/CHANGELOG.md +0 -321
- package/agent-spine-plugin/CHANGELOG.md +0 -406
- package/agent-spine-plugin/CONTRIBUTING.md +0 -52
- package/agent-spine-plugin/README.md +0 -344
- package/agent-spine-plugin/SECURITY.md +0 -47
- package/agent-spine-plugin/docs/acceptance.md +0 -61
- package/agent-spine-plugin/docs/architecture.md +0 -183
- package/agent-spine-plugin/docs/attention.md +0 -121
- package/agent-spine-plugin/docs/automatic-continuity.md +0 -79
- package/agent-spine-plugin/docs/channel-runtime.md +0 -92
- package/agent-spine-plugin/docs/coordination.md +0 -138
- package/agent-spine-plugin/docs/feed-transport.md +0 -99
- package/agent-spine-plugin/docs/gateway-runtime.md +0 -116
- package/agent-spine-plugin/docs/harness-reference.md +0 -45
- package/agent-spine-plugin/docs/host-integration.md +0 -129
- package/agent-spine-plugin/docs/https-transport.md +0 -116
- package/agent-spine-plugin/docs/learning.md +0 -133
- package/agent-spine-plugin/docs/object-transport.md +0 -93
- package/agent-spine-plugin/docs/peer-transport.md +0 -88
- package/agent-spine-plugin/docs/preflight-recall.md +0 -69
- package/agent-spine-plugin/docs/preservation-contract.md +0 -53
- package/agent-spine-plugin/docs/quality-gates.md +0 -50
- package/agent-spine-plugin/docs/relationships.md +0 -73
- package/agent-spine-plugin/docs/releasing.md +0 -83
- package/agent-spine-plugin/docs/roadmap.md +0 -307
- package/agent-spine-plugin/docs/selfstarter.md +0 -88
- package/agent-spine-plugin/docs/session-briefing.md +0 -74
- package/agent-spine-plugin/docs/shared-memory.md +0 -259
- package/agent-spine-plugin/docs/source-roots.md +0 -86
- package/agent-spine-plugin/docs/sqlite-transport.md +0 -76
- package/agent-spine-plugin/scripts/check-hosts.js +0 -195
- package/agent-spine-plugin/scripts/check-install.js +0 -569
- package/agent-spine-plugin/scripts/check-syntax.js +0 -29
- package/agent-spine-plugin/scripts/github-actions.js +0 -11
- package/agent-spine-plugin/scripts/release-check.js +0 -128
- package/agent-spine-plugin/scripts/run-acceptance.js +0 -19
- package/agent-spine-plugin/scripts/run-checks.js +0 -46
- package/agent-spine-plugin/scripts/run-tests-hermetic.js +0 -73
- package/agent-spine-plugin/spine-example/1-identity.md +0 -12
- package/agent-spine-plugin/spine-example/2-voice.md +0 -6
- package/agent-spine-plugin/spine-example/3-conduct.md +0 -8
- package/agent-spine-plugin/spine-example/4-history.md +0 -4
- package/bin/fredrik-glm-provider.cjs +0 -256
- package/bin/package-regression-policy.cjs +0 -77
- package/fredrik-glm-profile.toml.example +0 -26
- package/release-planned-removals.json +0 -15
- package/scripts/check-active-profile-plugin-startup.js +0 -36
- package/scripts/check-active-work-steer-regression.js +0 -46
- package/scripts/check-approval-observability-regression.js +0 -111
- package/scripts/check-approval-queue-shortcuts-regression.js +0 -65
- package/scripts/check-bundled-agent-spine-regression.js +0 -48
- package/scripts/check-codebase-search-packaging-regression.js +0 -92
- package/scripts/check-copy-command-regression.js +0 -74
- package/scripts/check-current-turn-read-pin-mutation-regression.js +0 -72
- package/scripts/check-current-turn-read-pin-regression.js +0 -94
- package/scripts/check-deepseek-native-max-regression.js +0 -49
- package/scripts/check-empty-response-effort-downgrade-regression.js +0 -48
- package/scripts/check-fredrik-glm-mutation-regression.js +0 -18
- package/scripts/check-fredrik-glm-regression.js +0 -169
- package/scripts/check-historical-tool-result-preview-regression.js +0 -77
- package/scripts/check-history-pressure-offload-regression.js +0 -77
- package/scripts/check-mcp-startup-wait-budget.js +0 -48
- package/scripts/check-package-regression.js +0 -38
- package/scripts/check-plugin-startup-regression.js +0 -53
- package/scripts/check-programmatic-context-isolation-regression.js +0 -193
- package/scripts/check-programmatic-tool-regression.js +0 -294
- package/scripts/check-queue-controls-regression.js +0 -189
- package/scripts/check-release-metadata.js +0 -103
- package/scripts/check-reload-agent-spine-regression.js +0 -76
- package/scripts/check-resume-replay-regression.js +0 -102
- package/scripts/check-session-cancel-regression.js +0 -43
- package/scripts/check-session-picker-resume-metrics-regression.js +0 -97
- package/scripts/check-session-start-hook-context-regression.js +0 -228
- package/scripts/check-shell-terminal-isolation-regression.js +0 -81
- package/scripts/check-slash-escape-regression.js +0 -89
- package/scripts/check-startup-swarm-command-regression.js +0 -24
- package/scripts/check-structured-subagent-output-regression.js +0 -331
- package/scripts/check-telegram-bridge-watchdog.js +0 -60
- package/scripts/check-telegram-direct-work-resume-regression.js +0 -53
- package/scripts/check-telegram-loop-exactly-once-regression.js +0 -71
- package/scripts/check-todo-loop-regression.js +0 -78
- package/scripts/check-todo-progress-regression.js +0 -416
- package/scripts/check-todo-recovery-catalog-regression.js +0 -50
- package/scripts/check-tool-schema-capacity-regression.js +0 -40
- package/scripts/programmatic-tool-runtime.test.mjs +0 -365
- package/scripts/structured-subagent-output.test.cjs +0 -170
- /package/{scripts → bin}/fix-node-pty-perms.js +0 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { Readability } = require('@mozilla/readability');
|
|
4
|
+
const { parseHTML } = require('linkedom');
|
|
5
|
+
const TurndownService = require('turndown');
|
|
6
|
+
const { tables } = require('turndown-plugin-gfm');
|
|
7
|
+
|
|
8
|
+
const MAX_LINK_LENGTH = 2_048;
|
|
9
|
+
const JUNK_SELECTORS = Object.freeze([
|
|
10
|
+
'script',
|
|
11
|
+
'style',
|
|
12
|
+
'noscript',
|
|
13
|
+
'nav',
|
|
14
|
+
'footer',
|
|
15
|
+
'header',
|
|
16
|
+
'aside',
|
|
17
|
+
'iframe',
|
|
18
|
+
'svg',
|
|
19
|
+
'form',
|
|
20
|
+
'button',
|
|
21
|
+
'input',
|
|
22
|
+
'[role="navigation"]',
|
|
23
|
+
'[role="banner"]',
|
|
24
|
+
'[role="contentinfo"]',
|
|
25
|
+
'[aria-hidden="true"]',
|
|
26
|
+
'[hidden]',
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
function canonicalBaseUrl(value) {
|
|
30
|
+
const parsed = new URL(value);
|
|
31
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
32
|
+
throw new Error('HTML-to-Markdown base URL must use http or https.');
|
|
33
|
+
}
|
|
34
|
+
return parsed.toString();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function safeResolvedLink(raw, baseUrl) {
|
|
38
|
+
if (typeof raw !== 'string' || raw.trim().length === 0) return null;
|
|
39
|
+
let resolved;
|
|
40
|
+
try {
|
|
41
|
+
resolved = new URL(raw, baseUrl);
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
if (resolved.protocol !== 'http:' && resolved.protocol !== 'https:') return null;
|
|
46
|
+
const value = resolved.toString();
|
|
47
|
+
return value.length <= MAX_LINK_LENGTH ? value : null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function removeAll(root, selector) {
|
|
51
|
+
for (const element of root.querySelectorAll(selector)) element.remove();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function sanitizeTree(root, baseUrl) {
|
|
55
|
+
for (const selector of JUNK_SELECTORS) removeAll(root, selector);
|
|
56
|
+
|
|
57
|
+
for (const element of root.querySelectorAll('[style]')) {
|
|
58
|
+
const style = element.getAttribute('style') ?? '';
|
|
59
|
+
if (/display\s*:\s*none|visibility\s*:\s*hidden/iu.test(style)) element.remove();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
for (const selector of [
|
|
63
|
+
'sup.reference',
|
|
64
|
+
'sup.noprint',
|
|
65
|
+
'.reflist',
|
|
66
|
+
'.references',
|
|
67
|
+
'.refbegin',
|
|
68
|
+
'.catlinks',
|
|
69
|
+
'.mw-authority-control',
|
|
70
|
+
]) removeAll(root, selector);
|
|
71
|
+
|
|
72
|
+
for (const anchor of root.querySelectorAll('a[href]')) {
|
|
73
|
+
const safe = safeResolvedLink(anchor.getAttribute('href'), baseUrl);
|
|
74
|
+
if (safe === null) anchor.removeAttribute('href');
|
|
75
|
+
else anchor.setAttribute('href', safe);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
for (const image of root.querySelectorAll('img')) {
|
|
79
|
+
const alt = (image.getAttribute('alt') ?? '').trim();
|
|
80
|
+
if (alt.length > 0) image.replaceWith(image.ownerDocument.createTextNode(alt));
|
|
81
|
+
else image.remove();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function createMarkdownService() {
|
|
86
|
+
const service = new TurndownService({
|
|
87
|
+
headingStyle: 'atx',
|
|
88
|
+
codeBlockStyle: 'fenced',
|
|
89
|
+
bulletListMarker: '-',
|
|
90
|
+
});
|
|
91
|
+
service.use(tables);
|
|
92
|
+
service.remove(['script', 'style', 'noscript', 'iframe', 'svg']);
|
|
93
|
+
return service;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function withTitle(title, markdown) {
|
|
97
|
+
const cleanTitle = title.trim();
|
|
98
|
+
if (cleanTitle.length === 0) return markdown;
|
|
99
|
+
const titleHeading = `# ${cleanTitle}`;
|
|
100
|
+
if (markdown === titleHeading || markdown.startsWith(`${titleHeading}\n`)) return markdown;
|
|
101
|
+
return `${titleHeading}\n\n${markdown}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function renderRoot(root, title, safeBaseUrl) {
|
|
105
|
+
sanitizeTree(root, safeBaseUrl);
|
|
106
|
+
const fallback = (root.textContent ?? '').replace(/\s+/gu, ' ').trim();
|
|
107
|
+
let markdown = createMarkdownService().turndown(root.innerHTML).trim();
|
|
108
|
+
if (markdown.length === 0) markdown = fallback;
|
|
109
|
+
markdown = markdown.replace(/\n{4,}/gu, '\n\n\n').trim();
|
|
110
|
+
return withTitle(title, markdown);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function htmlToResearchMarkdown(html, baseUrl) {
|
|
114
|
+
if (typeof html !== 'string' || html.trim().length === 0) return '';
|
|
115
|
+
const safeBaseUrl = canonicalBaseUrl(baseUrl);
|
|
116
|
+
const primary = parseHTML(html);
|
|
117
|
+
try {
|
|
118
|
+
const article = new Readability(primary.document, { charThreshold: 0 }).parse();
|
|
119
|
+
if (article !== null && (article.content ?? '').trim().length > 0) {
|
|
120
|
+
const extracted = parseHTML(article.content);
|
|
121
|
+
const extractedRoot = extracted.document.querySelector('body');
|
|
122
|
+
if (extractedRoot !== null) {
|
|
123
|
+
const markdown = renderRoot(extractedRoot, article.title ?? '', safeBaseUrl);
|
|
124
|
+
if (markdown.length > 0) return markdown;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
} catch {
|
|
128
|
+
// Keep the existing fail-open container fallback.
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const { document } = parseHTML(html);
|
|
132
|
+
const title = (document.querySelector('title')?.textContent ?? '').trim();
|
|
133
|
+
const root = document.querySelector('article')
|
|
134
|
+
?? document.querySelector('main')
|
|
135
|
+
?? document.querySelector('[role="main"]')
|
|
136
|
+
?? document.querySelector('body');
|
|
137
|
+
return root === null ? '' : renderRoot(root, title, safeBaseUrl);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
module.exports = Object.freeze({
|
|
141
|
+
JUNK_SELECTORS,
|
|
142
|
+
MAX_LINK_LENGTH,
|
|
143
|
+
canonicalBaseUrl,
|
|
144
|
+
htmlToResearchMarkdown,
|
|
145
|
+
safeResolvedLink,
|
|
146
|
+
});
|
|
@@ -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
|
|
146
|
-
|
|
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
|
|
4
|
-
const
|
|
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 <
|
|
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
|
-
|
|
26
|
-
|
|
53
|
+
READ_PRESSURE_RATIO,
|
|
54
|
+
READ_PRESSURE_CHUNK_BYTES,
|
|
55
|
+
READ_PRESSURE_LINE_CAP,
|
|
56
|
+
readModelVisibleBytes,
|
|
57
|
+
readModelVisibleLines,
|
|
27
58
|
readContinuationNotice,
|
|
28
59
|
};
|