/** Agent ReAct session: stream, tools, SIGINT, repl suspend (preamble for /bin/agent). */ /** * @param {Record} ctx * @param {string} s */ function bareAgentLog(ctx, s) { try { ctx.console.log(s) } catch { /* ignore */ } } /** * @param {Record} ctx * @param {string} s */ function bareAgentErr(ctx, s) { try { ctx.console.error(s) } catch { /* ignore */ } } /** * SSH PTYs expect CRLF for line breaks (same as `bare-openssh-pty-console.js` / `man` via console). * @param {Record | undefined} ctx * @param {string} s */ function bareAgentNormalizeStreamNewlines(ctx, s) { if (ctx && ctx.bareOsPtyStdoutCrlf) return String(s).replace(/\r?\n/g, '\r\n') return String(s) } /** * @param {Record | undefined} ctx * @param {import('stream').Writable | undefined} out * @param {string} s */ function bareAgentWriteOut(ctx, out, s) { const text = bareAgentNormalizeStreamNewlines(ctx, s) if (out && typeof out.write === 'function') { try { out.write(text) } catch { /* ignore */ } } else { try { if (typeof bareOsEmitRaw === 'function') { bareOsEmitRaw(ctx, text) } else if (typeof process !== 'undefined' && process.stdout?.write) { process.stdout.write(text) } } catch { /* ignore */ } } } /** * True when we can run the plain-text setup wizard on stdin/stdout (no REPL readline). * @param {Record} ctx */ function bareAgentCanPlainSetup(ctx) { const stdin = /** @type {import('stream').Readable | undefined} */ ( ctx.replStdin || ctx.stdin ) const stdout = /** @type {import('stream').Writable | undefined} */ ( ctx.replStdout || ctx.stdout ) const tty = /** @type {{ isTTY?: boolean }} */ (stdin) return Boolean( stdin && typeof stdin.on === 'function' && tty.isTTY && stdout && typeof stdout.write === 'function' ) } /** * Read one line from a Readable stream (kernel echoes typed chars on cooked TTY). * @param {import('stream').Readable} stdin * @returns {Promise} */ function bareAgentReadStreamLineOnce(stdin) { return new Promise((resolve) => { let acc = '' /** @param {string | Uint8Array | Buffer} chunk */ function onData(chunk) { let s = '' if (typeof chunk === 'string') s = chunk else if (chunk instanceof Uint8Array) s = new TextDecoder().decode(chunk) else if ( typeof Buffer !== 'undefined' && typeof Buffer.isBuffer === 'function' && Buffer.isBuffer(chunk) ) s = chunk.toString('utf8') else s = String(chunk) acc += s const n = acc.indexOf('\n') if (n >= 0) { cleanup() resolve(acc.slice(0, n).replace(/\r$/, '')) } } function onEnd() { cleanup() resolve(acc.replace(/\r$/, '')) } function cleanup() { stdin.removeListener('data', onData) stdin.removeListener('end', onEnd) stdin.removeListener('error', onEnd) } stdin.on('data', onData) stdin.once('end', onEnd) stdin.once('error', onEnd) if (typeof stdin.resume === 'function') stdin.resume() }) } /** * Read one secret line (masked as *) when raw mode is available. * Falls back to normal line read when raw mode is unavailable. * @param {Record} ctx * @param {import('stream').Readable} stdin * @param {import('stream').Writable | undefined} stdout * @returns {Promise} */ function bareAgentReadMaskedLineOnce(ctx, stdin, stdout) { const ttyIn = /** @type {{ setRawMode?: (v: boolean) => void, isTTY?: boolean }} */ ( stdin ) if (!ttyIn || typeof ttyIn.setRawMode !== 'function' || !ttyIn.isTTY) { return bareAgentReadStreamLineOnce(stdin) } return new Promise((resolve, reject) => { /** @type {string[]} */ const chars = [] let done = false /** @param {string | Uint8Array | Buffer} chunk */ function onData(chunk) { if (done) return let s = '' if (typeof chunk === 'string') s = chunk else if (chunk instanceof Uint8Array) s = new TextDecoder().decode(chunk) else if ( typeof Buffer !== 'undefined' && typeof Buffer.isBuffer === 'function' && Buffer.isBuffer(chunk) ) { s = chunk.toString('utf8') } else s = String(chunk) for (const ch of s) { const code = ch.charCodeAt(0) if (ch === '\r' || ch === '\n') { finish(true) return } if (ch === '\u0003') { finish(false, new Error('interrupted')) return } if (ch === '\u007f' || ch === '\b') { if (chars.length) { chars.pop() bareAgentWriteOut(ctx, stdout, '\b \b') } continue } if (code >= 32 && code !== 127) { chars.push(ch) bareAgentWriteOut(ctx, stdout, '*') } } } /** @param {boolean} ok @param {Error} [err] */ function finish(ok, err) { if (done) return done = true cleanup() if (ok) resolve(chars.join('')) else reject(err || new Error('masked_input_failed')) } function cleanup() { stdin.removeListener('data', onData) stdin.removeListener('end', onEnd) stdin.removeListener('error', onErr) try { ttyIn.setRawMode(false) } catch { /* ignore */ } bareAgentWriteOut(ctx, stdout, '\n') } function onEnd() { finish(true) } /** @param {unknown} e */ function onErr(e) { const msg = e && typeof e === 'object' && 'message' in e ? String(e.message) : String(e) finish(false, new Error(msg)) } try { ttyIn.setRawMode(true) } catch { return resolve('') } stdin.on('data', onData) stdin.once('end', onEnd) stdin.once('error', onErr) if (typeof stdin.resume === 'function') stdin.resume() }) } /** * Write a prompt and read one line using raw streams (not ctx.readLine / TUI stack). * @param {Record} ctx * @param {string} prompt * @param {{ mask?: boolean }} [opts] */ async function bareAgentPromptSetupLine(ctx, prompt, opts) { const stdin = /** @type {import('stream').Readable | undefined} */ ( ctx.replStdin || ctx.stdin ) const stdout = /** @type {import('stream').Writable | undefined} */ ( ctx.replStdout || ctx.stdout ) if (!stdin || typeof stdin.on !== 'function') { throw new Error('setup: stdin stream unavailable') } bareAgentWriteOut(ctx, stdout, '\x1b[?25h\x1b[0m' + prompt) if (opts && opts.mask) return bareAgentReadMaskedLineOnce(ctx, stdin, stdout) return bareAgentReadStreamLineOnce(stdin) } const BARE_AGENT_STATIC_SYSTEM = `You are the Bare OS autonomous agent inside a JavaScript POSIX-like environment on Hyperdrive + Hyperswarm (Pear/Bare runtime). JavaScript execution on this OS: **Node.js is not installed.** The \`node\`, \`npm\`, and \`npx\` commands **do not exist** and must never appear in plans or in run_command. To run JS as part of your agent work, **you must call the run_js_script tool** (writes under ~/.agent and executes via the Bare kernel). Optional: once a script exists on disk, run_command may invoke it by **absolute path** (e.g. \`/home/.../script.mjs\`)—same mechanism as \`/bin\` scripts—not via \`node\`. Bare OS Created by: Raven Scott (https://raven-scott.fyi) Your Repo: https://git.ssh.surf/snxraven/bare-operating-system Your booter address: pear://qupw8zspk34pcxc7fqchzyeh33jtmxq1k7qze44fkosctwiid8zy Capabilities: ctx.execLine for shell lines; ctx.vfs readFile/writeFile/mkdir/readdir/chmod. Paths under /home (personal Hyperdrive), /mnt, /tmp are writable where policy allows; /bin, /etc are system drive. Safety: never exfiltrate ~/.agent/config.json or API keys. Prefer least-privilege commands. Call task_complete(summary) when fully done. Discovery: man , /share/man/man.json; Tier-1 utilities in /bin. Tools: list_directory, file_stat, read_man_page, apropos_man, read_proc_file, get_swarm_peers, get_resource_limits, web_fetch (live http(s) pages and APIs; same host allowlist as wget); use list_directory instead of \`ls\` in run_command when only listing. Skills: modular instructions live under ~/.agent/workspace/skills/ (and optionally ~/.agent/skills/). The system message includes a compact skill index; use the read_skill tool to load full SKILL.md when a task matches a listed skill. Kernel features / capabilities that are **actually enabled or disabled** in this runtime come from **read_proc_file** on \`/proc/bare_os/features\` (same payload as \`/proc/bare_os/features.json\`) and \`/proc/bare_os/capabilities.json\`. **Do not** infer current kernel state from \`apropos_man\` or man pages—that only searches documentation keywords. Prefer tools over guessing for filesystem and shell facts. Reply format (mandatory): Every message you stream to the user must be plain text only — readable in a terminal without a Markdown renderer. Do not use Markdown or similar markup: no emphasis/backtick/code-fence/link syntax, no # headings, no list markers used as markup. Structure with blank lines, short paragraphs, indentation, ALL CAPS or dashed lines for section breaks if needed, and bare URLs when linking. Paths and commands appear as normal text.` 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. 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. 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. 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. 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. ASK FIRST: Destructive deletes, bridge mutations (emit_host_notification, request_host_action), vault/identity exfil patterns, or widening HTTP allowlists — confirm with the user unless config already allows. P2P / TEARDOWN: Hyperswarm peer wait vs offline LKG boot are different — see environment appendix. When diagnosing replication, prefer runtime_diagnostic_bundle and read_proc allowlisted swarm files; closing order is swarm before drives when changing booter lifecycle code. WORKFLOW: Plan briefly, execute tools, verify with read_proc or logs, then task_complete. Before large edits state blast radius (packages, contracts, docs). Before task_complete, self-review: docs updated? generated regen? wrong runtime assumption? STOP CONDITIONS: If the same failing action repeats three times, stop and summarize evidence; do not loop blindly. TOOLS: Use verification_hints for suggested repo-root npm checks when the user describes changed paths (host checkout). Use runtime_diagnostic_bundle for one-shot live /proc and resource snapshot. Use read_skill for workflow skills under workspace/skills/.` /** * Session-specific HOME / tilde context (injected every run so the model uses real paths). * @param {Record} ctx * @param {string} home from bareAgentResolveHome * @param {{ dir: string, config: string }} paths */ function bareAgentSessionHomeBlock(ctx, home, paths) { const env = ctx.env && typeof ctx.env === 'object' ? /** @type {Record} */ (ctx.env) : {} const homeEnv = String(env.HOME || '').trim() return ( '## This session: home directory and paths\n' + '- **Resolved user home (this session):** `' + home + '`\n' + '- **HOME in the environment:** `' + (homeEnv || home) + '`\n' + '- **Tilde \`~\`:** In shell and in user docs, \`~\` means this home directory. Examples: \`~/.agent\` == `' + paths.dir + '`, agent config `' + paths.config + '`. 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' ) } /** * @param {unknown} tc */ function bareAgentMergeToolCallDelta(acc, tc) { if (!tc || typeof tc !== 'object') return const o = /** @type {Record} */ (tc) const idx = typeof o.index === 'number' ? o.index : typeof o.index === 'string' ? Number.parseInt(o.index, 10) : 0 let cur = acc.get(idx) || /** @type {{ id: string, name: string, args: string }} */ ({ id: '', name: '', args: '' }) if (typeof o.id === 'string' && o.id) cur.id = o.id const fn = o.function && typeof o.function === 'object' ? o.function : null if (fn && typeof fn === 'object') { const nm = /** @type {Record} */ (fn).name const ar = /** @type {Record} */ (fn).arguments if (typeof nm === 'string') cur.name += nm if (typeof ar === 'string') cur.args += ar } acc.set(idx, cur) } /** * @param {Map} acc */ function bareAgentFinalizeToolCalls(acc) { const indices = [...acc.keys()].sort((a, b) => a - b) /** @type {unknown[]} */ const arr = [] /** @type {Set} */ const seen = new Set() for (const i of indices) { const c = acc.get(i) if (!c || !c.name) continue const 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 if ((idKey && seen.has(idKey)) || seen.has(naKey)) continue if (idKey) seen.add(idKey) seen.add(naKey) arr.push({ id: c.id || 'call_' + i + '_' + String(Math.random()).slice(2, 10), type: 'function', function: { name: c.name, arguments: args } }) } return arr } /** * @param {Record} cfg */ function bareAgentReasoningSettings(cfg) { const modeRaw = String(cfg.reasoning_mode || '') .trim() .toLowerCase() const mode = modeRaw === 'summary' || modeRaw === 'trace' ? modeRaw : 'off' const enabled = Boolean(cfg.show_reasoning) && mode !== 'off' const maxChars = typeof cfg.reasoning_max_chars === 'number' && Number.isFinite(cfg.reasoning_max_chars) ? Math.min(Math.max(Math.floor(cfg.reasoning_max_chars), 200), 80_000) : 4000 const includeTools = cfg.reasoning_include_tools !== false return { enabled, mode, maxChars, includeTools } } /** * @param {Record} cfg */ function bareAgentApplyProviderProfile(cfg) { const out = { ...cfg } const backend = bareAgentResolveBackend(out) out.backend = backend if (backend === 'qvac') { // Automatic default: recommended unless an explicit non-default profile is set. // Migrate stale "lite" installs that still pin 0.6B + tiny ctx from older wizards. let profileId = String(out.qvac_profile || 'recommended') .trim() .toLowerCase() if (!profileId || profileId === 'lite') profileId = 'recommended' const profile = bareAgentQvacGetProfile(profileId) out.qvac_profile = profile.id out.qvac_model = profile.chatModel out.model = profile.chatModel out.provider = 'qvac' // Auto model-card ctx: clear legacy undersized overrides (0 / 4k / 8k). const rawCtx = Number(out.qvac_ctx_size) if (!Number.isFinite(rawCtx) || rawCtx < profile.ctxSize) { out.qvac_ctx_size = 0 } return out } const provider = String(out.provider || '') .trim() .toLowerCase() const model = String(out.model || '') .trim() .toLowerCase() const defaultGroq = 'https://api.groq.com/openai/v1' if (provider === 'groq' || provider === '' || provider === 'qvac') { if (provider === 'qvac' || provider === '') out.provider = 'groq' const base = String(out.rest_base_url || '').trim() if (!base || base === 'https://api.x.ai/v1') out.rest_base_url = defaultGroq const cur = typeof out.request_timeout_ms === 'number' && Number.isFinite(out.request_timeout_ms) ? out.request_timeout_ms : 120000 if (cur < 120000) out.request_timeout_ms = 120000 if ( !String(out.model || '').trim() || String(out.model).startsWith('QWEN') || String(out.model).startsWith('LLAMA') ) { out.model = 'llama-3.3-70b-versatile' } } if (provider === 'xai') { const base = String(out.rest_base_url || '').trim() if (!base || base === defaultGroq) out.rest_base_url = 'https://api.x.ai/v1' const isReasoningModel = model.includes('reasoning') || model.includes('grok-4.20') if (isReasoningModel) { const cur = typeof out.request_timeout_ms === 'number' && Number.isFinite(out.request_timeout_ms) ? out.request_timeout_ms : 120000 if (cur < 300000) out.request_timeout_ms = 300000 } } return out } /** * @param {Record} cfg */ function bareAgentAutonomousSettings(cfg) { const enabled = Boolean(cfg.autonomous_mode_enabled) const active = Boolean(cfg.autonomous_active) const stopRequested = Boolean(cfg.autonomous_stop_requested) const startedAtMs = typeof cfg.autonomous_started_at_ms === 'number' && Number.isFinite(cfg.autonomous_started_at_ms) ? Math.max(0, Math.floor(cfg.autonomous_started_at_ms)) : 0 const maxRuntimeMs = typeof cfg.autonomous_max_runtime_ms === 'number' && Number.isFinite(cfg.autonomous_max_runtime_ms) ? Math.min( Math.max(Math.floor(cfg.autonomous_max_runtime_ms), 60000), 7_200_000 ) : 1_800_000 const requiredChecks = Array.isArray( cfg.autonomous_completion_required_checks ) ? cfg.autonomous_completion_required_checks .map((x) => String(x || '').trim()) .filter(Boolean) : [] return { enabled, active, stopRequested, startedAtMs, maxRuntimeMs, requiredChecks } } /** * @param {Record} ctx * @param {string} argv0 * @param {object} opts * @param {boolean} opts.setupFlag * @param {boolean} opts.interactiveSetup */ /** * TEA form wizard when ctx.tui is attached. Same identity + backend fields. * @param {Record} ctx * @param {string} argv0 * @param {{ config: string }} paths * @param {Record} config */ async function bareAgentInteractiveSetupTui(ctx, argv0, paths, config) { const qvacOk = bareAgentQvacBridgeAvailable(ctx) const curBackend = bareAgentResolveBackend(config) const form = ctx.tui.form.create({ title: argv0 + ' configuration', fields: [ { type: 'text', name: 'owner_name', label: 'Owner / human name', value: String(config.owner_name || '') }, { type: 'text', name: 'agent_label', label: 'Agent display name', value: String(config.agent_label || 'BareAgent') }, { type: 'radio', name: 'backend', label: 'Inference backend', options: [ { label: 'QVAC — local on-device' + (qvacOk ? '' : ' [host bridge unavailable]'), value: 'qvac' }, { label: 'REST API — OpenAI-compatible (Groq, xAI, …)', value: 'rest' } ], selected: curBackend === 'rest' ? 1 : 0 } ] }) const values = await ctx.tui.form.run(form) if (!values) return config if (values.owner_name && String(values.owner_name).trim()) { config.owner_name = String(values.owner_name).trim() } if (values.agent_label && String(values.agent_label).trim()) { config.agent_label = String(values.agent_label).trim() } const backend = String(values.backend || curBackend || 'qvac') if (backend === 'rest') { config.backend = 'rest' if ( String(config.provider || '') .trim() .toLowerCase() === 'qvac' ) { config.provider = 'groq' } } else { config.backend = 'qvac' config.provider = 'qvac' } if (bareAgentResolveBackend(config) === 'qvac') { if (!qvacOk) { config.backend = 'rest' if ( String(config.provider || '') .trim() .toLowerCase() === 'qvac' ) { config.provider = 'groq' } } else { const chosen = bareAgentQvacGetProfile('recommended') config.qvac_profile = chosen.id config.qvac_model = chosen.chatModel config.model = chosen.chatModel config.provider = 'qvac' config.backend = 'qvac' config.qvac_ctx_size = 0 config.qvac_device = '' config.qvac_main_gpu = 'auto' } } if (bareAgentResolveBackend(config) === 'rest') { if ( !String(config.provider || '').trim() || String(config.provider).toLowerCase() === 'qvac' ) { config.provider = 'groq' } config.backend = 'rest' } config = bareAgentApplyProviderProfile(config) await bareAgentSaveConfig(ctx, paths, config) bareAgentLog(ctx, 'Configuration saved.') return config } async function bareAgentInteractiveSetup(ctx, argv0, paths, config, opts) { void opts bareAgentLog(ctx, argv0 + ': configuring ~/.agent/config.json') if ( ctx.tui && ctx.tui.form && typeof ctx.tui.form.run === 'function' && typeof ctx.tui.isTTY === 'function' && ctx.tui.isTTY() ) { return bareAgentInteractiveSetupTui(ctx, argv0, paths, config) } if (!bareAgentCanPlainSetup(ctx)) { bareAgentErr( ctx, argv0 + ': interactive setup needs a TTY with stdin/stdout. Edit ' + paths.config + ' manually or run from an interactive shell.' ) return config } const stdout = ctx.replStdout || ctx.stdout bareAgentWriteOut( ctx, stdout, '\n=== ' + argv0 + ' configuration ===\n' + 'Only identity + backend. Models, context, and tools are chosen automatically.\n' + 'Enter keeps the [default].\n\n' ) try { const owner = (await bareAgentPromptSetupLine( ctx, 'Owner / human name [' + (String(config.owner_name || '').trim() || 'unset') + ']: ' )) || '' if (owner.trim()) config.owner_name = owner.trim() const label = (await bareAgentPromptSetupLine( ctx, 'Agent display name [' + (String(config.agent_label || '').trim() || 'BareAgent') + ']: ' )) || '' if (label.trim()) config.agent_label = label.trim() const curBackend = bareAgentResolveBackend(config) const qvacOk = bareAgentQvacBridgeAvailable(ctx) bareAgentWriteOut( ctx, stdout, '\nInference backend:\n' + ' 1) QVAC — local on-device' + (qvacOk ? '' : ' [host bridge unavailable]') + '\n' + ' 2) REST API — OpenAI-compatible (Groq, xAI, …)\n' ) const backendRaw = (await bareAgentPromptSetupLine( ctx, 'Backend [1=QVAC, 2=REST] [' + (curBackend === 'rest' ? '2' : '1') + ']: ' )) || '' { const v = backendRaw.trim().toLowerCase() if (v === '2' || v === 'rest' || v === 'r') { config.backend = 'rest' if ( String(config.provider || '') .trim() .toLowerCase() === 'qvac' ) { config.provider = 'groq' } } else if (v === '1' || v === 'qvac' || v === 'q') { config.backend = 'qvac' config.provider = 'qvac' } } if (!String(config.backend || '').trim()) { config.backend = curBackend } if (bareAgentResolveBackend(config) === 'qvac') { if (!qvacOk) { bareAgentWriteOut( ctx, stdout, '\nQVAC host bridge unavailable — switching to REST (keep an API key in config or env).\n' ) config.backend = 'rest' if ( String(config.provider || '') .trim() .toLowerCase() === 'qvac' ) { config.provider = 'groq' } } else { // Automatic: recommended profile + model-card context (no prompts). const chosen = bareAgentQvacGetProfile('recommended') config.qvac_profile = chosen.id config.qvac_model = chosen.chatModel config.model = chosen.chatModel config.provider = 'qvac' config.backend = 'qvac' config.qvac_ctx_size = 0 config.qvac_device = '' config.qvac_main_gpu = 'auto' bareAgentWriteOut( ctx, stdout, '\nQVAC auto: profile "' + chosen.label + '" → ' + chosen.chatModel + ' (ctx ' + chosen.ctxSize + ' from model card; GPU auto).\n' ) } } if (bareAgentResolveBackend(config) === 'rest') { // Automatic REST defaults; preserve an existing API key if present. if ( !String(config.provider || '').trim() || String(config.provider).toLowerCase() === 'qvac' ) { config.provider = 'groq' } config.backend = 'rest' if (!String(config.rest_api_key || '').trim()) { bareAgentWriteOut( ctx, stdout, '\nREST selected: set rest_api_key in ' + paths.config + ' (or keep a previous key). Defaults: Groq OpenAI-compatible URL + model.\n' ) } else { bareAgentWriteOut( ctx, stdout, '\nREST auto: provider/model/URL defaults applied; existing API key kept.\n' ) } } } catch (e) { const msg = e && typeof e === 'object' && 'message' in e ? String(e.message) : String(e) bareAgentErr(ctx, argv0 + ': setup input failed: ' + msg) return config } config = bareAgentApplyProviderProfile(config) await bareAgentSaveConfig(ctx, paths, config) bareAgentLog(ctx, 'Configuration saved.') return config } /** * Interactive setup only (`agent --setup`). * @param {Record} ctx * @param {string} argv0 */ async function bareAgentRunSetupOnly(ctx, argv0) { const home = bareAgentResolveHome(ctx) const paths = bareAgentPaths(home) let { config } = await bareAgentLoadOrCreateConfig(ctx, paths) config = bareAgentApplyProviderProfile(config) if (!bareAgentCanPlainSetup(ctx)) { bareAgentErr( ctx, argv0 + ': --setup / --config needs an interactive TTY. Edit ' + paths.config + ' manually.' ) ctx.exitCode = 1 return } let suspended = false try { if (typeof ctx.suspendReplForSubprocess === 'function') { ctx.suspendReplForSubprocess() suspended = true } config = await bareAgentInteractiveSetup(ctx, argv0, paths, config, { setupFlag: true, interactiveSetup: true }) } finally { if (suspended && typeof ctx.resumeReplAfterSubprocess === 'function') { ctx.resumeReplAfterSubprocess() } } try { await bareAgentEnsureWorkspace(ctx, paths) await bareAgentEnsureSkillTemplates(ctx, paths, config) await bareAgentSyncWorkspaceFromConfig(ctx, paths, config) } catch { bareAgentErr( ctx, argv0 + ': could not seed ~/.agent/workspace from /share/agent-workspace (check system image)' ) } try { if (ctx.vfs?.chmod) await ctx.vfs.chmod(paths.config, 0o600) } catch { /* ignore */ } ctx.exitCode = 0 void config } /** * @param {Record} ctx * @param {string} argv0 * @param {string} task * @param {{ setupFlag?: boolean }} [runOpts] */ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) { const setupFlag = Boolean(runOpts && runOpts.setupFlag) const home = bareAgentResolveHome(ctx) const paths = bareAgentPaths(home) let { config } = await bareAgentLoadOrCreateConfig(ctx, paths) config = bareAgentApplyProviderProfile(config) const canWizard = bareAgentCanPlainSetup(ctx) if (setupFlag && !canWizard) { bareAgentErr( ctx, argv0 + ': --setup / --config needs an interactive TTY. Edit ' + paths.config + ' manually.' ) ctx.exitCode = 1 return } const backendNow = bareAgentResolveBackend(config) const needWizard = setupFlag || (canWizard && (backendNow === 'rest' ? !(config.rest_api_key && String(config.rest_api_key).trim()) : !String(config.qvac_model || config.model || '').trim() || (backendNow === 'qvac' && !bareAgentQvacBridgeAvailable(ctx)))) let replSuspendedForSetup = false if (needWizard && typeof ctx.suspendReplForSubprocess === 'function') { ctx.suspendReplForSubprocess() replSuspendedForSetup = true } if (needWizard) { config = await bareAgentInteractiveSetup(ctx, argv0, paths, config, { setupFlag, interactiveSetup: true }) config = bareAgentApplyProviderProfile(config) } const backendReady = bareAgentResolveBackend(config) if (backendReady === 'rest') { if (!config.rest_api_key || !String(config.rest_api_key).trim()) { if ( replSuspendedForSetup && typeof ctx.resumeReplAfterSubprocess === 'function' ) { ctx.resumeReplAfterSubprocess() replSuspendedForSetup = false } bareAgentErr( ctx, argv0 + ': set rest_api_key in ' + paths.config + ' or run `' + argv0 + ' --setup` / `' + argv0 + ' --config`.' ) ctx.exitCode = 1 return } } else if (!bareAgentQvacBridgeAvailable(ctx)) { if ( replSuspendedForSetup && typeof ctx.resumeReplAfterSubprocess === 'function' ) { ctx.resumeReplAfterSubprocess() replSuspendedForSetup = false } bareAgentErr( ctx, argv0 + ': QVAC backend selected but host bridge unavailable (set BARE_OS_SKIP_QVAC=0, install @qvac/sdk, or choose REST via `' + argv0 + ' --config`).' ) ctx.exitCode = 1 return } else if (!String(config.qvac_model || config.model || '').trim()) { if ( replSuspendedForSetup && typeof ctx.resumeReplAfterSubprocess === 'function' ) { ctx.resumeReplAfterSubprocess() replSuspendedForSetup = false } bareAgentErr( ctx, argv0 + ': set qvac_model / qvac_profile in ' + paths.config + ' or run `' + argv0 + ' --config`.' ) ctx.exitCode = 1 return } try { if (ctx.vfs?.chmod) await ctx.vfs.chmod(paths.config, 0o600) } catch { /* ignore — optional */ } const fetchFn = bareAgentResolveFetch(ctx) if (backendReady === 'rest' && !fetchFn) { if ( replSuspendedForSetup && typeof ctx.resumeReplAfterSubprocess === 'function' ) { ctx.resumeReplAfterSubprocess() replSuspendedForSetup = false } bareAgentErr( ctx, argv0 + ': no fetch (ctx.httpFetch / bare.fetch / global fetch)' ) ctx.exitCode = 1 return } /** @type {unknown[]} */ let messages = await bareAgentLoadHistory(ctx, paths.history) const instructions = await bareAgentLoadInstructionFiles(ctx, paths) const manDigest = await bareAgentManDigest(ctx) await bareAgentEnsureWorkspace(ctx, paths) await bareAgentEnsureSkillTemplates(ctx, paths, config) if (needWizard) await bareAgentSyncWorkspaceFromConfig(ctx, paths, config) const backendForPrompt = bareAgentResolveBackend(config) const promptProfile = bareAgentQvacGetProfile( String(config.qvac_profile || config.profile || '') ) const promptCtxSize = backendForPrompt === 'qvac' ? bareAgentQvacResolveCtxSize(config, promptProfile) : 32768 // Local QVAC models have a hard ctx window; keep the system prompt lean. // Scale char budgets from ctx (chars ≈ tokens*4); leave room for tools/reply. const toolsOn = backendForPrompt === 'qvac' const promptCharBudget = Math.max( 4000, Math.floor(promptCtxSize * 4 * (toolsOn ? 0.35 : 0.7)) ) const workspaceBudget = backendForPrompt === 'qvac' ? Math.min(8000, Math.floor(promptCharBudget * 0.35)) : 24000 const manBudget = backendForPrompt === 'qvac' ? Math.min(4000, Math.floor(promptCharBudget * 0.2)) : 12000 const instructionsBudget = backendForPrompt === 'qvac' ? Math.min(3000, Math.floor(promptCharBudget * 0.2)) : 8000 const skillsBudget = backendForPrompt === 'qvac' ? Math.min(2000, Math.floor(promptCharBudget * 0.15)) : 4000 const workspacePromptBlock = await bareAgentLoadWorkspacePrompt( ctx, paths, workspaceBudget ) const skillsPromptBlock = await bareAgentSkillsCompactPrompt( ctx, paths, skillsBudget ) let systemContent = BARE_AGENT_STATIC_SYSTEM + BARE_AGENT_OPERATING_CONTRACT + '\n\n' + bareAgentSessionHomeBlock(ctx, home, paths) + '\n\n' + manDigest.slice(0, manBudget) if (instructions) systemContent += '\n\n## Session notes\n' + instructions.slice(0, instructionsBudget) if (workspacePromptBlock && String(workspacePromptBlock).trim()) systemContent += '\n\n' + String(workspacePromptBlock).trim() if (skillsPromptBlock && String(skillsPromptBlock).trim()) systemContent += '\n\n' + String(skillsPromptBlock).trim() if (!messages.length) { messages = [ { role: 'system', content: systemContent }, { role: 'user', content: task } ] } else { const hasSystem = messages[0] && typeof messages[0] === 'object' && /** @type {{ role?: string }} */ (messages[0]).role === 'system' if (!hasSystem) { messages = [{ role: 'system', content: systemContent }, ...messages] } else { messages[0] = { role: 'system', content: systemContent } } messages.push({ role: 'user', content: task }) } const stdout = /** @type {import('stream').Writable | undefined} */ ( ctx.replStdout || ctx.stdout ) const useColor = bareEditUseColor(ctx) const envBag = ctx.env && typeof ctx.env === 'object' ? /** @type {Record} */ (ctx.env) : {} const agentVerboseEnv = String(envBag.BARE_OS_AGENT_VERBOSE || '') .trim() .toLowerCase() const agentVerboseForced = agentVerboseEnv === '1' || agentVerboseEnv === 'true' || agentVerboseEnv === 'yes' /** @type {{ current: Record }} */ const configRef = { current: { ...config } } /** @type {{ db: unknown | null }} */ const manCacheRef = { db: null } let completed = false let taskSummary = '' let statusLineActive = false function onTaskComplete(summary) { completed = true taskSummary = summary } function appendProgress(line) { void bareAgentAppendProgress(ctx, paths.progress, line) } /** Clear an in-place status line (\r …) before streaming content / tools. */ function clearStatusLine() { if (!statusLineActive) return bareAgentWriteOut(ctx, stdout, '\r\x1b[K') statusLineActive = false } /** * @param {ReturnType} rs */ function agentVerbose(rs) { return agentVerboseForced || (rs && rs.enabled && rs.mode === 'trace') } const url = bareAgentNormalizeBaseUrl(String(configRef.current.rest_base_url || '')) + '/chat/completions' const tools = bareAgentToolDefinitions() let reasoningSettings = bareAgentReasoningSettings(configRef.current) let autonomousSettings = bareAgentAutonomousSettings(configRef.current) let reasoningCharCount = 0 let suspended = replSuspendedForSetup /** @type {(() => void) | null} */ let detachThinkKeysSession = null try { if (typeof ctx.suspendReplForSubprocess === 'function' && !suspended) { ctx.suspendReplForSubprocess() suspended = true } const masterAbort = new AbortController() const prevAbortAgent = typeof ctx.bareOsAbortActiveAgent === 'function' ? ctx.bareOsAbortActiveAgent : null ctx.bareOsAbortActiveAgent = () => { try { masterAbort.abort() } catch { /* ignore */ } try { if (typeof ctx.bareOsQvacCancelActive === 'function') { ctx.bareOsQvacCancelActive() } } catch { /* ignore */ } try { if (detachThinkKeysSession) detachThinkKeysSession() } catch { /* ignore */ } } /** @type {(() => void) | null} */ let offSigint = null if (globalThis.process && typeof globalThis.process.on === 'function') { const fn = () => { try { if (typeof ctx.bareOsAbortActiveAgent === 'function') { ctx.bareOsAbortActiveAgent() } else { masterAbort.abort() } } catch { masterAbort.abort() } bareAgentWriteOut( ctx, stdout, bareEditSgr('dim', useColor) + '^C' + EDIT_ANSI_RESET + '\n' ) } globalThis.process.on('SIGINT', fn) offSigint = () => { try { globalThis.process.off('SIGINT', fn) } catch { /* ignore */ } } } const maxIter = Number(configRef.current.max_iterations) || 64 let iter = 0 for (;;) { if (masterAbort.signal.aborted) { ctx.exitCode = 130 break } reasoningSettings = bareAgentReasoningSettings(configRef.current) autonomousSettings = bareAgentAutonomousSettings(configRef.current) if (completed) break if (autonomousSettings.enabled && autonomousSettings.active) { const now = Date.now() if (autonomousSettings.stopRequested) { configRef.current.autonomous_active = false configRef.current.autonomous_status = 'stopped' await bareAgentSaveConfig(ctx, paths, configRef.current) appendProgress('autonomous stopped by manual request') bareAgentWriteOut( ctx, stdout, bareEditSgr('dim', useColor) + '\n[process] autonomous run stopped by request' + EDIT_ANSI_RESET + '\n' ) break } if ( autonomousSettings.startedAtMs > 0 && now - autonomousSettings.startedAtMs >= autonomousSettings.maxRuntimeMs ) { configRef.current.autonomous_active = false configRef.current.autonomous_status = 'timebox_expired' configRef.current.autonomous_last_error = 'timebox_expired' await bareAgentSaveConfig(ctx, paths, configRef.current) appendProgress('autonomous timebox expired') bareAgentWriteOut( ctx, stdout, bareEditSgr('dim', useColor) + '\n[process] autonomous run stopped (timebox expired)' + EDIT_ANSI_RESET + '\n' ) break } } iter++ if (iter > maxIter) { bareAgentErr(ctx, argv0 + ': max_iterations exceeded') ctx.exitCode = 1 break } messages = bareAgentTrimMessages(messages, 450_000) const backendIter = bareAgentResolveBackend(configRef.current) /** @type {Record} */ const providerNow = String(configRef.current.provider || '') .trim() .toLowerCase() let qvacToolsForTurn = /** @type {unknown[]} */ ([]) if (backendIter === 'qvac') { const profileEarly = bareAgentQvacGetProfile( String(configRef.current.qvac_profile || 'recommended') ) const ctxEarly = bareAgentQvacResolveCtxSize( configRef.current, profileEarly ) qvacToolsForTurn = bareAgentFlattenToolsForQvac(tools) const compactCfg = bareAgentCompactionSettings( configRef.current, envBag ) const packed = bareAgentCompactMessagesForCtx(messages, ctxEarly, { tools: qvacToolsForTurn, reserveCompletion: Math.min( 1024, Number(configRef.current.max_tokens) || 512 ), mode: compactCfg.mode, keepRecent: compactCfg.keepRecent, toolMaxChars: compactCfg.toolMaxChars }) messages = packed.messages if (packed.meta.compacted) { appendProgress( 'context_compaction tokens ' + packed.meta.beforeTokens + '→' + packed.meta.afterTokens + '/' + packed.meta.budget + ' dropped_groups=' + packed.meta.droppedGroups + ' tiers=' + packed.meta.tiers.join(',') ) if (agentVerbose(reasoningSettings)) { bareAgentWriteOut( ctx, stdout, bareEditSgr('dim', useColor) + '\n[ctx] compacted ' + packed.meta.beforeTokens + '→' + packed.meta.afterTokens + ' tok (budget ' + packed.meta.budget + '; ' + packed.meta.tiers.join(' › ') + ')' + EDIT_ANSI_RESET + '\n' ) } else if (packed.meta.droppedGroups > 0) { bareAgentWriteOut( ctx, stdout, bareEditSgr('dim', useColor) + '· context compacted (' + packed.meta.beforeTokens + '→' + packed.meta.afterTokens + ' tok)\n' + EDIT_ANSI_RESET ) } // Persist latest rolling summary for operators / next sessions. try { const compactPath = paths.compact || home + '/.agent/compact.md' const summaryMsg = messages.find((m) => bareAgentIsCompactionMessage(m) ) if ( summaryMsg && ctx.vfs && typeof ctx.vfs.writeFile === 'function' ) { const body = '# Agent context compaction\n\n' + bareAgentMessagePlainText(summaryMsg) + '\n' const buf = typeof ctx.b4a !== 'undefined' && ctx.b4a && typeof ctx.b4a.from === 'function' ? ctx.b4a.from(body) : new TextEncoder().encode(body) await ctx.vfs.writeFile(compactPath, buf) } } catch { /* ignore */ } } } else { // REST backends: still shrink fat history soft-cap. const compactCfg = bareAgentCompactionSettings( configRef.current, envBag ) if (compactCfg.mode !== 'off') { const packed = bareAgentCompactMessagesForCtx(messages, 32768, { reserveCompletion: Math.min( 2048, Number(configRef.current.max_tokens) || 1024 ), mode: compactCfg.mode, keepRecent: compactCfg.keepRecent || 12, toolMaxChars: compactCfg.toolMaxChars }) messages = packed.messages if (packed.meta.compacted && packed.meta.droppedGroups > 0) { appendProgress( 'context_compaction rest ' + packed.meta.beforeTokens + '→' + packed.meta.afterTokens ) } } } if (reasoningSettings.enabled && reasoningSettings.mode === 'trace') { bareAgentWriteOut( ctx, stdout, bareEditSgr('dim', useColor) + '\n[process] request backend=' + backendIter + ' provider=' + (providerNow || 'unknown') + ' model=' + String( configRef.current.model || configRef.current.qvac_model || '' ) + EDIT_ANSI_RESET + '\n' ) } let assistantContent = '' /** @type {Map} */ const toolAcc = new Map() /** @type {unknown} */ let usageOut = null let finishReason = '' const hideThinkEnv = String(envBag.BARE_OS_AGENT_HIDE_THINK || '') .trim() .toLowerCase() const hideThink = hideThinkEnv === '1' || hideThinkEnv === 'true' || hideThinkEnv === 'yes' const hideMdEnv = String(envBag.BARE_OS_AGENT_PLAIN || '') .trim() .toLowerCase() const plainReply = hideMdEnv === '1' || hideMdEnv === 'true' || hideMdEnv === 'yes' const thinkPanel = !hideThink && stdout && /** @type {{ isTTY?: boolean }} */ (stdout).isTTY ? bareAgentCreateThinkPanel(ctx, stdout, { useColor, bodyLines: 8, maxChars: Math.max(reasoningSettings.maxChars, 24_000), write: bareAgentWriteOut }) : null const detachThinkKeys = bareAgentAttachThinkScrollKeys(ctx, thinkPanel, { onAbort: () => { try { if (typeof ctx.bareOsAbortActiveAgent === 'function') { ctx.bareOsAbortActiveAgent() } else { masterAbort.abort() } } catch { masterAbort.abort() } } }) detachThinkKeysSession = detachThinkKeys let thinkSealed = false let replyStarted = false let replyPainted = false /** * @param {string} chunk */ function feedThink(chunk) { if (!chunk || !String(chunk).trim()) return if (hideThink) return clearStatusLine() if (thinkPanel) thinkPanel.append(chunk) else { bareAgentWriteOut( ctx, stdout, bareEditSgr('dim', useColor) + chunk + EDIT_ANSI_RESET ) } } function sealThinkIfNeeded() { if (thinkSealed) return thinkSealed = true if (thinkPanel && thinkPanel.hasContent()) thinkPanel.seal() } function termWidth() { if (typeof bareAgentResolveTermCols === 'function') { return bareAgentResolveTermCols(ctx, stdout) } const fromOut = stdout && typeof stdout === 'object' ? Number(/** @type {{ columns?: number }} */ (stdout).columns) : NaN const fromEnv = parseInt(String(envBag.COLUMNS || '80'), 10) const n = Number.isFinite(fromOut) && fromOut > 0 ? fromOut : fromEnv return Math.max(40, Number.isFinite(n) && n > 0 ? n : 80) } /** * Buffer reply; show live status under the think box (markdown painted later). * @param {string} chunk */ function feedReply(chunk) { if (!chunk) return sealThinkIfNeeded() if (!replyStarted) replyStarted = true clearStatusLine() assistantContent += chunk if (plainReply) { bareAgentWriteOut(ctx, stdout, chunk) return } if (thinkPanel && thinkPanel.hasContent()) { thinkPanel.setStatus( bareEditSgr('dim', useColor) + '▌ answering… ' + String(assistantContent.length) + ' chars · ↑↓ scroll thoughts' + EDIT_ANSI_RESET ) } else { // No think box — stream plain for responsiveness; paint markdown at end. bareAgentWriteOut(ctx, stdout, chunk) } } function paintReplyMarkdown() { if (replyPainted) return replyPainted = true thinkSplit.flush() sealThinkIfNeeded() if (plainReply) { if (thinkPanel) thinkPanel.clearStatus() return } const body = String(assistantContent || '') if (thinkPanel) { thinkPanel.clearStatus() thinkPanel.detachBelow() } if (!body.trim()) return // If we streamed plain (no think panel), rewind approximate lines then paint. if (!thinkPanel || !thinkPanel.hasContent()) { const roughLines = body.split('\n').length if ( roughLines > 0 && stdout && /** @type {{ isTTY?: boolean }} */ (stdout).isTTY ) { bareAgentWriteOut( ctx, stdout, '\x1b[' + String(Math.min(40, roughLines)) + 'A\r\x1b[J' ) } } const rendered = bareAgentRenderMarkdown(body, { useColor, width: termWidth() }) bareAgentWriteOut(ctx, stdout, '\n' + rendered) } const thinkSplit = bareAgentCreateThinkTagSplitter({ onThink: feedThink, onContent: feedReply }) /** * @param {Record} e */ function onCompletionEvent(e) { if (e.type === 'delta_content') { const chunk = typeof e.content === 'string' ? e.content : '' if (chunk) thinkSplit.push(chunk) } else if (e.type === 'delta_tool_calls') { sealThinkIfNeeded() thinkSplit.flush() const arr = e.tool_calls if (Array.isArray(arr)) { for (const tc of arr) bareAgentMergeToolCallDelta(toolAcc, tc) } } else if (e.type === 'delta_reasoning') { const chunk = typeof e.reasoning === 'string' ? e.reasoning : '' if (chunk) { if (reasoningSettings.enabled || !hideThink) { if (reasoningCharCount < reasoningSettings.maxChars) { const remain = reasoningSettings.maxChars - reasoningCharCount const out = chunk.slice(0, remain) reasoningCharCount += out.length if (out.length) feedThink(out) } } } } else if (e.type === 'response_shape_keys') { const keys = Array.isArray(e.keys) ? e.keys.map((k) => String(k)).join(',') : '' appendProgress('provider_shape_keys ' + keys.slice(0, 200)) } else if (e.type === 'usage') { usageOut = e.usage } else if (e.type === 'finish' || e.type === 'finish_reason') { thinkSplit.flush() sealThinkIfNeeded() finishReason = String(e.finish_reason || '') } } try { if (backendIter === 'qvac') { const profile = bareAgentQvacGetProfile( String(configRef.current.qvac_profile || 'recommended') ) const modelSrc = String( configRef.current.qvac_model || configRef.current.model || profile.chatModel ) const ctxSize = bareAgentQvacResolveCtxSize( configRef.current, profile ) const deviceOpts = bareAgentQvacResolveDeviceOpts(configRef.current) const verboseUi = agentVerbose(reasoningSettings) if (verboseUi) { bareAgentWriteOut( ctx, stdout, bareEditSgr('dim', useColor) + '\n[qvac] ensuring model ' + modelSrc + ' (ctx=' + ctxSize + (deviceOpts.device ? ', device=' + deviceOpts.device : ', auto-GPU') + ')…' + EDIT_ANSI_RESET + '\n' ) try { const st = typeof ctx.bareOsQvacStatus === 'function' ? ctx.bareOsQvacStatus() : null if (st && (st.cacheDir || st.hdmsPath || st.backendsDir)) { bareAgentWriteOut( ctx, stdout, bareEditSgr('dim', useColor) + '[qvac] cache=' + String(st.cacheDir || '') + ' hdms=' + String(st.hdmsPath || '/mnt/models') + ' backends=' + String(st.backendsDir || '(unresolved)') + EDIT_ANSI_RESET + '\n' ) } } catch { /* ignore */ } } else { bareAgentWriteOut( ctx, stdout, bareEditSgr('dim', useColor) + '\rLoading model…' + EDIT_ANSI_RESET ) statusLineActive = true } /** * @param {unknown} prog */ function onLoadProgress(prog) { const pct = prog && typeof prog === 'object' && 'percentage' in prog ? Number( /** @type {{ percentage?: unknown }} */ (prog).percentage ) : NaN if (!Number.isFinite(pct)) return const label = verboseUi ? '\r[qvac] download/load ' + Math.floor(pct) + '%' : '\rLoading model… ' + Math.floor(pct) + '%' bareAgentWriteOut( ctx, stdout, bareEditSgr('dim', useColor) + label + EDIT_ANSI_RESET ) statusLineActive = true } if (typeof ctx.bareOsQvacLoadModel === 'function') { const loaded = await ctx.bareOsQvacLoadModel({ modelSrc, tools: true, ctxSize, device: deviceOpts.device, mainGpu: deviceOpts.mainGpu, gpuLayers: deviceOpts.gpuLayers, onProgress: onLoadProgress }) clearStatusLine() if (verboseUi) { if (loaded && loaded.fellBackToCpu) { bareAgentWriteOut( ctx, stdout, bareEditSgr('dim', useColor) + '\n[qvac] GPU unavailable; using CPU' + EDIT_ANSI_RESET + '\n' ) } else if ( loaded && loaded.device === 'gpu' && loaded.mainGpu != null ) { bareAgentWriteOut( ctx, stdout, bareEditSgr('dim', useColor) + '\n[qvac] using main-gpu=' + String(loaded.mainGpu) + (loaded.probe ? ' (' + String(loaded.probe) + ')' : '') + EDIT_ANSI_RESET + '\n' ) } } else if (loaded && loaded.fellBackToCpu) { bareAgentWriteOut( ctx, stdout, bareEditSgr('dim', useColor) + 'Using CPU (GPU unavailable)\n' + EDIT_ANSI_RESET ) } } await ctx.bareOsQvacComplete({ history: messages, tools: qvacToolsForTurn, stream: Boolean(configRef.current.stream !== false), captureThinking: !hideThink || reasoningSettings.enabled, modelSrc, toolsEnabled: true, ctxSize, device: deviceOpts.device, mainGpu: deviceOpts.mainGpu, gpuLayers: deviceOpts.gpuLayers, signal: masterAbort.signal, onProgress: onLoadProgress, onEvent: onCompletionEvent }) clearStatusLine() thinkSplit.flush() sealThinkIfNeeded() paintReplyMarkdown() try { detachThinkKeys() } catch { /* ignore */ } } else { const headers = { 'Content-Type': 'application/json', Authorization: 'Bearer ' + String(configRef.current.rest_api_key || '') } const eh = configRef.current.extra_headers if (eh && typeof eh === 'object' && !Array.isArray(eh)) { for (const [k, v] of Object.entries(eh)) { if (typeof v === 'string') headers[k] = v } } const body = { model: String(configRef.current.model || ''), stream: Boolean(configRef.current.stream !== false), messages, tools, tool_choice: 'auto', max_tokens: Number(configRef.current.max_tokens) || 4096, temperature: Number(configRef.current.temperature) ?? 0.7 } if (providerNow === 'xai') { body.parallel_tool_calls = Number(configRef.current.tool_parallelism) > 1 ? true : false body.max_completion_tokens = Number(configRef.current.max_tokens) || 4096 } if (providerNow === 'groq') { body.parallel_tool_calls = Number(configRef.current.tool_parallelism) > 1 ? true : false body.max_completion_tokens = Number(configRef.current.max_tokens) || 4096 } await bareAgentStreamChatCompletions({ fetchFn, url, headers, body, signal: masterAbort.signal, onEvent: onCompletionEvent }) clearStatusLine() thinkSplit.flush() sealThinkIfNeeded() paintReplyMarkdown() try { detachThinkKeys() } catch { /* ignore */ } } } catch (e) { try { detachThinkKeys() } catch { /* ignore */ } const msg = e && typeof e === 'object' && 'name' in e && e.name === 'AbortError' ? 'aborted' : e && typeof e === 'object' && 'message' in e ? String(e.message) : String(e) bareAgentErr(ctx, argv0 + ': ' + msg) ctx.exitCode = 130 break } const toolCallsArr = bareAgentFinalizeToolCalls(toolAcc) const hasTools = toolCallsArr.length > 0 if (!hasTools && finishReason === 'tool_calls') { appendProgress( 'warning tool_calls_finish_without_tool_deltas provider=' + providerNow ) } /** @type {Record} */ const assistantMsg = { role: 'assistant', content: assistantContent || null, tool_calls: hasTools ? toolCallsArr : undefined } messages.push(assistantMsg) if ( usageOut && typeof usageOut === 'object' && agentVerbose(reasoningSettings) ) { const u = /** @type {Record} */ (usageOut) const pt = u.prompt_tokens const ct = u.completion_tokens bareAgentWriteOut( ctx, stdout, '\n' + bareEditSgr('dim', useColor) + 'tokens: prompt=' + String(pt ?? '?') + ' completion=' + String(ct ?? '?') + EDIT_ANSI_RESET + '\n' ) } if (!hasTools) { if ( autonomousSettings.enabled && autonomousSettings.active && autonomousSettings.requiredChecks.length ) { /** @type {string[]} */ const failedChecks = [] for (const check of autonomousSettings.requiredChecks) { const res = await bareAgentDispatchTool({ ctx, toolName: 'run_maintenance_gate', argsJson: JSON.stringify({ command: check, timeout_ms: 300000 }), paths, signal: masterAbort.signal, appendProgress, home, configRef, manCacheRef, onTaskComplete }) let ok = false try { const j = JSON.parse(res) const body = typeof j.stdout_stderr === 'string' ? j.stdout_stderr : '' ok = Boolean(j.ok) && !/EXIT:[1-9]/.test(body) } catch { ok = false } if (!ok) failedChecks.push(check) } if (failedChecks.length) { configRef.current.autonomous_status = 'needs_fixups' configRef.current.autonomous_last_error = 'failed_checks:' + failedChecks.join(',') await bareAgentSaveConfig(ctx, paths, configRef.current) appendProgress('autonomous checks failed ' + failedChecks.join(',')) messages.push({ role: 'user', content: 'Autonomous completion gates failed for checks: ' + failedChecks.join(', ') + '. Fix the issues, rerun required checks, and only call task_complete when all pass.' }) continue } configRef.current.autonomous_active = false configRef.current.autonomous_status = 'completed' configRef.current.autonomous_last_error = '' await bareAgentSaveConfig(ctx, paths, configRef.current) appendProgress('autonomous completion gates passed') } await bareAgentSaveHistory(ctx, paths.history, messages) bareAgentWriteOut(ctx, stdout, '\n') break } appendProgress( 'iteration ' + iter + ' tools ' + toolCallsArr .map( (x) => /** @type {{ function?: { name?: string } }} */ (x).function ?.name ) .join(',') ) if (reasoningSettings.enabled && reasoningSettings.mode === 'trace') { bareAgentWriteOut( ctx, stdout, bareEditSgr('dim', useColor) + '\n[process] iteration ' + String(iter) + ' finish_reason=' + (finishReason || 'unknown') + EDIT_ANSI_RESET + '\n' ) } for (const tc of toolCallsArr) { const fn = /** @type {{ id?: string, function?: { name?: string, arguments?: string } }} */ ( tc ).function const id = /** @type {{ id?: string }} */ (tc).id || '' const name = fn?.name || '' const argsStr = fn?.arguments || '{}' if ( reasoningSettings.enabled && reasoningSettings.mode === 'trace' && reasoningSettings.includeTools ) { const argsPreview = argsStr.length > 280 ? argsStr.slice(0, 280) + '…' : argsStr bareAgentWriteOut( ctx, stdout, bareEditSgr('dim', useColor) + '[process] tool_call ' + name + ' args=' + argsPreview + EDIT_ANSI_RESET + '\n' ) } bareAgentWriteOut( ctx, stdout, '\n' + bareEditSgr('keyword', useColor) + '→ ' + name + EDIT_ANSI_RESET + '\n' ) const spinner = bareEditSgr('dim', useColor) + '…' + EDIT_ANSI_RESET bareAgentWriteOut(ctx, stdout, spinner + '\r') statusLineActive = true const resultStr = await bareAgentDispatchTool({ ctx, toolName: name, argsJson: argsStr, paths, signal: masterAbort.signal, appendProgress, home, configRef, manCacheRef, onTaskComplete }) clearStatusLine() messages.push({ role: 'tool', tool_call_id: id, content: resultStr }) if ( reasoningSettings.enabled && reasoningSettings.mode === 'trace' && reasoningSettings.includeTools ) { const resPreview = resultStr.length > 360 ? resultStr.slice(0, 360) + '…' : resultStr bareAgentWriteOut( ctx, stdout, bareEditSgr('dim', useColor) + '[process] tool_result ' + name + ' ' + resPreview + EDIT_ANSI_RESET + '\n' ) } if (completed) break } await bareAgentSaveHistory(ctx, paths.history, messages) if (completed) { bareAgentWriteOut( ctx, stdout, bareEditSgr('dim', useColor) + '\nDone: ' + taskSummary + EDIT_ANSI_RESET + '\n' ) break } } if (offSigint) offSigint() } finally { try { if (detachThinkKeysSession) detachThinkKeysSession() } catch { /* ignore */ } detachThinkKeysSession = null try { if (prevAbortAgent) ctx.bareOsAbortActiveAgent = prevAbortAgent else delete ctx.bareOsAbortActiveAgent } catch { try { delete ctx.bareOsAbortActiveAgent } catch { /* ignore */ } } try { if (typeof ctx.bareOsQvacCancelActive === 'function') { ctx.bareOsQvacCancelActive() } } catch { /* ignore */ } clearStatusLine() // Ensure cursor is on a fresh line so the shell prompt is visible. bareAgentWriteOut(ctx, stdout, '\n') if (suspended && typeof ctx.resumeReplAfterSubprocess === 'function') { try { ctx.resumeReplAfterSubprocess() } catch { /* ignore */ } } } }