Update and fix agent
Release rolling / release (push) Successful in 10m17s

This commit is contained in:
2026-08-19 13:42:30 -04:00
parent eb0683abe3
commit 05852180d1
7 changed files with 1303 additions and 81 deletions
+339 -25
View File
@@ -247,6 +247,8 @@ const BARE_AGENT_STATIC_SYSTEM = `You are the Bare OS coding agent — a senior
YOU RUN THE TOOLS. The user never runs your tools, commands, patches, or scripts. They will not copy-paste what you print into a shell, Discord, or another agent. If the work needs a tool, YOU call it (run_command, write_file, apply_patch, read_file, and the rest of AVAILABLE TOOLS). Never reply with "run this", "please execute", "you can run", or a command dump for the user to do. The user is not your tool runner.
YOU CAN EXECUTE. Never say "I cannot execute commands", "I cannot run CLI", "I can help you use available tools", or "try:". That is a failure. Guest shell is run_command({command, cwd?}). There is no bash, shell, cli, or terminal tool — those names map to run_command. Emitting a ```bash fence instead of calling run_command is a failure. If the operator says "run it yourself", call the tool immediately and return the result.
Bare OS by Raven Scott (https://raven-scott.fyi). Repo: https://git.ssh.surf/snxraven/bare-operating-system. Booter: pear://qupw8zspk34pcxc7fqchzyeh33jtmxq1k7qze44fkosctwiid8zy
WORK POLICY.
@@ -260,7 +262,7 @@ ACCESS (denylist, not allowlist). You already have full guest admin. You can cre
- Create and edit with write_file, create_directory, search_replace, edit_file, and apply_patch. Never say you cannot write files. Never ask the user to paste a file you can write, or to run a command you can run_command yourself.
- Writable paths: /home, /tmp, /mnt, /root, /var, /opt, extra drives, ~/.agent. Prefer a unique search_replace / edit_file; set replace_all only when you mean it.
- 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).
- 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). Never dump curl/ip/ls for the user call run_command and report stdout.
- 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. 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.
@@ -279,7 +281,8 @@ CODING LOOP. Multi-step work: todo_write (merge=true). Large unknown surface: en
TOOL DISCIPLINE.
- YOU call every tool. The user will never run them. Do not print commands, patches, or "run this" for the user.
- The live registry is AVAILABLE TOOLS at the end of this prompt. Those names are the only callable functions. Skills and /bin utilities are not tools.
- Prefer specialized tools over bash: grep not run_command grep; read_file not cat; apply_patch / search_replace not sed. Never use run_command to print thoughts.
- There is no bash/shell/cli tool. Guest shell is run_command. Prefer specialized tools over shell: grep not run_command grep; read_file not cat; apply_patch / search_replace not sed. Never use run_command to print thoughts.
- Forbidden replies: "I cannot execute", "run this yourself", "try:" plus a command fence. Call the tool instead.
- Independent reads may be issued together; the harness may serialize them (tool_parallelism defaults to 1).
- Do not paste huge files into the user reply cite paths and show only the slice that matters.
- Persist durable facts in MEMORY.md; older turns may be compacted.
@@ -304,7 +307,8 @@ This turn is shown in Discord. This block overrides the TTY plain-text rule abov
- Use [label](https://url) for links. Do not wrap the entire reply in one fence.
- The Discord message is ONLY your final answer. Do not prefix status, model name, "Loading", or a restatement of the prompt.
- Tool chatter and process steps belong in tools (progress.txt is automated), not the reply.
- Do not tell the user to run a command. Call tools yourself.
- Do not tell the user to run a command. Call tools yourself. If they say "run it yourself", that is an order to call run_command now — not to print curl/ip/bash for them.
- Forbidden: "I cannot execute commands directly", "I can help you use available tools", dumping \`\`\`bash fences for the operator to copy. Execute, then answer with the result.
- You may discord_send_message at any time on the open DM channel, including before the final answer.`
const BARE_AGENT_OPERATING_CONTRACT = `
@@ -417,10 +421,18 @@ function bareAgentFinalizeToolCalls(acc) {
for (const i of indices) {
const c = acc.get(i)
if (!c || !c.name) continue
const args = c.args || '{}'
const name =
typeof bareAgentCanonicalToolName === 'function'
? bareAgentCanonicalToolName(c.name)
: c.name
if (!name) continue
const args =
typeof bareAgentCoerceToolArgs === 'function'
? bareAgentCoerceToolArgs(name, c.args || '{}')
: c.args || '{}'
// Drop stream+final duplicates (same id, or same name+args).
const idKey = c.id ? 'id:' + c.id : ''
const naKey = 'na:' + c.name + '\0' + args
const naKey = 'na:' + name + '\0' + args
if ((idKey && seen.has(idKey)) || seen.has(naKey)) continue
if (idKey) seen.add(idKey)
seen.add(naKey)
@@ -428,7 +440,7 @@ function bareAgentFinalizeToolCalls(acc) {
id: c.id || 'call_' + i + '_' + String(Math.random()).slice(2, 10),
type: 'function',
function: {
name: c.name,
name: name,
arguments: args
}
})
@@ -437,9 +449,184 @@ function bareAgentFinalizeToolCalls(acc) {
}
/**
* Recover tool calls the provider streamed as plain text (Hermes / Qwen XML)
* instead of `delta_tool_calls`. Today's models often do this when the SDK
* drops tools or picks the wrong dialect.
* Map common leaked tool names (Grok/Claude `bash`, `shell`, …) onto the
* live registry. Used by text recovery and finalize.
* @param {string} name
*/
function bareAgentCanonicalToolName(name) {
const n = String(name || '').trim()
if (!n) return ''
const key = n.toLowerCase().replace(/[\s-]+/g, '_')
const aliases = {
bash: 'run_command',
shell: 'run_command',
sh: 'run_command',
zsh: 'run_command',
fish: 'run_command',
cli: 'run_command',
exec: 'run_command',
execute: 'run_command',
terminal: 'run_command',
run: 'run_command',
cmd: 'run_command',
command: 'run_command',
run_cmd: 'run_command',
run_shell: 'run_command',
execute_command: 'run_command',
execute_bash: 'run_command',
shell_command: 'run_command',
list_dir: 'list_directory',
glob: 'glob_files',
ripgrep: 'grep',
rg: 'grep'
}
return aliases[key] || n
}
/**
* Coerce leaked bash/run_command argument blobs into {command, cwd?}.
* @param {string} name
* @param {string} args
*/
function bareAgentCoerceToolArgs(name, args) {
let a = String(args == null ? '' : args).trim()
if (!a) a = '{}'
const canon = bareAgentCanonicalToolName(name)
if (canon !== 'run_command') {
if (a[0] !== '{' && a[0] !== '[') {
try {
JSON.parse(a)
} catch {
a = JSON.stringify({ value: a })
}
}
return a
}
/** @type {Record<string, unknown> | null} */
let obj = null
try {
const p = JSON.parse(a[0] === '{' || a[0] === '[' ? a : JSON.stringify(a))
if (p && typeof p === 'object' && !Array.isArray(p)) obj = p
else if (typeof p === 'string') obj = { command: p }
} catch {
obj = { command: a }
}
if (!obj) return JSON.stringify({ command: a })
const cmd =
obj.command || obj.cmd || obj.code || obj.script || obj.input || obj.value || obj.query
if (typeof cmd === 'string' && cmd.trim()) {
const out = { command: cmd.trim() }
if (typeof obj.cwd === 'string' && obj.cwd.trim()) out.cwd = obj.cwd.trim()
return JSON.stringify(out)
}
if (a[0] !== '{' && a[0] !== '[') return JSON.stringify({ command: a })
return a
}
/**
* True when a fenced block looks like a guest shell line, not prose or JSON.
* @param {string} text
*/
function bareAgentLooksLikeShellCommand(text) {
const s = String(text || '').trim()
if (!s || s.length > 4000) return false
if (/^[\s]*[{\[]/.test(s)) return false
const lines = s.split(/\r?\n/).filter(function (l) {
return l.trim() && !/^\s*#/.test(l)
})
if (!lines.length || lines.length > 16) return false
const first = lines[0].trim()
if (
/^(curl|wget|ip|ping|uname|ifconfig|hostname|cat|ls|pwd|whoami|id|df|ps|env|printenv|date|echo|head|tail|grep|sed|awk|chmod|mkdir|rm|cp|mv|git|systemctl|journalctl|procstat|ss|netstat|traceroute|nslookup|dig|host|which|realpath|stat|find|xargs|tar|unzip|python|node|npm)\b/i.test(
first
)
) {
return true
}
if (/^(\.\/|\/(bin|usr\/bin|sbin|home|tmp|opt)\/)/.test(first)) return true
if (lines.length === 1 && first.length < 240 && !/[.!?]$/.test(first)) {
const tok = first.split(/\s+/)[0] || ''
if (!/^[a-zA-Z0-9_./-]+$/.test(tok)) return false
if (
/^(the|this|that|for|and|or|if|when|please|you|i|we|it|to|a|an|try|here|command|example)$/i.test(
tok
)
) {
return false
}
return first.split(/\s+/).length <= 14
}
return false
}
/**
* Assistant dumped "run this yourself" instead of calling tools.
* @param {string} text
*/
function bareAgentLooksLikeInstructionDump(text) {
const s = String(text || '')
if (!s.trim()) return false
const low = s.toLowerCase()
if (
/i cannot execute/.test(low) ||
/i can't execute/.test(low) ||
/cannot execute commands/.test(low) ||
/can't execute commands/.test(low) ||
/i cannot run (commands|them|that|it|cli|the command)/.test(low) ||
/i can't run (commands|them|that|it|cli|the command)/.test(low) ||
/don't have (access|the ability) to (run|execute)/.test(low) ||
/do not have (access|the ability) to (run|execute)/.test(low) ||
/as an ai.{0,40}(cannot|can't)/.test(low) ||
/i can help you use available tools/.test(low) ||
/help you use available tools/.test(low) ||
/run the command yourself/.test(low) ||
/you (can|should|could) (run|execute|try)/.test(low) ||
/please (run|execute|try) (this|the|it)/.test(low) ||
/try this command/.test(low) ||
/for .{0,60} try:/.test(low) ||
/let me know if you need help with other commands/.test(low)
) {
return true
}
if (
/```(?:bash|sh|shell|zsh|console|terminal)\b/i.test(s) &&
/(try|run this|you can|example:|for .{0,20}, try)/i.test(low)
) {
return true
}
return false
}
/**
* Operator told the agent to stop instructing and execute.
* @param {string} text
*/
function bareAgentLooksLikeDoItYourself(text) {
const s = String(text || '').toLowerCase()
if (!s.trim()) return false
return (
/run .{0,48}yourself/.test(s) ||
/run (it|them|that|this) (yourself|for me)/.test(s) ||
/do(n't| not) (tell|instruct|show) me (how|to)/.test(s) ||
/just (do|run|execute) it/.test(s) ||
/execute .{0,48}yourself/.test(s) ||
/don'?t ask me to run/.test(s) ||
/stop (telling|instructing) me/.test(s)
)
}
/**
* Bounce a refusal/instruction dump back into the ReAct loop.
*/
function bareAgentRefusalNudge() {
return (
'[harness] You CAN execute. Full guest admin is already on. The shell tool is run_command with {"command":"..."}. There is no bash/shell/cli tool. Never say you cannot execute. Never print a command for the user. Call run_command now and keep going until you have the result.'
)
}
/**
* Recover tool calls the provider streamed as plain text (Hermes / Qwen XML,
* ```bash fences, Grok/Claude `bash` dumps) instead of `delta_tool_calls`.
* @param {string} text
* @returns {{ index: number, id: string, function: { name: string, arguments: string } }[]}
*/
@@ -449,23 +636,20 @@ function bareAgentExtractToolCallsFromText(text) {
/** @type {{ index: number, id: string, function: { name: string, arguments: string } }[]} */
const out = []
let idx = 0
/** @type {Set<string>} */
const seen = new Set()
/**
* @param {string} name
* @param {string} args
*/
function push(name, args) {
const n = String(name || '').trim()
const n = bareAgentCanonicalToolName(name)
if (!n) return
let a = String(args == null ? '' : args).trim()
if (!a) a = '{}'
else if (a[0] !== '{' && a[0] !== '[') {
try {
JSON.parse(a)
} catch {
a = JSON.stringify({ value: a })
}
}
const a = bareAgentCoerceToolArgs(n, args)
const key = n + '\0' + a
if (seen.has(key)) return
seen.add(key)
out.push({
index: idx,
id: 'text_call_' + idx,
@@ -508,6 +692,81 @@ function bareAgentExtractToolCallsFromText(text) {
}
}
const invoke = /<invoke\s+name=["']([^"']+)["']>([\s\S]*?)<\/invoke>/gi
while ((m = invoke.exec(src))) {
/** @type {Record<string, string>} */
const args = {}
const paramRe = /<parameter\s+name=["']([^"']+)["']>([\s\S]*?)<\/parameter>/gi
let pm
while ((pm = paramRe.exec(m[2] || ''))) {
args[String(pm[1] || '').trim()] = String(pm[2] || '').trim()
}
push(m[1], JSON.stringify(args))
}
const bracket = /\[(?:tool_call\s+)?([a-zA-Z0-9_]+)\s*\(([^)]*)\)\]/g
while ((m = bracket.exec(src))) {
const name = m[1]
const raw = String(m[2] || '').trim()
if (!raw) {
push(name, '{}')
continue
}
if (/^[{\[]/.test(raw)) {
push(name, raw)
continue
}
const kv = /^([a-zA-Z0-9_]+)\s*=\s*(["']?)([\s\S]*)\2$/.exec(raw)
if (kv) {
const rec = {}
rec[kv[1]] = kv[3]
push(name, JSON.stringify(rec))
continue
}
push(name, raw)
}
const dump = bareAgentLooksLikeInstructionDump(src)
const fence = /```([a-zA-Z0-9_-]*)[ \t]*\r?\n([\s\S]*?)```/g
const shellLang = /^(bash|sh|shell|zsh|fish|console|terminal|posix|busybox)$/i
while ((m = fence.exec(src))) {
const lang = String(m[1] || '').trim()
const body = String(m[2] || '').trim()
if (!body) continue
if (shellLang.test(lang) || ((dump || !lang) && bareAgentLooksLikeShellCommand(body))) {
push('run_command', JSON.stringify({ command: body }))
}
}
if (dump) {
const lines = src.split(/\r?\n/)
for (let i = 0; i < lines.length; i++) {
let t = String(lines[i] || '').trim()
t = t.replace(/^[-*]\s+/, '').replace(/^`+|`+$/g, '')
if (/^(bash|sh|shell|zsh|fish|console|terminal)$/i.test(t)) {
const nxt = String(lines[i + 1] || '')
.trim()
.replace(/^`+|`+$/g, '')
if (bareAgentLooksLikeShellCommand(nxt)) {
push('run_command', JSON.stringify({ command: nxt }))
i += 1
}
continue
}
if (bareAgentLooksLikeShellCommand(t)) {
push('run_command', JSON.stringify({ command: t }))
}
}
const inline = /`([^`\n]{1,240})`/g
let im
while ((im = inline.exec(src))) {
const body = String(im[1] || '').trim()
if (bareAgentLooksLikeShellCommand(body)) {
push('run_command', JSON.stringify({ command: body }))
}
}
}
return out
}
@@ -517,6 +776,10 @@ function bareAgentExtractToolCallsFromText(text) {
function bareAgentStripToolCallsFromText(text) {
return String(text || '')
.replace(/<tool_call>[\s\S]*?<\/tool_call>/gi, '')
.replace(/<function_calls>[\s\S]*?<\/function_calls>/gi, '')
.replace(/<invoke\s+name=["'][^"']+["']>[\s\S]*?<\/invoke>/gi, '')
.replace(/```(?:bash|sh|shell|zsh|fish|console|terminal|posix|busybox)[ \t]*\r?\n[\s\S]*?```/gi, '')
.replace(/\[(?:tool_call\s+)?(?:run_command|bash|shell|sh)\s*\([^)]*\)\]/gi, '')
.replace(/\n{3,}/g, '\n\n')
.trim()
}
@@ -1660,6 +1923,17 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
userTask +=
'\n\n[harness] Answer from AVAILABLE TOOLS / list_agent_tools. List every callable tool name. Do not list skills or /bin names as tools.'
}
if (
typeof bareAgentLooksLikeDoItYourself === 'function' &&
bareAgentLooksLikeDoItYourself(userTask)
) {
userTask +=
'\n\n[harness] The operator told you to execute. Call run_command / write_file / the rest of AVAILABLE TOOLS yourself. Do not print commands for them. There is no bash tool — use run_command.'
}
if (discordReply) {
userTask +=
'\n\n[harness] This is Discord. Execute with tools. The operator will not run your commands. Call run_command yourself. Never dump curl/ip/bash for them to copy. Never say you cannot execute.'
}
if (config.autonomous_active) {
userTask =
'AUTONOMOUS RUN. Execute until the goal is done. Do not stop after a plan — use tools, then call task_complete.\n' +
@@ -1815,6 +2089,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
const maxIterBase = Number(configRef.current.max_iterations) || 64
let iter = 0
let instructionDumpNudges = 0
for (;;) {
if (masterAbort.signal.aborted) {
@@ -2057,15 +2332,22 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
recoveredFromText = true
if (typeof bareAgentExtractToolCallsFromText !== 'function') return
const extracted = bareAgentExtractToolCallsFromText(assistantContent)
if (!toolAcc.size && extracted.length) {
for (const tc of extracted) bareAgentMergeToolCallDelta(toolAcc, tc)
appendProgress(
'recovered_tool_calls_from_text n=' + String(extracted.length)
)
}
if (extracted.length) {
if (!toolAcc.size) {
for (const tc of extracted) bareAgentMergeToolCallDelta(toolAcc, tc)
appendProgress(
'recovered_tool_calls_from_text n=' + String(extracted.length)
)
}
assistantContent = bareAgentStripToolCallsFromText(assistantContent)
}
if (
typeof bareAgentLooksLikeInstructionDump === 'function' &&
bareAgentLooksLikeInstructionDump(assistantContent) &&
(toolAcc.size || extracted.length)
) {
assistantContent = ''
}
}
const hideThinkEnv = String(envBag.BARE_OS_AGENT_HIDE_THINK || '')
.trim()
@@ -2504,6 +2786,12 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
for (const tc of extracted) bareAgentMergeToolCallDelta(toolAcc, tc)
toolCallsArr = bareAgentFinalizeToolCalls(toolAcc)
assistantContent = bareAgentStripToolCallsFromText(assistantContent)
if (
typeof bareAgentLooksLikeInstructionDump === 'function' &&
bareAgentLooksLikeInstructionDump(assistantContent)
) {
assistantContent = ''
}
appendProgress(
'recovered_tool_calls_from_text n=' + String(toolCallsArr.length)
)
@@ -2516,6 +2804,13 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
providerNow
)
}
if (
hasTools &&
typeof bareAgentLooksLikeInstructionDump === 'function' &&
bareAgentLooksLikeInstructionDump(assistantContent)
) {
assistantContent = ''
}
/** @type {Record<string, unknown>} */
const assistantMsg = {
@@ -2548,6 +2843,25 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
}
if (!hasTools) {
if (
instructionDumpNudges < 2 &&
typeof bareAgentLooksLikeInstructionDump === 'function' &&
bareAgentLooksLikeInstructionDump(assistantContent)
) {
instructionDumpNudges += 1
messages.push({
role: 'user',
content:
typeof bareAgentRefusalNudge === 'function'
? bareAgentRefusalNudge()
: '[harness] Call run_command now. Do not tell the user to run it.'
})
appendProgress(
'instruction_dump_nudge n=' + String(instructionDumpNudges)
)
await bareAgentSaveHistory(ctx, paths.history, messages)
continue
}
if (
bareAgentAutonomousShouldContinue(configRef.current, {
completed: completed,