/** Shared agent tool helpers (preamble for /bin/agent; schemas/dispatch are sibling files). */ /** * Host-checkout verification hints (keep loosely aligned with scripts/lib/agent-check-hints-data.mjs). * @param {string} combined paths + topic * @returns {string[]} */ function bareAgentVerificationHintsList(combined) { const c = String(combined || '').toLowerCase() /** @type {string[]} */ const hints = [] if (/(^|\/)kernel\/|kernel\\|\/boot\/init|lib\/init|lib\\init/.test(c)) { hints.push('npm run bundle:kernel', 'node scripts/verify-kernel-seeder-parity.mjs') } if (/bare-os-coreutils|kernel\/bin|kernel\\bin/.test(c)) { hints.push('npm run build -w bare-os-coreutils', 'node scripts/verify-man-coverage.mjs') } if (/bare-os-booter|bare-os-ctx-api/.test(c)) { hints.push( 'npm run test -w bare-os-booter', 'node scripts/verify-ctx-api-feature-bits.mjs' ) } if (/bare-os-protocol|seed-rpc|channel\.js/.test(c)) { hints.push('npm run test -w bare-os-protocol') } if (/bare-os-seeder/.test(c)) { hints.push('node scripts/verify-kernel-seeder-parity.mjs') } if (/bare-os-bare-libs|kernel\/lib\/bare|kernel\\lib\\bare/.test(c)) { hints.push('npm run build -w bare-os-bare-libs', 'node scripts/verify-bundle-health.mjs') } if (/shell|sh\.js|test\.js/.test(c) && /booter/.test(c)) { hints.push('npm run test:shell-fast') } if (/docs\/|handbook\/|developer-guide\//.test(c)) { hints.push('npm run pretest', 'node scripts/verify-doc-links.mjs') } if (!hints.length) hints.push('npm run pretest', 'npm test') return [...new Set(hints)] } /** * @param {string} s */ function bareAgentShellQuote(s) { return "'" + String(s).replace(/'/g, "'\\''") + "'" } /** * @param {unknown} v * @returns {string} */ function bareAgentJsonResult(v) { try { return JSON.stringify(v) } catch { return '{"error":"json_stringify_failed"}' } } /** * @param {Record} ctx * @param {string} absPath */ function bareAgentPathAllowed(absPath) { if (typeof bareAgentPathAllowedRead === 'function') { return bareAgentPathAllowedRead(absPath) } const p = String(absPath || '').replace(/\\/g, '/') return Boolean(p.startsWith('/') && !p.includes('..')) } /** * @param {string} command * @param {unknown} denyList */ function bareAgentCommandDeniedByList(command, denyList) { const cmd = String(command || '').toLowerCase() const list = Array.isArray(denyList) ? denyList : [] for (let i = 0; i < list.length; i++) { const needle = String(list[i] || '').trim().toLowerCase() if (needle && cmd.includes(needle)) return needle } return '' } /** * @param {Record} base * @param {Record} patch */ function bareAgentMergeConfigPatch(base, patch) { const out = { ...base } const keys = [ 'backend', 'rest_base_url', 'rest_api_key', 'model', 'qvac_model', 'qvac_profile', 'qvac_ctx_size', 'qvac_device', 'qvac_main_gpu', 'qvac_gpu_layers', 'max_tokens', 'temperature', 'provider', 'max_iterations', 'stream', 'tool_parallelism', 'request_timeout_ms', 'access_policy', 'allow_delete', 'require_confirm_token', 'command_deny', 'mutate_deny_prefixes', 'owner_name', 'agent_label', 'show_reasoning', 'reasoning_mode', 'reasoning_max_chars', 'reasoning_include_tools', 'allow_bridge_mutations', 'allow_host_notifications', 'allow_host_actions', 'emergency_stop_mutations', 'autonomous_mode_enabled', 'autonomous_max_runtime_ms', 'autonomous_completion_required_checks', 'autonomous_allow_paths', 'autonomous_deny_ops', 'autonomous_active', 'autonomous_started_at_ms', 'autonomous_stop_requested', 'autonomous_goal', 'autonomous_status', 'autonomous_last_error', 'context_compaction', 'compaction_keep_recent', 'compaction_tool_chars', 'plan_mode_active', 'todo_nudge_enabled' ] const numKeys = new Set([ 'max_tokens', 'temperature', 'max_iterations', 'tool_parallelism', 'request_timeout_ms', 'reasoning_max_chars', 'compaction_keep_recent', 'compaction_tool_chars', 'qvac_ctx_size', 'qvac_gpu_layers', 'autonomous_max_runtime_ms', 'autonomous_started_at_ms' ]) for (const k of keys) { if (Object.prototype.hasOwnProperty.call(patch, k)) { /** @type {unknown} */ const v = patch[k] if (numKeys.has(k)) { const n = Number(v) if (Number.isFinite(n)) out[k] = n } else if ( k === 'autonomous_completion_required_checks' || k === 'autonomous_allow_paths' || k === 'autonomous_deny_ops' || k === 'command_deny' || k === 'mutate_deny_prefixes' ) { out[k] = Array.isArray(v) ? v.map((x) => String(x ?? '')).filter(Boolean) : out[k] } else if ( k === 'stream' || k === 'allow_delete' || k === 'show_reasoning' || k === 'reasoning_include_tools' || k === 'allow_bridge_mutations' || k === 'allow_host_notifications' || k === 'allow_host_actions' || k === 'emergency_stop_mutations' || k === 'autonomous_mode_enabled' || k === 'autonomous_active' || k === 'autonomous_stop_requested' || k === 'plan_mode_active' || k === 'todo_nudge_enabled' ) { out[k] = Boolean(v) } else if (k === 'reasoning_mode') { const mode = String(v ?? '').trim().toLowerCase() out[k] = mode === 'summary' || mode === 'trace' ? mode : 'off' } else if (k === 'context_compaction') { const mode = String(v ?? '').trim().toLowerCase() out[k] = mode === 'off' || mode === 'aggressive' ? mode : 'auto' } else if (k === 'require_confirm_token') { out[k] = String(v ?? '') } else if (k === 'access_policy') { const pol = String(v ?? '').trim().toLowerCase() out[k] = pol === 'restricted' ? 'restricted' : 'full' } else { out[k] = String(v ?? '') } } } if ( patch.extra_headers && typeof patch.extra_headers === 'object' && !Array.isArray(patch.extra_headers) ) { out.extra_headers = { .../** @type {Record} */ (patch.extra_headers) } } return out } /** * @param {Record} ctx * @param {{ config: string }} paths * @param {Record} config */ async function bareAgentSaveConfigFromTools(ctx, paths, config) { if (typeof bareAgentSaveConfig === 'function') { await bareAgentSaveConfig(ctx, paths, config) return } const vfs = ctx.vfs if (!vfs?.writeFile) return const json = JSON.stringify(config, null, 2) + '\n' const body = typeof ctx.b4a !== 'undefined' && ctx.b4a && typeof ctx.b4a.from === 'function' ? ctx.b4a.from(json) : new TextEncoder().encode(json) await vfs.writeFile(paths.config, body) }