Fixes
Release rolling / release (push) Successful in 10m16s

This commit is contained in:
2026-08-18 23:56:09 -04:00
parent 7184b17553
commit eb0683abe3
11 changed files with 170 additions and 13 deletions
+50 -3
View File
@@ -1433,6 +1433,38 @@ function bareAgentProcReadPathAllowed(absPath) {
return Boolean(p && (p === '/proc' || p.startsWith('/proc/')))
}
/**
* Kernel-runner evals guest scripts as AsyncFunction('ctx','argv', body).
* Agent-authored code that redeclares ctx/argv or uses ESM export throws
* "Identifier 'ctx' has already been declared" — same contract as /bin
* utilities: define async function run(ctx, argv), never bind ctx yourself.
* @param {string} code
*/
function bareAgentNormalizeGuestScript(code) {
let src = String(code || '').replace(/^\uFEFF/, '')
src = src.replace(/^#!.*(?:\r?\n|$)/, '')
src = src.replace(/^\s*export\s*\{[^}]*\}\s*;?\s*$/gm, '')
src = src.replace(/^export\s+default\s+/gm, '')
src = src.replace(/^export\s+async\s+function\s+/gm, 'async function ')
src = src.replace(/^export\s+function\s+/gm, 'function ')
src = src.replace(/^export\s+const\s+/gm, 'const ')
src = src.replace(/^export\s+let\s+/gm, 'let ')
src = src.replace(/^export\s+var\s+/gm, 'var ')
src = src.replace(/^(?:const|let|var)\s+ctx\b[^;\n]*;?\s*$/gm, '')
src = src.replace(/^(?:const|let|var)\s+argv\b[^;\n]*;?\s*$/gm, '')
src = src.replace(
/^(?:const|let|var)\s*\{\s*ctx[\s,}][^;\n]*;?\s*$/gm,
''
)
if (
!/\b(?:async\s+)?function\s+run\s*\(/.test(src) &&
!/\brun\s*=\s*(?:async\s*)?(?:function\s*)?\(/.test(src)
) {
src = 'async function run(ctx, argv) {\n' + src.trim() + '\n}\n'
}
return src.trim() + '\n'
}
/** ~/.agent paths, config, history trim, man digest (preamble for /bin/agent). */
/**
@@ -6950,7 +6982,7 @@ function bareAgentToolDefinitions() {
function: {
name: 'run_js_script',
description:
'REQUIRED to run agent-authored JavaScript: Node is not installed. Writes code to ~/.agent/_tmp_agent_run.mjs and runs it by absolute path (Bare kernel — same as /bin scripts). Do not use run_command with node/npm/npx. Prefer async function run(ctx, argv). stdout/stderr captured.',
'Run custom guest JavaScript only. Do not use this to replace read_file, write_file, list_directory, or run_command. Node is not installed. Writes ~/.agent/_tmp_agent_run.mjs and runs it on the Bare kernel. ctx and argv are already injected — never declare them, never export. Body must be: async function run(ctx, argv) { const vfs = ctx.vfs; ... }',
parameters: {
type: 'object',
properties: {
@@ -9373,7 +9405,11 @@ async function bareAgentDispatchTool(o) {
}
if (toolName === 'run_js_script') {
const code = typeof args.code === 'string' ? args.code : ''
const rawCode = typeof args.code === 'string' ? args.code : ''
const code =
typeof bareAgentNormalizeGuestScript === 'function'
? bareAgentNormalizeGuestScript(rawCode)
: rawCode
const scriptPath = paths.dir + '/_tmp_agent_run.mjs'
if (!vfs?.writeFile || !execLine) {
return bareAgentJsonResult({ ok: false, error: 'vfs or execLine' })
@@ -9387,6 +9423,17 @@ async function bareAgentDispatchTool(o) {
/** Absolute path → kernel-runner runs .mjs like `./script.mjs` (no host `node` binary). captureExec adds stdout redirect. */
const cmd = bareAgentShellQuote(scriptPath)
const r = await captureExec(cmd, 60000)
const out = r && typeof r === 'object' ? r : {}
const text = String(out.stdout_stderr || out.error || '')
if (/already been declared/i.test(text) || /already been declared/i.test(String(out.error || ''))) {
return bareAgentJsonResult({
ok: false,
error: 'guest_script_ctx_redeclared',
hint:
'The kernel already injects ctx and argv. Do not write const/let/var ctx or export. Use native tools (read_file, write_file, run_command) unless you need custom JS. Script body should be only: async function run(ctx, argv) { const vfs = ctx.vfs; ... }',
stdout_stderr: text
})
}
return bareAgentJsonResult(
r.ok === false ? r : { ok: true, stdout_stderr: r.stdout_stderr }
)
@@ -13380,7 +13427,7 @@ ACCESS (denylist, not allowlist). You already have full guest admin. You can cre
- Read any absolute path, including /proc (read_proc_file, runtime_diagnostic_bundle). Use read_file offset/limit for large files.
- Run every guest command via run_command (command_deny is empty by default). Prefer list_directory, glob_files, and file_stat over ls/find when you only need names. list_bin lists guest /bin utilities (POSIX-in-JS, not GNU).
- Delete and move are enabled. Do not mutate the read-only base system: /bin /etc /boot /lib /usr /share /proc /dev /sys /run.
- Node is not installed in the guest. Never plan or run node, npm, or npx here. Author JS with run_js_script (writes under ~/.agent) or run_js_script_at_path / run_command with an absolute .mjs path. Guest scripts use async function run(ctx, argv) ctx.vfs, ctx.execLine, ctx.console, ctx.exitCode. Do not import Node builtins.
- Node is not installed in the guest. Never plan or run node, npm, or npx here. Do not use run_js_script to read files, write files, or run shell — call read_file, write_file, run_command instead. Only use run_js_script for custom guest JS. The kernel already injects ctx and argv: never write const/let/var ctx, never export. Body must be only async function run(ctx, argv) { const vfs = ctx.vfs; ... }. Do not import Node builtins. Never paste a "final correct script" into the user reply — call the tool.
- Live kernel: read_proc_file on /proc/bare_os/features (or features.json) and /proc/bare_os/capabilities.json. Man pages and apropos_man are documentation only — never infer what is enabled from them.
- web_fetch uses the same host allow/deny list as wget/curl.
- emit_host_notification and request_host_action are enabled by default. emergency_stop_mutations is the kill switch.
@@ -81,7 +81,7 @@ For the **full** list and semantics, open **`developer-guide/02-the-context-obje
* @param {Record<string, unknown>} ctx
* @param {string[]} argv
*/
export async function run(ctx, argv) {
async function run(ctx, argv) {
const argv0 = argv[0] || 'script'
const vfs = ctx.vfs
if (!vfs?.readFile) {