feat(agent): expand ops/developer/bridge tools and harden xAI compatibility
Add second-wave agent capabilities for initd/journal/network/IPC diagnostics, allowlisted maintenance + contract-check automation, and policy-gated HRPC bridge actions with emergency-stop controls. Harden xAI provider support by applying provider-aware request defaults, parsing reasoning-summary/tool-call streaming variants, and updating seeded docs/skills/tests to reflect max-autonomy safeguards and trace-first observability.
This commit is contained in:
+644
-6
@@ -1096,7 +1096,11 @@ function bareAgentDefaultConfig() {
|
||||
show_reasoning: false,
|
||||
reasoning_mode: 'off',
|
||||
reasoning_max_chars: 4000,
|
||||
reasoning_include_tools: true
|
||||
reasoning_include_tools: true,
|
||||
allow_bridge_mutations: false,
|
||||
allow_host_notifications: false,
|
||||
allow_host_actions: false,
|
||||
emergency_stop_mutations: false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1134,7 +1138,11 @@ function bareAgentMergeConfig(defaults, src) {
|
||||
'show_reasoning',
|
||||
'reasoning_mode',
|
||||
'reasoning_max_chars',
|
||||
'reasoning_include_tools'
|
||||
'reasoning_include_tools',
|
||||
'allow_bridge_mutations',
|
||||
'allow_host_notifications',
|
||||
'allow_host_actions',
|
||||
'emergency_stop_mutations'
|
||||
])
|
||||
for (const k of Object.keys(src)) {
|
||||
if (k.startsWith('x-')) continue
|
||||
@@ -1177,7 +1185,11 @@ function bareAgentMergeConfig(defaults, src) {
|
||||
k === 'stream' ||
|
||||
k === 'allow_delete' ||
|
||||
k === 'show_reasoning' ||
|
||||
k === 'reasoning_include_tools'
|
||||
k === 'reasoning_include_tools' ||
|
||||
k === 'allow_bridge_mutations' ||
|
||||
k === 'allow_host_notifications' ||
|
||||
k === 'allow_host_actions' ||
|
||||
k === 'emergency_stop_mutations'
|
||||
) {
|
||||
out[k] = Boolean(val)
|
||||
continue
|
||||
@@ -1216,7 +1228,11 @@ function bareAgentValidateConfigShape(raw) {
|
||||
'show_reasoning',
|
||||
'reasoning_mode',
|
||||
'reasoning_max_chars',
|
||||
'reasoning_include_tools'
|
||||
'reasoning_include_tools',
|
||||
'allow_bridge_mutations',
|
||||
'allow_host_notifications',
|
||||
'allow_host_actions',
|
||||
'emergency_stop_mutations'
|
||||
]
|
||||
if (!known.includes(k)) {
|
||||
throw new Error('unknown config key: ' + k)
|
||||
@@ -1517,6 +1533,7 @@ var BARE_AGENT_SKILL_SEED_REL = Object.freeze([
|
||||
'skills/bare-os-kernel-proc/SKILL.md',
|
||||
'skills/bare-os-super-developer/SKILL.md',
|
||||
'skills/agent-ops/SKILL.md',
|
||||
'skills/xai-compat/SKILL.md',
|
||||
'skills/holesail/SKILL.md',
|
||||
'skills/hdms/SKILL.md'
|
||||
])
|
||||
@@ -2064,6 +2081,7 @@ async function bareAgentStreamChatCompletions(opts) {
|
||||
const reader = stream.getReader()
|
||||
const dec = new TextDecoder()
|
||||
let buf = ''
|
||||
let emittedShape = false
|
||||
try {
|
||||
for (;;) {
|
||||
const { done, value } = await reader.read()
|
||||
@@ -2084,6 +2102,30 @@ async function bareAgentStreamChatCompletions(opts) {
|
||||
if (parsed.kind !== 'json' || !parsed.value || typeof parsed.value !== 'object')
|
||||
continue
|
||||
const j = /** @type {Record<string, unknown>} */ (parsed.value)
|
||||
if (!emittedShape) {
|
||||
emittedShape = true
|
||||
onEvent({
|
||||
type: 'response_shape_keys',
|
||||
keys: Object.keys(j).slice(0, 24)
|
||||
})
|
||||
}
|
||||
if (typeof j.type === 'string') {
|
||||
if (j.type === 'response.reasoning_summary_text.delta' && typeof j.delta === 'string') {
|
||||
onEvent({ type: 'delta_reasoning', reasoning: j.delta })
|
||||
}
|
||||
if (j.type === 'response.output_text.delta' && typeof j.delta === 'string') {
|
||||
onEvent({ type: 'delta_content', content: j.delta })
|
||||
}
|
||||
if (
|
||||
j.type === 'response.function_call_arguments.delta' &&
|
||||
typeof j.delta === 'string'
|
||||
) {
|
||||
onEvent({
|
||||
type: 'delta_tool_calls',
|
||||
tool_calls: [{ index: 0, function: { arguments: j.delta } }]
|
||||
})
|
||||
}
|
||||
}
|
||||
const choices = bareAgentDeepGet(j, 'choices')
|
||||
const ch0 =
|
||||
Array.isArray(choices) && choices[0] && typeof choices[0] === 'object'
|
||||
@@ -3456,6 +3498,201 @@ function bareAgentToolDefinitions() {
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'get_initd_graph',
|
||||
description:
|
||||
'Read initd dependency DAG/readiness graph from /proc/bare_os/initd_dag.json and /proc/bare_os/initd_readiness.json.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'read_unit_journal',
|
||||
description:
|
||||
'Read a bounded/redacted tail of /run/bare-os/unit-journal/<unit>.ndjson.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
unit: { type: 'string', description: 'Initd unit name, e.g. bare-cron' },
|
||||
max_chars: { type: 'integer', description: 'Maximum output chars (default 12000)' },
|
||||
tail_only: { type: 'boolean', description: 'Return trailing max_chars only' }
|
||||
},
|
||||
required: ['unit']
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'inspect_ipc_backpressure',
|
||||
description:
|
||||
'Inspect IPC/backpressure operator snapshots from /proc/bare_os JSON surfaces.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'get_network_summary',
|
||||
description:
|
||||
'Read a typed network/swarm summary from /proc/bare_os surfaces with best-effort fallback paths.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'tail_telemetry_streams',
|
||||
description:
|
||||
'Read bounded/redacted tails from telemetry logs such as /var/log/bare-os/audit.log, logger.jsonl, and initd logs.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
max_chars: { type: 'integer', description: 'Maximum chars per stream (default 8000)' }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'pkg_index_lookup',
|
||||
description:
|
||||
'Run pkg-swarm-index lookup and return parsed output for one package key.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
key: { type: 'string', description: 'Package key to lookup' }
|
||||
},
|
||||
required: ['key']
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'list_verification_scripts',
|
||||
description:
|
||||
'List known verification scripts from /scripts and summarize likely check families.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'run_maintenance_gate',
|
||||
description:
|
||||
'Run one allowlisted maintenance command with bounded capture for automation workflows.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
command: { type: 'string', description: 'Allowlisted command id' },
|
||||
cwd: { type: 'string', description: 'Optional working directory' },
|
||||
timeout_ms: { type: 'integer', description: 'Timeout in milliseconds' }
|
||||
},
|
||||
required: ['command']
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'run_contract_checks',
|
||||
description:
|
||||
'Run a grouped set of contract checks by profile id (allowlisted) with bounded output.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
profile: { type: 'string', description: 'Check profile id, e.g. core, docs, parity' }
|
||||
},
|
||||
required: ['profile']
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'summarize_build_drift',
|
||||
description:
|
||||
'Summarize build/generated drift by comparing git status and key generated artifacts.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'get_hrpc_bridge_health',
|
||||
description:
|
||||
'Read HRPC bridge/operator health from /proc/bare_os surfaces and include host capability hints when available.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'get_hrpc_allowlist_status',
|
||||
description:
|
||||
'Inspect effective HRPC allowlist/probe status using hrpc probe and operator snapshots.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'emit_host_notification',
|
||||
description:
|
||||
'Request an audited host notification via HRPC route (policy-gated; disabled by default).',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
title: { type: 'string' },
|
||||
message: { type: 'string' },
|
||||
level: { type: 'string', enum: ['info', 'warn', 'error'] }
|
||||
},
|
||||
required: ['title', 'message']
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'request_host_action',
|
||||
description:
|
||||
'Request a schema-validated host action through HRPC (policy-gated; disabled by default).',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
action: { type: 'string', description: 'Host action id' },
|
||||
payload: { type: 'object', description: 'Action payload object' }
|
||||
},
|
||||
required: ['action']
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
@@ -3625,6 +3862,46 @@ async function bareAgentDispatchTool(o) {
|
||||
return { ok: true, stdout_stderr: captured }
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} text
|
||||
*/
|
||||
function redactSensitiveText(text) {
|
||||
return String(text || '')
|
||||
.replace(/\bBearer\s+\S+/gi, 'Bearer <redacted>')
|
||||
.replace(/\bsk-[a-zA-Z0-9]{16,}\b/g, '<redacted_sk>')
|
||||
.replace(
|
||||
/\b([A-Z0-9_]*(KEY|TOKEN|SECRET|PASS|PASSWORD)[A-Z0-9_]*)\s*=\s*([^\s]+)/gi,
|
||||
'$1=<redacted>'
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} path
|
||||
* @param {number} maxChars
|
||||
* @param {boolean} tailOnly
|
||||
* @param {boolean} redact
|
||||
*/
|
||||
async function readBoundedText(path, maxChars, tailOnly, redact) {
|
||||
if (!vfs || typeof vfs.readFile !== 'function') {
|
||||
return { ok: false, error: 'vfs unavailable' }
|
||||
}
|
||||
try {
|
||||
const b = await vfs.readFile(path)
|
||||
if (!b || !b.length) return { ok: false, error: 'empty_or_missing' }
|
||||
let t =
|
||||
typeof ctx.b4a !== 'undefined' && ctx.b4a && typeof ctx.b4a.toString === 'function'
|
||||
? ctx.b4a.toString(b)
|
||||
: String(new TextDecoder().decode(b))
|
||||
if (redact) t = redactSensitiveText(t)
|
||||
const out = tailOnly ? t.slice(-maxChars) : bareAgentTruncateChars(t, maxChars)
|
||||
return { ok: true, path, text: out, truncated: t.length > out.length }
|
||||
} catch (e) {
|
||||
const msg =
|
||||
e && typeof e === 'object' && 'message' in e ? String(e.message) : String(e)
|
||||
return { ok: false, error: msg }
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (toolName === 'read_skill') {
|
||||
const skill = typeof args.skill === 'string' ? args.skill.trim() : ''
|
||||
@@ -4456,6 +4733,311 @@ async function bareAgentDispatchTool(o) {
|
||||
}
|
||||
}
|
||||
|
||||
if (toolName === 'get_initd_graph') {
|
||||
appendProgress('get_initd_graph')
|
||||
if (!vfs || typeof vfs.readFile !== 'function') {
|
||||
return bareAgentJsonResult({ ok: false, error: 'vfs unavailable' })
|
||||
}
|
||||
/** @type {Record<string, unknown>} */
|
||||
const out = { ok: true }
|
||||
for (const p of ['/proc/bare_os/initd_dag.json', '/proc/bare_os/initd_readiness.json']) {
|
||||
try {
|
||||
const b = await vfs.readFile(p)
|
||||
if (!b || !b.length) continue
|
||||
const t =
|
||||
typeof ctx.b4a !== 'undefined' && ctx.b4a && typeof ctx.b4a.toString === 'function'
|
||||
? ctx.b4a.toString(b)
|
||||
: String(new TextDecoder().decode(b))
|
||||
try {
|
||||
out[p] = JSON.parse(t)
|
||||
} catch {
|
||||
out[p] = bareAgentTruncateChars(t, 8000)
|
||||
}
|
||||
} catch {
|
||||
/* ignore missing */
|
||||
}
|
||||
}
|
||||
return bareAgentJsonResult(out)
|
||||
}
|
||||
|
||||
if (toolName === 'read_unit_journal') {
|
||||
const unit = typeof args.unit === 'string' ? args.unit.trim() : ''
|
||||
if (!/^[a-zA-Z0-9._-]{1,96}$/.test(unit)) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'invalid_unit' })
|
||||
}
|
||||
const maxChars =
|
||||
typeof args.max_chars === 'number' && Number.isFinite(args.max_chars)
|
||||
? Math.min(Math.max(Math.floor(args.max_chars), 200), 120_000)
|
||||
: 12_000
|
||||
const tailOnly = Boolean(args.tail_only)
|
||||
const p = '/run/bare-os/unit-journal/' + unit + '.ndjson'
|
||||
appendProgress('read_unit_journal ' + unit)
|
||||
return bareAgentJsonResult(await readBoundedText(p, maxChars, tailOnly, true))
|
||||
}
|
||||
|
||||
if (toolName === 'inspect_ipc_backpressure') {
|
||||
appendProgress('inspect_ipc_backpressure')
|
||||
if (!vfs || typeof vfs.readFile !== 'function') {
|
||||
return bareAgentJsonResult({ ok: false, error: 'vfs unavailable' })
|
||||
}
|
||||
const candidates = [
|
||||
'/proc/bare_os/ipc_backpressure.json',
|
||||
'/proc/bare_os/replication_operator_sketch.json',
|
||||
'/proc/bare_os/metrics_live.json'
|
||||
]
|
||||
/** @type {Record<string, unknown>} */
|
||||
const out = { ok: true, sources: [] }
|
||||
for (const p of candidates) {
|
||||
try {
|
||||
const b = await vfs.readFile(p)
|
||||
if (!b || !b.length) continue
|
||||
const t =
|
||||
typeof ctx.b4a !== 'undefined' && ctx.b4a && typeof ctx.b4a.toString === 'function'
|
||||
? ctx.b4a.toString(b)
|
||||
: String(new TextDecoder().decode(b))
|
||||
out.sources.push(p)
|
||||
try {
|
||||
out[p] = JSON.parse(t)
|
||||
} catch {
|
||||
out[p] = bareAgentTruncateChars(t, 6000)
|
||||
}
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
return bareAgentJsonResult(out)
|
||||
}
|
||||
|
||||
if (toolName === 'get_network_summary') {
|
||||
appendProgress('get_network_summary')
|
||||
if (!vfs || typeof vfs.readFile !== 'function') {
|
||||
return bareAgentJsonResult({ ok: false, error: 'vfs unavailable' })
|
||||
}
|
||||
const candidates = [
|
||||
'/proc/bare_os/net_summary.json',
|
||||
'/proc/bare_os/swarm.json',
|
||||
'/proc/bare_os/swarm_status.json',
|
||||
'/proc/bare_os/swarm_connection_manager_status.json'
|
||||
]
|
||||
/** @type {Record<string, unknown>} */
|
||||
const out = { ok: true, sources: [] }
|
||||
for (const p of candidates) {
|
||||
try {
|
||||
const b = await vfs.readFile(p)
|
||||
if (!b || !b.length) continue
|
||||
const t =
|
||||
typeof ctx.b4a !== 'undefined' && ctx.b4a && typeof ctx.b4a.toString === 'function'
|
||||
? ctx.b4a.toString(b)
|
||||
: String(new TextDecoder().decode(b))
|
||||
out.sources.push(p)
|
||||
try {
|
||||
out[p] = JSON.parse(t)
|
||||
} catch {
|
||||
out[p] = bareAgentTruncateChars(t, 6000)
|
||||
}
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
return bareAgentJsonResult(out)
|
||||
}
|
||||
|
||||
if (toolName === 'tail_telemetry_streams') {
|
||||
const maxChars =
|
||||
typeof args.max_chars === 'number' && Number.isFinite(args.max_chars)
|
||||
? Math.min(Math.max(Math.floor(args.max_chars), 200), 60_000)
|
||||
: 8000
|
||||
appendProgress('tail_telemetry_streams')
|
||||
const pathsToRead = [
|
||||
'/var/log/bare-os/audit.log',
|
||||
'/var/log/bare-os/logger.jsonl',
|
||||
'/var/log/bare-os/initd.log',
|
||||
'/var/log/bare-os/cron.log'
|
||||
]
|
||||
/** @type {Record<string, unknown>} */
|
||||
const out = { ok: true, streams: {} }
|
||||
for (const p of pathsToRead) {
|
||||
out.streams[p] = await readBoundedText(p, maxChars, true, true)
|
||||
}
|
||||
return bareAgentJsonResult(out)
|
||||
}
|
||||
|
||||
if (toolName === 'pkg_index_lookup') {
|
||||
const key = typeof args.key === 'string' ? args.key.trim() : ''
|
||||
if (!key) return bareAgentJsonResult({ ok: false, error: 'key_required' })
|
||||
appendProgress('pkg_index_lookup ' + key.slice(0, 80))
|
||||
if (!execLine) return bareAgentJsonResult({ ok: false, error: 'execLine unavailable' })
|
||||
const cmd = 'pkg-swarm-index get --key ' + bareAgentShellQuote(key)
|
||||
const r = await captureExec(cmd, 90000)
|
||||
if (r.ok === false) return bareAgentJsonResult(r)
|
||||
const txt = typeof r.stdout_stderr === 'string' ? r.stdout_stderr : ''
|
||||
let json = null
|
||||
try {
|
||||
json = JSON.parse(txt)
|
||||
} catch {
|
||||
json = null
|
||||
}
|
||||
return bareAgentJsonResult({ ok: true, key, json, text: bareAgentTruncateChars(txt, 12000) })
|
||||
}
|
||||
|
||||
if (toolName === 'list_verification_scripts') {
|
||||
appendProgress('list_verification_scripts')
|
||||
if (!vfs || typeof vfs.readdir !== 'function') {
|
||||
return bareAgentJsonResult({ ok: false, error: 'vfs unavailable' })
|
||||
}
|
||||
const out = []
|
||||
for (const dir of ['/scripts', '/home/guest/scripts']) {
|
||||
try {
|
||||
const names = await vfs.readdir(dir)
|
||||
const rows = names
|
||||
.filter((n) => typeof n === 'string' && (n.endsWith('.mjs') || n.endsWith('.js')))
|
||||
.sort()
|
||||
.slice(0, 400)
|
||||
.map((n) => dir + '/' + n)
|
||||
out.push(...rows)
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
return bareAgentJsonResult({ ok: true, scripts: out })
|
||||
}
|
||||
|
||||
if (toolName === 'run_maintenance_gate') {
|
||||
const command = typeof args.command === 'string' ? args.command.trim() : ''
|
||||
const timeoutMs =
|
||||
typeof args.timeout_ms === 'number' && Number.isFinite(args.timeout_ms)
|
||||
? Math.min(Math.max(Math.floor(args.timeout_ms), 1000), 900000)
|
||||
: 180000
|
||||
appendProgress('run_maintenance_gate ' + command)
|
||||
const allow = {
|
||||
'verify-kernel-seeder-parity': 'node scripts/verify-kernel-seeder-parity.mjs',
|
||||
'verify-man-coverage': 'node scripts/verify-man-coverage.mjs',
|
||||
'verify-ctx-api-feature-bits': 'node scripts/verify-ctx-api-feature-bits.mjs',
|
||||
'coreutils-test': 'npm test -w bare-os-coreutils'
|
||||
}
|
||||
if (!Object.prototype.hasOwnProperty.call(allow, command)) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'command_not_allowlisted', allowlist: Object.keys(allow) })
|
||||
}
|
||||
if (!execLine) return bareAgentJsonResult({ ok: false, error: 'execLine unavailable' })
|
||||
const cmd = /** @type {Record<string, string>} */ (allow)[command]
|
||||
const r = await captureExec(cmd, timeoutMs, { captureExit: true })
|
||||
return bareAgentJsonResult(r)
|
||||
}
|
||||
|
||||
if (toolName === 'run_contract_checks') {
|
||||
const profile = typeof args.profile === 'string' ? args.profile.trim() : ''
|
||||
appendProgress('run_contract_checks ' + profile)
|
||||
const mapping = {
|
||||
core: 'node scripts/verify-kernel-seeder-parity.mjs && node scripts/verify-man-coverage.mjs',
|
||||
docs: 'node scripts/verify-doc-links.mjs && node scripts/verify-doc-contracts.mjs',
|
||||
parity: 'node scripts/verify-kernel-seeder-parity.mjs'
|
||||
}
|
||||
if (!Object.prototype.hasOwnProperty.call(mapping, profile)) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'unknown_profile', profiles: Object.keys(mapping) })
|
||||
}
|
||||
if (!execLine) return bareAgentJsonResult({ ok: false, error: 'execLine unavailable' })
|
||||
const cmd = /** @type {Record<string, string>} */ (mapping)[profile]
|
||||
const r = await captureExec(cmd, 300000, { captureExit: true })
|
||||
return bareAgentJsonResult(r)
|
||||
}
|
||||
|
||||
if (toolName === 'summarize_build_drift') {
|
||||
appendProgress('summarize_build_drift')
|
||||
if (!execLine) return bareAgentJsonResult({ ok: false, error: 'execLine unavailable' })
|
||||
const r = await captureExec('git status --short', 60000)
|
||||
return bareAgentJsonResult(r)
|
||||
}
|
||||
|
||||
if (toolName === 'get_hrpc_bridge_health') {
|
||||
appendProgress('get_hrpc_bridge_health')
|
||||
/** @type {Record<string, unknown>} */
|
||||
const out = {
|
||||
ok: true,
|
||||
hostCapabilities: {
|
||||
hrpcBridge:
|
||||
typeof ctx.bareOsHostCapability === 'function'
|
||||
? Boolean(ctx.bareOsHostCapability('hrpcBridge'))
|
||||
: false
|
||||
}
|
||||
}
|
||||
if (vfs && typeof vfs.readFile === 'function') {
|
||||
for (const p of ['/proc/bare_os/hrpc_route_table.json', '/proc/bare_os/hrpc_health.json']) {
|
||||
try {
|
||||
const b = await vfs.readFile(p)
|
||||
if (!b || !b.length) continue
|
||||
const t =
|
||||
typeof ctx.b4a !== 'undefined' && ctx.b4a && typeof ctx.b4a.toString === 'function'
|
||||
? ctx.b4a.toString(b)
|
||||
: String(new TextDecoder().decode(b))
|
||||
try {
|
||||
out[p] = JSON.parse(t)
|
||||
} catch {
|
||||
out[p] = bareAgentTruncateChars(t, 6000)
|
||||
}
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
}
|
||||
return bareAgentJsonResult(out)
|
||||
}
|
||||
|
||||
if (toolName === 'get_hrpc_allowlist_status') {
|
||||
appendProgress('get_hrpc_allowlist_status')
|
||||
if (!execLine) return bareAgentJsonResult({ ok: false, error: 'execLine unavailable' })
|
||||
const r = await captureExec('hrpc probe', 60000)
|
||||
return bareAgentJsonResult(r)
|
||||
}
|
||||
|
||||
if (toolName === 'emit_host_notification' || toolName === 'request_host_action') {
|
||||
const cfg = configRef.current || {}
|
||||
if (cfg && cfg.emergency_stop_mutations) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'emergency_stop_mutations_enabled' })
|
||||
}
|
||||
if (toolName === 'emit_host_notification' && !cfg.allow_host_notifications) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'host_notifications_disabled' })
|
||||
}
|
||||
if (toolName === 'request_host_action' && !cfg.allow_host_actions) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'host_actions_disabled' })
|
||||
}
|
||||
if (!cfg.allow_bridge_mutations) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'bridge_mutations_disabled' })
|
||||
}
|
||||
if (typeof ctx.bareOsHrpcRequest !== 'function') {
|
||||
return bareAgentJsonResult({ ok: false, error: 'bareOsHrpcRequest unavailable' })
|
||||
}
|
||||
try {
|
||||
if (toolName === 'emit_host_notification') {
|
||||
const payload = {
|
||||
title: String(args.title || '').slice(0, 200),
|
||||
message: String(args.message || '').slice(0, 2000),
|
||||
level: typeof args.level === 'string' ? args.level : 'info'
|
||||
}
|
||||
appendProgress('emit_host_notification ' + payload.title)
|
||||
const res = await ctx.bareOsHrpcRequest('bare_os', 'host_notify', payload)
|
||||
return bareAgentJsonResult({ ok: true, result: res })
|
||||
}
|
||||
const action = String(args.action || '').trim()
|
||||
if (!/^[a-zA-Z0-9._-]{1,64}$/.test(action)) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'invalid_action' })
|
||||
}
|
||||
const payload =
|
||||
args.payload && typeof args.payload === 'object' && !Array.isArray(args.payload)
|
||||
? args.payload
|
||||
: {}
|
||||
appendProgress('request_host_action ' + action)
|
||||
const res = await ctx.bareOsHrpcRequest('bare_os', 'host_action', {
|
||||
action,
|
||||
payload
|
||||
})
|
||||
return bareAgentJsonResult({ ok: true, result: res })
|
||||
} catch (e) {
|
||||
const msg =
|
||||
e && typeof e === 'object' && 'message' in e ? String(e.message) : String(e)
|
||||
return bareAgentJsonResult({ ok: false, error: msg })
|
||||
}
|
||||
}
|
||||
|
||||
if (toolName === 'get_swarm_peers') {
|
||||
appendProgress('get_swarm_peers')
|
||||
if (!vfs || typeof vfs.readFile !== 'function') {
|
||||
@@ -4602,7 +5184,11 @@ function bareAgentMergeConfigPatch(base, patch) {
|
||||
'show_reasoning',
|
||||
'reasoning_mode',
|
||||
'reasoning_max_chars',
|
||||
'reasoning_include_tools'
|
||||
'reasoning_include_tools',
|
||||
'allow_bridge_mutations',
|
||||
'allow_host_notifications',
|
||||
'allow_host_actions',
|
||||
'emergency_stop_mutations'
|
||||
]
|
||||
const numKeys = new Set([
|
||||
'max_tokens',
|
||||
@@ -4623,7 +5209,11 @@ function bareAgentMergeConfigPatch(base, patch) {
|
||||
k === 'stream' ||
|
||||
k === 'allow_delete' ||
|
||||
k === 'show_reasoning' ||
|
||||
k === 'reasoning_include_tools'
|
||||
k === 'reasoning_include_tools' ||
|
||||
k === 'allow_bridge_mutations' ||
|
||||
k === 'allow_host_notifications' ||
|
||||
k === 'allow_host_actions' ||
|
||||
k === 'emergency_stop_mutations'
|
||||
) {
|
||||
out[k] = Boolean(v)
|
||||
} else if (k === 'reasoning_mode') {
|
||||
@@ -4937,6 +5527,29 @@ function bareAgentReasoningSettings(cfg) {
|
||||
return { enabled, mode, maxChars, includeTools }
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Record<string, unknown>} cfg
|
||||
*/
|
||||
function bareAgentApplyProviderProfile(cfg) {
|
||||
const out = { ...cfg }
|
||||
const provider = String(out.provider || '').trim().toLowerCase()
|
||||
const model = String(out.model || '').trim().toLowerCase()
|
||||
const defaultGroq = 'https://api.groq.com/openai/v1'
|
||||
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<string, unknown>} ctx
|
||||
* @param {string} argv0
|
||||
@@ -5073,6 +5686,7 @@ 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,
|
||||
@@ -5327,6 +5941,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
}
|
||||
|
||||
/** @type {Record<string, unknown>} */
|
||||
const providerNow = String(configRef.current.provider || '').trim().toLowerCase()
|
||||
const body = {
|
||||
model: String(configRef.current.model || ''),
|
||||
stream: Boolean(configRef.current.stream !== false),
|
||||
@@ -5336,6 +5951,24 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
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 (reasoningSettings.enabled && reasoningSettings.mode === 'trace') {
|
||||
bareAgentWriteOut(
|
||||
ctx,
|
||||
stdout,
|
||||
bareEditSgr('dim', useColor) +
|
||||
'\n[process] request provider=' +
|
||||
(providerNow || 'unknown') +
|
||||
' model=' +
|
||||
String(configRef.current.model || '') +
|
||||
EDIT_ANSI_RESET +
|
||||
'\n'
|
||||
)
|
||||
}
|
||||
|
||||
let assistantContent = ''
|
||||
/** @type {Map<number, { id: string, name: string, args: string }>} */
|
||||
@@ -5381,6 +6014,11 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
)
|
||||
}
|
||||
}
|
||||
} 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_reason') {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"schema": 2,
|
||||
"profileId": "bare-os-posix-like",
|
||||
"generatedAt": "2026-04-26T07:05:45.727Z",
|
||||
"generatedAt": "2026-04-26T08:56:11.752Z",
|
||||
"note": "Sparse POSIX Issue 7 coverage hints for /bin utilities. Omitted command names are not yet profiled here.",
|
||||
"commandIndex": [
|
||||
{
|
||||
@@ -16,10 +16,6 @@
|
||||
"name": "awk",
|
||||
"tier": "tier1_bin"
|
||||
},
|
||||
{
|
||||
"name": "bare-sshd",
|
||||
"tier": "tier1_bin"
|
||||
},
|
||||
{
|
||||
"name": "baresay",
|
||||
"tier": "tier1_bin"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"atMs": 1777187145726,
|
||||
"atMs": 1777193771751,
|
||||
"commands": [
|
||||
"agent",
|
||||
"arch",
|
||||
|
||||
@@ -19,3 +19,7 @@ Enable process visibility with `edit_agent_config` (`show_reasoning=true`, `reas
|
||||
## /reasoning-off
|
||||
|
||||
Disable reasoning/process output with `edit_agent_config` (`show_reasoning=false`, `reasoning_mode=off`).
|
||||
|
||||
## /xai-debug
|
||||
|
||||
Validate xAI compatibility settings, stream behavior, reasoning summary visibility, and tool-call handling. Recommend `reasoning_mode=trace` when summaries are absent.
|
||||
|
||||
@@ -22,7 +22,7 @@ This tree follows the **agent** Markdown workspace convention: “soul” files
|
||||
3. During a session, the model loads the full document with the **`read_skill`** tool (do not paste huge skills into the user channel unless asked).
|
||||
4. Shared skills can live under **`~/.agent/skills/`**; keep **`workspace/skills/`** for machine-local or repo-specific behavior.
|
||||
|
||||
Seeded examples in this repo (under **`skills/`**): **`p2p-os-status`**, **`bare-os-kernel-proc`**, **`bare-os-super-developer`**, **`agent-ops`**, **`holesail`** (managed **`state.json`**, **`seed`**/**`key`**, stock **`bare-www-*`** / **`bare-ssh-*`**), and **`hdms`** (Hyperdrive mounts and invite/pair).
|
||||
Seeded examples in this repo (under **`skills/`**): **`p2p-os-status`**, **`bare-os-kernel-proc`**, **`bare-os-super-developer`**, **`agent-ops`**, **`xai-compat`**, **`holesail`** (managed **`state.json`**, **`seed`**/**`key`**, stock **`bare-www-*`** / **`bare-ssh-*`**), and **`hdms`** (Hyperdrive mounts and invite/pair).
|
||||
|
||||
After **`agent --config`** / **`--setup`** (or changing **`owner_name`** / **`agent_label`** via **`edit_agent_config`**), **`IDENTITY.md`** and **`USER.md`** are regenerated from **`config.json`** so the workspace matches the operator and agent label.
|
||||
|
||||
@@ -33,6 +33,13 @@ Reasoning/process visibility is configurable in `config.json`:
|
||||
- `reasoning_max_chars` — bounded reasoning output
|
||||
- `reasoning_include_tools` — include tool traces in process stream
|
||||
|
||||
Max-autonomy bridge policy switches are also in `config.json`:
|
||||
|
||||
- `allow_bridge_mutations` — master mutation gate
|
||||
- `allow_host_notifications` — notification route gate
|
||||
- `allow_host_actions` — host action route gate
|
||||
- `emergency_stop_mutations` — kill switch for mutating bridge tools
|
||||
|
||||
## Editing
|
||||
|
||||
1. Change files under **`~/.agent/workspace/`** on your **personal** drive.
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
- **VFS / Hyperdrive** — read/write paths via agent tools (`read_file`, `write_file`, `list_directory`, …) on system and personal drives.
|
||||
- **Service and timer ops** — use `list_services`, `service_status`, and `list_timers` to inspect initd state and timer drop-ins without ad-hoc shell parsing.
|
||||
- **Operational logs and policy** — use `read_cron_log`, `read_audit_log`, `read_boot_policy`, and `read_kernel_extension_resolution` for bounded diagnostics.
|
||||
- **Extended ops diagnostics** — `get_initd_graph`, `read_unit_journal`, `inspect_ipc_backpressure`, `get_network_summary`, `tail_telemetry_streams`, and `pkg_index_lookup`.
|
||||
- **Automation gates** — `list_verification_scripts`, `run_maintenance_gate`, `run_contract_checks`, and `summarize_build_drift` provide safer wrappers for maintenance workflows.
|
||||
- **Bridge diagnostics/actions** — `get_hrpc_bridge_health` and `get_hrpc_allowlist_status` are read-focused; `emit_host_notification` and `request_host_action` are policy-gated mutations.
|
||||
- **`web_fetch`** — live `http(s)` fetches **only** when the operator allows them (`ctx.httpFetch`, `BARE_OS_HTTP_ALLOWLIST` / denylist). Same policy as delegated `curl` / `wget`.
|
||||
- **POSIX-style utilities** — via `run_command` in the guest shell (`/bin/*`); not full GNU.
|
||||
- **Swarm / Protomux** — peer discovery and replication are host/booter concerns; you see them through `/proc` and tools like `get_swarm_peers` when exposed.
|
||||
@@ -18,6 +21,14 @@
|
||||
- `reasoning_max_chars` (bounded output)
|
||||
- `reasoning_include_tools` (include tool call/result traces in process output)
|
||||
- Use `edit_agent_config` to toggle these safely during a session.
|
||||
- For provider `xai`, keep `rest_base_url` at `https://api.x.ai/v1` and prefer trace mode when reasoning summaries are unavailable.
|
||||
|
||||
## Max-autonomy policy toggles
|
||||
|
||||
- `allow_bridge_mutations`: master gate for bridge mutation tools.
|
||||
- `allow_host_notifications`: required for `emit_host_notification`.
|
||||
- `allow_host_actions`: required for `request_host_action`.
|
||||
- `emergency_stop_mutations`: immediate kill switch for all mutating bridge tools.
|
||||
|
||||
## Skills system
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: xai-compat
|
||||
version: 1.0.0
|
||||
description: Keep agent requests and tool loops compatible with xAI chat/responses semantics, reasoning summaries, and streaming behavior.
|
||||
tags: [xai, compatibility, streaming, tools, reasoning]
|
||||
requires: [edit_agent_config, run_command]
|
||||
---
|
||||
|
||||
# xai-compat
|
||||
|
||||
Use this skill when provider is `xai` or when debugging xAI tool/reasoning behavior.
|
||||
|
||||
## Baseline configuration
|
||||
|
||||
1. Ensure:
|
||||
- `provider = "xai"`
|
||||
- `rest_base_url = "https://api.x.ai/v1"`
|
||||
2. Prefer `reasoning_mode = "trace"` for observability when summaries are sparse.
|
||||
3. Keep `stream = true` for tool-call visibility and progressive diagnostics.
|
||||
|
||||
## Tool calling expectations
|
||||
|
||||
- xAI supports OpenAI-compatible function tools on chat completions.
|
||||
- `parallel_tool_calls` can be enabled; process all returned tool calls before continuing.
|
||||
- Keep tool schemas explicit and bounded.
|
||||
|
||||
## Reasoning expectations
|
||||
|
||||
- Reasoning models may expose `reasoning_content` summaries, but availability can vary by model/endpoint.
|
||||
- In trace mode, rely on local process lines even when no reasoning summary deltas arrive.
|
||||
|
||||
## Debug checklist
|
||||
|
||||
1. Verify provider/base URL in `~/.agent/config.json`.
|
||||
2. Run with `reasoning_mode=trace`.
|
||||
3. Check for tool-call chunks and usage tokens in stream output.
|
||||
4. If reasoning deltas are absent, treat this as provider/model behavior and continue with trace-level progress lines.
|
||||
+1
-20525
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user