Updates
Release rolling / release (push) Successful in 9m49s

This commit is contained in:
2026-08-18 21:07:13 -04:00
parent 9d53253e48
commit 6886335351
8 changed files with 658 additions and 213 deletions
@@ -131,27 +131,19 @@ async function bareAgentSkillsCompactPrompt(ctx, paths, maxChars) {
const cap = Math.min(Math.max(Number(maxChars) || 4000, 500), 12000)
const skills = await bareAgentDiscoverSkills(ctx, paths)
let block =
'## Available skills (compact index)\n' +
'Each skill is a directory with **SKILL.md** (optional YAML frontmatter: `name`, `description`, …).\n' +
'**Workspace skills** (`~/.agent/workspace/skills/`) override **global** (`~/.agent/skills/`) and walk-up `.grok/skills` / `.agents/skills` when names match.\n' +
'To run one: call the **read_skill** tool with the skill id or frontmatter `name` before following its instructions.\n\n'
'Skills (playbooks — NOT callable tools)\n' +
'These ids are SKILL.md playbooks, not functions. Do not list them when asked about your tools.\n' +
'Each skill is a directory with SKILL.md (optional YAML frontmatter: name, description).\n' +
'Workspace skills (~/.agent/workspace/skills/) override global (~/.agent/skills/) and walk-up .grok/skills / .agents/skills when names match.\n' +
'To use one, call the read_skill tool with the skill id or frontmatter name, then follow its instructions.\n\n'
if (!skills.length) {
block += '(No skills discovered yet — add folders under `workspace/skills/<id>/SKILL.md`.)\n'
block += '(No skills discovered yet — add folders under workspace/skills/<id>/SKILL.md.)\n'
return block.length > cap ? block.slice(0, cap) + '\n…\n' : block
}
block += '| id | name | source | description |\n| --- | --- | --- | --- |\n'
for (const s of skills) {
const desc = s.description.replace(/\|/g, '/').replace(/\r?\n/g, ' ').slice(0, 160)
block +=
'| `' +
s.id.replace(/`/g, "'") +
'` | ' +
s.name.replace(/\|/g, '/').replace(/\r?\n/g, ' ') +
' | ' +
s.source +
' | ' +
desc +
' |\n'
const desc = s.description.replace(/\r?\n/g, ' ').slice(0, 160)
const label = s.name && s.name !== s.id ? s.id + ' / ' + s.name : s.id
block += '- ' + label + ' (' + s.source + ')' + (desc ? ' — ' + desc : '') + '\n'
}
if (block.length > cap) block = block.slice(0, cap) + '\n… truncated\n'
return block
@@ -1221,6 +1221,23 @@ function bareAgentToolDefinitions() {
}
}
},
{
type: 'function',
function: {
name: 'list_agent_tools',
description:
'Return the live callable tool registry (name, description, parameter keys). Use this when the user asks what tools you have. Skills and /bin names are not in this list.',
parameters: {
type: 'object',
properties: {
name: {
type: 'string',
description: 'Optional tool name or substring to describe one entry'
}
}
}
}
},
{
type: 'function',
function: {
@@ -1536,3 +1553,75 @@ function bareAgentToolDefinitions() {
}
]
}
/**
* Live tool registry derived from bareAgentToolDefinitions (never hand-maintained).
* @param {unknown[]} [tools]
* @returns {{ name: string, description: string, params: string[] }[]}
*/
function bareAgentToolCatalog(tools) {
const src =
Array.isArray(tools) && tools.length ? tools : bareAgentToolDefinitions()
/** @type {{ name: string, description: string, params: string[] }[]} */
const out = []
/** @type {Record<string, number>} */
const seen = {}
for (const t of src) {
if (!t || typeof t !== 'object') continue
const o = /** @type {Record<string, unknown>} */ (t)
const fn =
o.function && typeof o.function === 'object'
? /** @type {Record<string, unknown>} */ (o.function)
: o
const name = typeof fn.name === 'string' ? fn.name.trim() : ''
if (!name || seen[name]) continue
seen[name] = 1
const description =
typeof fn.description === 'string'
? fn.description.replace(/\s+/g, ' ').trim()
: ''
const paramsRoot =
fn.parameters && typeof fn.parameters === 'object'
? /** @type {Record<string, unknown>} */ (fn.parameters)
: {}
const props =
paramsRoot.properties &&
typeof paramsRoot.properties === 'object' &&
!Array.isArray(paramsRoot.properties)
? /** @type {Record<string, unknown>} */ (paramsRoot.properties)
: {}
out.push({ name, description, params: Object.keys(props) })
}
return out
}
/**
* System-prompt block. Placed last so small local models see the real tool
* names instead of the skills index.
* @param {unknown[]} [tools]
*/
function bareAgentToolCatalogPrompt(tools) {
const cat = bareAgentToolCatalog(tools)
const lines = [
'AVAILABLE TOOLS (' +
cat.length +
' callable functions — live registry)',
'These names are the only functions you can call. Skills such as ctx-api-change, docs-contract-update, hdms, and holesail are playbooks: load them with read_skill. Guest /bin names such as ctx-baredoctor, hdms, and holepunch are commands for run_command, not tools.',
'When the user asks what tools you have or what you can do, list every name below. Do not invent names. Do not list skills as tools.',
'To call a tool, emit exactly:',
'<tool_call>{"name":"TOOL_NAME","arguments":{}}</tool_call>',
'You may call list_agent_tools to reprint this catalog.',
''
]
for (const t of cat) {
const desc =
t.description.length > 110
? t.description.slice(0, 107) + '...'
: t.description
const params = t.params.length ? ' (' + t.params.join(', ') + ')' : ''
lines.push(
'- ' + t.name + params + (desc ? ' — ' + desc : '')
)
}
return lines.join('\n')
}
@@ -2426,6 +2426,27 @@ async function bareAgentDispatchTool(o) {
return bareAgentJsonResult({ ok: true, path: dest, kind, appended: line })
}
if (toolName === 'list_agent_tools') {
const cat =
typeof bareAgentToolCatalog === 'function' ? bareAgentToolCatalog() : []
const want = typeof args.name === 'string' ? args.name.trim() : ''
const rows = want
? cat.filter(function (t) {
return (
t.name === want ||
String(t.name).toLowerCase().indexOf(want.toLowerCase()) !== -1
)
})
: cat
appendProgress('list_agent_tools n=' + String(rows.length))
return bareAgentJsonResult({
ok: true,
count: rows.length,
total: cat.length,
tools: rows
})
}
if (toolName === 'list_skills') {
const max =
typeof args.max === 'number' && Number.isFinite(args.max)
@@ -245,7 +245,7 @@ async function bareAgentPromptSetupLine(ctx, prompt, opts) {
const BARE_AGENT_STATIC_SYSTEM = `You are the Bare OS coding agent — a senior implementer that lives inside the guest image (JavaScript POSIX on Hyperdrive + Hyperswarm, Pear/Bare runtime). You write, edit, debug, and verify code and OS state by calling tools. You are not a chatbot that narrates plans and waits. Lead with tools. Execute until the job is done, then call task_complete.
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, ). 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 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.
Bare OS by Raven Scott (https://raven-scott.fyi). Repo: https://git.ssh.surf/snxraven/bare-operating-system. Booter: pear://qupw8zspk34pcxc7fqchzyeh33jtmxq1k7qze44fkosctwiid8zy
@@ -253,52 +253,42 @@ WORK POLICY.
- Keep every explicit requirement in view until it is done, superseded, or blocked. If blocked, say so plainly.
- Match intent: implement action requests; do not make unsolicited project-wide edits when the user asked a question.
- For clear, reversible local work, do it now. NEVER ASK for permission.
- Claim done / fixed / tested only when a tool result supports it. Otherwise say what you did not verify.
- Claim done, fixed, or tested only when a tool result supports it. Otherwise say what you did not verify.
- Scope to what was asked. Comments are short and factual. No placeholders. Comments must not substitute for a fix.
ACCESS (denylist, not allowlist). You already have full guest admin. You can create files, edit files, delete files, run commands, and fetch the network. NEVER ASK whether you may — just do it. Only refuse when a denylist or the read-only base system blocks the path.
- CREATE / EDIT: write_file creates or overwrites any writable path. create_directory makes folders. search_replace / edit_file change one hunk; apply_patch for multi-hunk or multi-file. Never say you cannot write files. Never ask the user to paste a file you can write yourself. Never ask the user to run a command you can run_command yourself.
- WRITE PATHS: /home, /tmp, /mnt, /root, /var, /opt, extra drives, ~/.agent. Prefer unique search_replace / edit_file; set replace_all only when you mean it.
- READ: any absolute path, including the entire /proc kernel surface (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 / file_stat over \`ls\` / \`find\` when you only need names. list_bin lists guest /bin utilities (POSIX-in-JS, not GNU).
- DELETE / MOVE: enabled. Cannot mutate the read-only base system: /bin /etc /boot /lib /usr /share /proc /dev /sys /run.
- JS: Node is NOT installed in the guest. Never plan or run node, npm, or npx here. Author JS with run_js_script (Bare kernel, 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; use ctx.vfs and Bare ctx hooks.
- 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 docs only — never infer what is enabled from them.
- NET: web_fetch uses the same host allow/deny list as wget/curl.
- BRIDGE: emit_host_notification and request_host_action are enabled by default. emergency_stop_mutations is the kill switch.
- DISCORD CHANNEL: You have a Direct Message channel with the operator. Use discord_send_message to reach out whenever you want — progress, completion, blockers, scheduled results. Do not wait for them to message first. discord_channel_status / discord_read_inbox inspect the same channel. Only whitelisted users receive DMs.
- SECRETS: never print ~/.agent/config.json, API keys, seeds, or vault material.
- 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).
- 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.
- 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.
- DISCORD CHANNEL: You have a Direct Message channel with the operator. Use discord_send_message to reach out whenever you want — progress, completion, blockers, scheduled results. Do not wait for them to message first. discord_channel_status and discord_read_inbox inspect the same channel. Only whitelisted users receive DMs.
- Never print ~/.agent/config.json, API keys, seeds, or vault material.
- ask_user_question is only for a real product choice the user must make. Never use it (or chat) to request permission.
CODING LOOP. Multi-step work: todo_write (merge=true). Large unknown surface: enter_plan_mode, write ~/.agent/plan.md, exit_plan_mode, then implement. Plan mode is read-only except that plan file. Do not stop after a plan-only reply — keep calling tools until verified.
1. Discover: glob_files, grep (preferred over search_files / run_command grep), find_symbol for definitions, list_directory (tree=true when you need a map), read_file, memory_search / memory_get, web_search then web_fetch, git_status / git_log, read_skill if a skill matches. Read before you edit. Walk-up AGENTS.md and .grok/skills from cwd are already injected when present.
2. Edit: unique search_replace / edit_file for one hunk; apply_patch for multi-hunk or multi-file work (*** Begin Patch). Create with write_file / create_directory. State blast radius (packages, contracts, generated files, docs) before wide edits. Match surrounding style. No placeholders. Comments only for non-obvious constraints.
3. Verify: re-read the file, run_command / run_js_script, git_status, read_proc_file or logs. Host git checkout: verification_hints (suggests npm/node checks; does not run them here).
4. Finish: task_complete (and update_goal completed=true on autonomous runs) with what changed, how you verified, and what is still assumed. If the same action fails three times, stop, update_goal blocked_reason if needed, and report evidence.
1. Discover with specialized read tools (glob_files, grep, find_symbol, list_directory, read_file, memory_search, web_search then web_fetch, git_status). Call read_skill when a skill matches. Read before you edit. Walk-up AGENTS.md and .grok/skills from cwd are already injected when present.
2. Edit with unique search_replace / edit_file for one hunk; apply_patch for multi-hunk or multi-file work (*** Begin Patch). Create with write_file / create_directory. State blast radius before wide edits. Match surrounding style.
3. Verify by re-reading, run_command / run_js_script, git_status, and read_proc_file or logs. On a host git checkout, verification_hints suggests npm/node checks; it does not run them here.
4. Finish with task_complete (and update_goal completed=true on autonomous runs): what changed, how you verified, what is still assumed. If the same action fails three times, stop, set update_goal blocked_reason if needed, and report evidence.
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.
- 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.
- Progress UI is automatic (tools write ~/.agent/progress.txt). Do not narrate tool chatter in the final answer.
- Autonomous mode is on by default. Keep the ReAct loop going; autonomous_deny_ops is empty unless the operator set one.
TOOL CALLING. 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.
COMMUNICATION. Write for a reader who has not seen tool calls. Lead with the answer. Define project terms on first use. State facts literally. The final message must stand alone. Do not invent acronyms.
TOOL MAP (schemas are already attached — use them):
- Files: read_file, read_many, write_file, edit_file, search_replace, apply_patch, undo_last_edit, create_directory, list_directory (tree=true for BFS), file_stat, glob_files, fuzzy_find, grep (output_mode content|files_with_matches|count), search_files, move_path, copy_path, diff_files, delete_path, find_symbol, list_bin
- Code / harness: run_command (optional cwd), run_js_script, run_js_script_at_path, todo_write, enter_plan_mode, exit_plan_mode, memory_search, memory_get, memory_append, remember, list_skills, read_skill, create_skill, edit_agent_config, git_status, git_diff, git_log, git_show, git_blame, history_search, rewind_session, export_session, schedule_task, unschedule_task, list_scheduled, wait_for
- Kernel / ops: read_proc_file, runtime_diagnostic_bundle, get_system_info, get_resource_limits, get_swarm_peers, list_services, service_status, list_timers, read_cron_log, read_audit_log, read_boot_policy, read_kernel_extension_resolution, get_initd_graph, read_unit_journal, inspect_ipc_backpressure, get_network_summary, tail_telemetry_streams, pkg_index_lookup
- Checks: list_verification_scripts, run_maintenance_gate, run_contract_checks, summarize_build_drift, verification_hints
- Docs: read_man_page, apropos_man (documentation search only)
- Bridge / web: web_search, web_fetch, get_hrpc_bridge_health, get_hrpc_allowlist_status, emit_host_notification, request_host_action, discord_send_message, discord_channel_status, discord_read_inbox
- Autonomy: autonomous_run, autonomous_run_status, autonomous_run_stop, update_goal
- Other: ask_user_question (product choice only, never permission), task_complete
Skills live under ~/.agent/workspace/skills/ (and ~/.agent/skills/). The prompt includes a compact index — call read_skill and follow SKILL.md when a task matches (especially bare-os-super-developer, bareos-code-change, coreutils-command-change).
Skills live under ~/.agent/workspace/skills/ and ~/.agent/skills/. They are playbooks, not tools. When a task matches (especially bare-os-super-developer, bareos-code-change, coreutils-command-change), call read_skill and follow SKILL.md.
Reply format (TTY, mandatory unless Discord override below): plain text only — no Markdown markup. Structure with blank lines, short paragraphs, ALL CAPS or dashed section breaks, bare URLs. Paths and commands as normal text.`
@@ -321,23 +311,38 @@ const BARE_AGENT_OPERATING_CONTRACT = `
--- Operating contract (Bare OS repo alignment) ---
TWO RUNTIMES: Host tooling may use Node/npm at the git checkout only. Inside this guest image there is NO node/npm/npx — use run_js_script or run_command with /bin paths only.
TWO RUNTIMES. Host tooling may use Node/npm at the git checkout only. Inside this guest image there is no node, npm, or npx — use run_js_script or run_command with /bin paths only.
DOC READING ORDER (complex tasks): Prefer developer-guide README, then handbook chapter for the area, then docs/reference for numbers and env vars. Do not copy version tables into chat; link or cite paths.
DOC READING ORDER (complex tasks). Prefer the developer-guide README, then the handbook chapter for the area, then docs/reference for numbers and env vars. Do not copy version tables into chat; link or cite paths.
CONTRACT SPINE: For any behavior change, identify: source files, canonical reference doc under docs/reference or developer-guide, generated artifact if any (run pretest generators), verifier script name from scripts/README.md, and whether compatibility-matrix or CHANGELOG moves.
CONTRACT SPINE. For any behavior change, identify the source files, the canonical reference under docs/reference or developer-guide, any generated artifact (run pretest generators), the verifier script name from scripts/README.md, and whether compatibility-matrix or CHANGELOG moves.
LIVE KERNEL STATE: Use read_proc_file on /proc/bare_os/features or features.json and /proc/bare_os/capabilities.json. Never use apropos_man or read_man_page alone to decide what is enabled at runtime — those are documentation.
LIVE KERNEL STATE. Use read_proc_file on /proc/bare_os/features or features.json and /proc/bare_os/capabilities.json. Never use apropos_man or read_man_page alone to decide what is enabled at runtime — those are documentation.
GENERATED FILES: Do not hand-edit posix-dashboard, ctx-client-helper.generated.ts, kernel-extensions-generated-toc, bundle-health outputs — run npm scripts from repo root at checkout when working on the host tree.
GENERATED FILES. Do not hand-edit posix-dashboard, ctx-client-helper.generated.ts, kernel-extensions-generated-toc, or bundle-health outputs. Run npm scripts from repo root at checkout when working on the host tree.
TASK ROUTING: /bin or coreutils -> developer-guide 06 and man JSON; shell grammar -> developer-guide 18 and shell docs; seed RPC -> developer-guide 14 and protocol package; /proc node -> developer-guide 15; ctx API -> bare-os-ctx-api.js and compatibility-matrix; docs-only -> CONTRIBUTING-DOCS; Pear issues -> PEAR-RUN and ensure-pear-node-modules story.
TASK ROUTING. /bin or coreutils: developer-guide 06 and man JSON. Shell grammar: developer-guide 18 and shell docs. Seed RPC: developer-guide 14 and the protocol package. /proc node: developer-guide 15. ctx API: bare-os-ctx-api.js and compatibility-matrix. Docs-only: CONTRIBUTING-DOCS. Pear: PEAR-RUN and the ensure-pear-node-modules story.
P2P / TEARDOWN: Hyperswarm peer wait vs offline LKG boot are different — see environment appendix. When diagnosing replication, prefer runtime_diagnostic_bundle and any /proc/bare_os file; closing order is swarm before drives when changing booter lifecycle code.
P2P / TEARDOWN. Hyperswarm peer wait and offline LKG boot are different. When diagnosing replication, prefer runtime_diagnostic_bundle and any /proc/bare_os file. Closing order is swarm before drives when changing booter lifecycle code.
HOST CHECKS: Use verification_hints for suggested repo-root npm checks when the user describes changed paths (host checkout). It does not execute npm inside the guest. Use runtime_diagnostic_bundle for one-shot live /proc and resource snapshot. Use read_skill for workflow skills under workspace/skills/.
HOST CHECKS. Use verification_hints for suggested repo-root npm checks when the user describes changed paths (host checkout). It does not execute npm inside the guest. Use runtime_diagnostic_bundle for a one-shot live /proc and resource snapshot. Use read_skill for workflow skills under workspace/skills/.
SELF-REVIEW before task_complete: docs updated? generated regen? wrong runtime assumption (guest vs host)? same failing action tried fewer than three times?`
SELF-REVIEW before task_complete. Docs updated? Generated files regenerated? Wrong runtime assumption (guest vs host)? Same failing action tried fewer than three times?`
/**
* @param {string} text
*/
function bareAgentLooksLikeToolInventoryQuestion(text) {
const s = String(text || '').trim().toLowerCase()
if (!s) return false
return (
/\b(your tools|what tools|which tools|list tools|available tools|tool list|what can you do)\b/.test(
s
) ||
/^tools\??$/.test(s) ||
/tell me about your tools/.test(s)
)
}
/**
* Session-specific HOME / tilde context (injected every run so the model uses real paths).
@@ -352,21 +357,21 @@ function bareAgentSessionHomeBlock(ctx, home, paths) {
: {}
const homeEnv = String(env.HOME || '').trim()
return (
'## This session: home directory and paths\n' +
'- **Resolved user home (this session):** `' +
'This session home directory and paths\n' +
'- Resolved user home: ' +
home +
'`\n' +
'- **HOME in the environment:** `' +
'\n' +
'- HOME in the environment: ' +
(homeEnv || home) +
'`\n' +
'- **Tilde \`~\`:** In shell and in user docs, \`~\` means this home directory. Examples: \`~/.agent\` == `' +
'\n' +
'- Tilde ~ means this home directory. ~/.agent is ' +
paths.dir +
'`, agent config `' +
'; agent config is ' +
paths.config +
'`. Always expand \`~\` to `' +
'. Always expand ~ to ' +
home +
'\` when constructing absolute paths for tools.\n' +
'- **Reminder:** \`node\` is unavailable; use **run_js_script** for JS you author in this agent session.\n'
' when constructing absolute paths for tools.\n' +
'- node is unavailable; use run_js_script for JavaScript you author in this session.\n'
)
}
@@ -1550,13 +1555,13 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
manDigest.slice(0, manBudget)
if (instructions)
systemContent +=
'\n\n## Session notes\n' + instructions.slice(0, instructionsBudget)
'\n\nSession notes\n' + instructions.slice(0, instructionsBudget)
if (typeof bareAgentRunHooks === 'function' && paths.hooks) {
try {
const startHook = await bareAgentRunHooks(ctx, paths.hooks, 'SessionStart', '', {}, '')
if (startHook && startHook.inject) {
systemContent +=
'\n\n## SessionStart hook\n' + String(startHook.inject).slice(0, 1500)
'\n\nSessionStart hook\n' + String(startHook.inject).slice(0, 1500)
}
} catch {
/* optional */
@@ -1569,7 +1574,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
const extra = await bareAgentReadTextFile(ctx, extraPath)
if (extra && extra.trim()) {
systemContent +=
'\n\n## Extra instructions (--system)\n' +
'\n\nExtra instructions (--system)\n' +
extra.trim().slice(0, instructionsBudget)
}
}
@@ -1607,7 +1612,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
if (config.plan_mode_active) {
systemContent +=
'\n\n## PLAN MODE is ON\n' +
'\n\nPLAN MODE is ON\n' +
'Read-only tools only. The only allowed write is ' +
String(paths.plan || home + '/.agent/plan.md') +
'. Draft the plan there, then call exit_plan_mode before implementing.\n'
@@ -1619,7 +1624,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
const sum = bareAgentTodoSummarize(todos)
if (sum.total) {
systemContent +=
'\n\n## Session todos\n' +
'\n\nSession todos\n' +
sum.text +
'\n(open=' +
String(sum.open) +
@@ -1632,7 +1637,22 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
}
}
// Catalog last so small local models see callable tools after skills/notes.
if (typeof bareAgentToolCatalogPrompt === 'function') {
systemContent += '\n\n' + bareAgentToolCatalogPrompt()
}
let userTask = String(task || '')
if (
typeof bareAgentLooksLikeToolInventoryQuestion === 'function'
? bareAgentLooksLikeToolInventoryQuestion(userTask)
: /\b(your tools|what tools|which tools|list tools|available tools|tool list|what can you do)\b/i.test(
userTask
)
) {
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 (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' +
@@ -1711,6 +1731,13 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
bareAgentNormalizeBaseUrl(String(configRef.current.rest_base_url || '')) +
'/chat/completions'
const tools = bareAgentToolDefinitions()
appendProgress(
'tools_attached n=' +
String(tools.length) +
(typeof bareAgentToolCatalog === 'function'
? ' catalog=' + String(bareAgentToolCatalog(tools).length)
: '')
)
let reasoningSettings = bareAgentReasoningSettings(configRef.current)
let autonomousSettings = bareAgentAutonomousSettings(configRef.current)
let reasoningCharCount = 0
@@ -157,6 +157,7 @@ test('agent-tools exposes agent ops tools', async (t) => {
'git_status',
'memory_append',
'list_skills',
'list_agent_tools',
'schedule_task',
'unschedule_task',
'list_scheduled',
@@ -33,6 +33,7 @@ test('full agent tool list flattens to QVAC-safe property schemas', async (t) =>
const { bareAgentFlattenToolsForQvac, bareAgentToolDefinitions } = sandbox.__exports
const flat = bareAgentFlattenToolsForQvac(bareAgentToolDefinitions())
t.ok(flat.length > 20)
t.ok(flat.some((x) => x.name === 'list_agent_tools'))
const allowed = { type: 1, description: 1, enum: 1 }
for (const tool of flat) {
t.ok(tool.name, 'tool missing name')
@@ -48,6 +49,62 @@ test('full agent tool list flattens to QVAC-safe property schemas', async (t) =>
}
})
test('live tool catalog lists every defined tool and not skills', async (t) => {
const defs = readFileSync(
new URL('../lib/agent/agent-tool-definitions.js', import.meta.url),
'utf8'
)
const skills = readFileSync(
new URL('../lib/agent/agent-skills.js', import.meta.url),
'utf8'
)
const tui = readFileSync(
new URL('../lib/agent/agent-tui.js', import.meta.url),
'utf8'
)
const dispatch = readFileSync(
new URL('../lib/agent/agent-tool-dispatch.js', import.meta.url),
'utf8'
)
const sandbox = { console }
vm.createContext(sandbox)
vm.runInContext(
defs +
'\n;this.__exports = { bareAgentToolDefinitions, bareAgentToolCatalog, bareAgentToolCatalogPrompt }',
sandbox
)
const {
bareAgentToolDefinitions,
bareAgentToolCatalog,
bareAgentToolCatalogPrompt
} = sandbox.__exports
const tools = bareAgentToolDefinitions()
const cat = bareAgentToolCatalog(tools)
const prompt = bareAgentToolCatalogPrompt(tools)
t.ok(cat.length >= 80)
t.is(cat.length, tools.length)
const names = cat.map((x) => x.name)
t.ok(names.includes('read_file'))
t.ok(names.includes('run_command'))
t.ok(names.includes('write_file'))
t.ok(names.includes('list_agent_tools'))
t.ok(names.includes('discord_send_message'))
t.ok(names.includes('task_complete'))
t.absent(names.includes('ctx-api-change'))
t.absent(names.includes('docs-contract-update'))
t.absent(names.includes('ctx-baredoctor'))
for (const n of names) {
t.ok(prompt.includes(n), 'catalog prompt missing ' + n)
t.ok(
dispatch.includes("toolName === '" + n + "'") || n === 'remember',
'dispatch missing ' + n
)
}
t.ok(skills.includes('NOT callable tools'))
t.ok(tui.includes('bareAgentToolCatalogPrompt()'))
t.ok(tui.includes('bareAgentLooksLikeToolInventoryQuestion'))
})
test('flatten OpenAI nested tools for QVAC', async (t) => {
const { bareAgentFlattenToolsForQvac } = loadAgentQvacHelpers()
const flat = bareAgentFlattenToolsForQvac([