add autonomous coding runner with guardrails and completion gates
This commit is contained in:
+404
-12
@@ -1100,7 +1100,18 @@ function bareAgentDefaultConfig() {
|
||||
allow_bridge_mutations: false,
|
||||
allow_host_notifications: false,
|
||||
allow_host_actions: false,
|
||||
emergency_stop_mutations: false
|
||||
emergency_stop_mutations: false,
|
||||
autonomous_mode_enabled: false,
|
||||
autonomous_max_runtime_ms: 1800000,
|
||||
autonomous_completion_required_checks: ['coreutils-test', 'verify-kernel-seeder-parity', 'verify-man-coverage'],
|
||||
autonomous_allow_paths: ['*'],
|
||||
autonomous_deny_ops: ['delete_path', 'request_host_action', 'emit_host_notification'],
|
||||
autonomous_active: false,
|
||||
autonomous_started_at_ms: 0,
|
||||
autonomous_stop_requested: false,
|
||||
autonomous_goal: '',
|
||||
autonomous_status: 'idle',
|
||||
autonomous_last_error: ''
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1142,7 +1153,18 @@ function bareAgentMergeConfig(defaults, src) {
|
||||
'allow_bridge_mutations',
|
||||
'allow_host_notifications',
|
||||
'allow_host_actions',
|
||||
'emergency_stop_mutations'
|
||||
'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'
|
||||
])
|
||||
for (const k of Object.keys(src)) {
|
||||
if (k.startsWith('x-')) continue
|
||||
@@ -1158,7 +1180,10 @@ function bareAgentMergeConfig(defaults, src) {
|
||||
k === 'model' ||
|
||||
k === 'provider' ||
|
||||
k === 'owner_name' ||
|
||||
k === 'agent_label'
|
||||
k === 'agent_label' ||
|
||||
k === 'autonomous_goal' ||
|
||||
k === 'autonomous_status' ||
|
||||
k === 'autonomous_last_error'
|
||||
) {
|
||||
out[k] = String(val ?? '')
|
||||
continue
|
||||
@@ -1175,12 +1200,22 @@ function bareAgentMergeConfig(defaults, src) {
|
||||
k === 'max_iterations' ||
|
||||
k === 'tool_parallelism' ||
|
||||
k === 'request_timeout_ms' ||
|
||||
k === 'reasoning_max_chars'
|
||||
k === 'reasoning_max_chars' ||
|
||||
k === 'autonomous_max_runtime_ms' ||
|
||||
k === 'autonomous_started_at_ms'
|
||||
) {
|
||||
const n = Number(val)
|
||||
out[k] = Number.isFinite(n) ? n : defaults[k]
|
||||
continue
|
||||
}
|
||||
if (
|
||||
k === 'autonomous_completion_required_checks' ||
|
||||
k === 'autonomous_allow_paths' ||
|
||||
k === 'autonomous_deny_ops'
|
||||
) {
|
||||
out[k] = Array.isArray(val) ? val.map((x) => String(x ?? '')).filter(Boolean) : defaults[k]
|
||||
continue
|
||||
}
|
||||
if (
|
||||
k === 'stream' ||
|
||||
k === 'allow_delete' ||
|
||||
@@ -1189,7 +1224,10 @@ function bareAgentMergeConfig(defaults, src) {
|
||||
k === 'allow_bridge_mutations' ||
|
||||
k === 'allow_host_notifications' ||
|
||||
k === 'allow_host_actions' ||
|
||||
k === 'emergency_stop_mutations'
|
||||
k === 'emergency_stop_mutations' ||
|
||||
k === 'autonomous_mode_enabled' ||
|
||||
k === 'autonomous_active' ||
|
||||
k === 'autonomous_stop_requested'
|
||||
) {
|
||||
out[k] = Boolean(val)
|
||||
continue
|
||||
@@ -1232,7 +1270,18 @@ function bareAgentValidateConfigShape(raw) {
|
||||
'allow_bridge_mutations',
|
||||
'allow_host_notifications',
|
||||
'allow_host_actions',
|
||||
'emergency_stop_mutations'
|
||||
'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'
|
||||
]
|
||||
if (!known.includes(k)) {
|
||||
throw new Error('unknown config key: ' + k)
|
||||
@@ -1623,8 +1672,9 @@ async function bareAgentEnsureWorkspace(ctx, paths) {
|
||||
* Ensure workspace/skills templates and ~/.agent/skill-loader.js exist (idempotent; for upgrades).
|
||||
* @param {Record<string, unknown>} ctx
|
||||
* @param {{ workspace: string, workspaceSkills: string, dir: string }} paths
|
||||
* @param {Record<string, unknown>} [config]
|
||||
*/
|
||||
async function bareAgentEnsureSkillTemplates(ctx, paths) {
|
||||
async function bareAgentEnsureSkillTemplates(ctx, paths, config) {
|
||||
const vfs = ctx.vfs
|
||||
if (
|
||||
!vfs ||
|
||||
@@ -1633,6 +1683,8 @@ async function bareAgentEnsureSkillTemplates(ctx, paths) {
|
||||
typeof vfs.mkdir !== 'function'
|
||||
)
|
||||
return
|
||||
const provider =
|
||||
config && typeof config === 'object' ? String(config.provider || '').trim().toLowerCase() : ''
|
||||
try {
|
||||
await vfs.mkdir(paths.workspaceSkills, { recursive: true })
|
||||
} catch {
|
||||
@@ -1640,6 +1692,16 @@ async function bareAgentEnsureSkillTemplates(ctx, paths) {
|
||||
}
|
||||
const share = BARE_AGENT_WORKSPACE_SHARE
|
||||
for (const rel of BARE_AGENT_SKILL_SEED_REL) {
|
||||
if (rel === 'skills/xai-compat/SKILL.md' && provider !== 'xai') {
|
||||
if (typeof vfs.unlink === 'function') {
|
||||
try {
|
||||
await vfs.unlink(paths.workspace + '/' + rel)
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
const dest = paths.workspace + '/' + rel
|
||||
try {
|
||||
const b = await vfs.readFile(dest)
|
||||
@@ -3693,6 +3755,54 @@ function bareAgentToolDefinitions() {
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'autonomous_run',
|
||||
description:
|
||||
'Start an autonomous coding run with goal, optional scope path, and runtime cap. This enables autonomous mode in config.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
goal: { type: 'string', description: 'Task goal the agent should complete autonomously' },
|
||||
scope_path: { type: 'string', description: 'Preferred working scope path (optional)' },
|
||||
max_runtime_ms: { type: 'integer', description: 'Optional runtime cap override' },
|
||||
required_checks: {
|
||||
type: 'array',
|
||||
items: { type: 'string' },
|
||||
description: 'Optional quality gates (allowlisted check ids)'
|
||||
}
|
||||
},
|
||||
required: ['goal']
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'autonomous_run_status',
|
||||
description:
|
||||
'Return current autonomous run state, elapsed/runtime budget, configured checks, and latest status.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'autonomous_run_stop',
|
||||
description:
|
||||
'Request manual stop for an autonomous run; loop will stop safely on next control checkpoint.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
reason: { type: 'string' }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
@@ -3772,8 +3882,32 @@ async function bareAgentDispatchTool(o) {
|
||||
} catch {
|
||||
return bareAgentJsonResult({ ok: false, error: 'invalid_tool_arguments_json' })
|
||||
}
|
||||
const cfgNow = configRef.current || {}
|
||||
if (
|
||||
cfgNow.autonomous_active &&
|
||||
Array.isArray(cfgNow.autonomous_deny_ops) &&
|
||||
cfgNow.autonomous_deny_ops.map((x) => String(x)).includes(toolName)
|
||||
) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'autonomous_op_denied', tool: toolName })
|
||||
}
|
||||
|
||||
const vfs = ctx.vfs
|
||||
const AUTONOMOUS_DENY_PATH_PREFIXES = [
|
||||
'/.git',
|
||||
'/proc',
|
||||
'/dev',
|
||||
'/sys',
|
||||
'/run',
|
||||
'/boot',
|
||||
'/lib',
|
||||
'/usr/lib'
|
||||
]
|
||||
const AUTONOMOUS_CHECK_ALLOW = {
|
||||
'coreutils-test': 'npm test -w bare-os-coreutils',
|
||||
'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'
|
||||
}
|
||||
const execLine =
|
||||
typeof ctx.execLine === 'function'
|
||||
? /** @type {(s: string, opts?: unknown) => Promise<unknown>} */ (
|
||||
@@ -3902,6 +4036,38 @@ async function bareAgentDispatchTool(o) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} p
|
||||
*/
|
||||
function autonomousPathAllowed(p) {
|
||||
const s = String(p || '').trim()
|
||||
if (!s) return true
|
||||
if (!s.startsWith('/')) return false
|
||||
for (const pref of AUTONOMOUS_DENY_PATH_PREFIXES) {
|
||||
if (s === pref || s.startsWith(pref + '/')) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} p
|
||||
*/
|
||||
function enforceAutonomousPath(p) {
|
||||
const cfg = configRef.current || {}
|
||||
if (!cfg.autonomous_active) return true
|
||||
const path = String(p || '').trim()
|
||||
if (!path) return true
|
||||
if (!autonomousPathAllowed(path)) return false
|
||||
const allowList = Array.isArray(cfg.autonomous_allow_paths)
|
||||
? cfg.autonomous_allow_paths.map((x) => String(x || '').trim()).filter(Boolean)
|
||||
: []
|
||||
if (!allowList.length || allowList.includes('*')) return true
|
||||
for (const pref of allowList) {
|
||||
if (path === pref || path.startsWith(pref.endsWith('/') ? pref : pref + '/')) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
if (toolName === 'read_skill') {
|
||||
const skill = typeof args.skill === 'string' ? args.skill.trim() : ''
|
||||
@@ -3952,8 +4118,97 @@ async function bareAgentDispatchTool(o) {
|
||||
})
|
||||
}
|
||||
|
||||
if (toolName === 'autonomous_run') {
|
||||
const goal = typeof args.goal === 'string' ? args.goal.trim() : ''
|
||||
const scopePath = typeof args.scope_path === 'string' ? args.scope_path.trim() : ''
|
||||
const cfg = configRef.current || {}
|
||||
if (!cfg.autonomous_mode_enabled) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'autonomous_mode_disabled' })
|
||||
}
|
||||
if (!goal) return bareAgentJsonResult({ ok: false, error: 'goal_required' })
|
||||
if (scopePath && !autonomousPathAllowed(scopePath)) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'scope_path_denied' })
|
||||
}
|
||||
const maxRuntimeMsRaw =
|
||||
typeof args.max_runtime_ms === 'number' && Number.isFinite(args.max_runtime_ms)
|
||||
? args.max_runtime_ms
|
||||
: cfg.autonomous_max_runtime_ms
|
||||
const maxRuntimeMs = Math.min(Math.max(Math.floor(Number(maxRuntimeMsRaw) || 0), 60000), 7_200_000)
|
||||
const requiredChecks = Array.isArray(args.required_checks)
|
||||
? args.required_checks.map((x) => String(x || '').trim()).filter(Boolean)
|
||||
: Array.isArray(cfg.autonomous_completion_required_checks)
|
||||
? cfg.autonomous_completion_required_checks.map((x) => String(x || '').trim()).filter(Boolean)
|
||||
: []
|
||||
const unknown = requiredChecks.filter((x) => !Object.prototype.hasOwnProperty.call(AUTONOMOUS_CHECK_ALLOW, x))
|
||||
if (unknown.length) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'unknown_required_checks', unknown, allowlist: Object.keys(AUTONOMOUS_CHECK_ALLOW) })
|
||||
}
|
||||
const merged = bareAgentMergeConfigPatch(cfg, {
|
||||
autonomous_active: true,
|
||||
autonomous_stop_requested: false,
|
||||
autonomous_started_at_ms: Date.now(),
|
||||
autonomous_goal: goal,
|
||||
autonomous_status: 'running',
|
||||
autonomous_last_error: '',
|
||||
autonomous_max_runtime_ms: maxRuntimeMs,
|
||||
autonomous_completion_required_checks: requiredChecks
|
||||
})
|
||||
await bareAgentSaveConfigFromTools(ctx, paths, merged)
|
||||
configRef.current = merged
|
||||
appendProgress('autonomous_run start goal=' + goal.slice(0, 160))
|
||||
return bareAgentJsonResult({
|
||||
ok: true,
|
||||
active: true,
|
||||
goal,
|
||||
scope_path: scopePath || null,
|
||||
max_runtime_ms: maxRuntimeMs,
|
||||
required_checks: requiredChecks,
|
||||
status: 'running'
|
||||
})
|
||||
}
|
||||
|
||||
if (toolName === 'autonomous_run_status') {
|
||||
const cfg = configRef.current || {}
|
||||
const started = Number(cfg.autonomous_started_at_ms) || 0
|
||||
const elapsed = started > 0 ? Math.max(0, Date.now() - started) : 0
|
||||
const maxRuntime = Number(cfg.autonomous_max_runtime_ms) || 0
|
||||
return bareAgentJsonResult({
|
||||
ok: true,
|
||||
autonomous_mode_enabled: Boolean(cfg.autonomous_mode_enabled),
|
||||
active: Boolean(cfg.autonomous_active),
|
||||
stop_requested: Boolean(cfg.autonomous_stop_requested),
|
||||
goal: String(cfg.autonomous_goal || ''),
|
||||
status: String(cfg.autonomous_status || 'idle'),
|
||||
last_error: String(cfg.autonomous_last_error || ''),
|
||||
started_at_ms: started,
|
||||
elapsed_ms: elapsed,
|
||||
max_runtime_ms: maxRuntime,
|
||||
remaining_ms: maxRuntime > 0 ? Math.max(0, maxRuntime - elapsed) : 0,
|
||||
required_checks: Array.isArray(cfg.autonomous_completion_required_checks)
|
||||
? cfg.autonomous_completion_required_checks
|
||||
: []
|
||||
})
|
||||
}
|
||||
|
||||
if (toolName === 'autonomous_run_stop') {
|
||||
const cfg = configRef.current || {}
|
||||
const reason = typeof args.reason === 'string' ? args.reason.trim() : ''
|
||||
const merged = bareAgentMergeConfigPatch(cfg, {
|
||||
autonomous_stop_requested: true,
|
||||
autonomous_status: 'stopped',
|
||||
autonomous_last_error: reason || String(cfg.autonomous_last_error || '')
|
||||
})
|
||||
await bareAgentSaveConfigFromTools(ctx, paths, merged)
|
||||
configRef.current = merged
|
||||
appendProgress('autonomous_run_stop ' + (reason || 'requested'))
|
||||
return bareAgentJsonResult({ ok: true, stop_requested: true, reason: reason || null })
|
||||
}
|
||||
|
||||
if (toolName === 'read_file') {
|
||||
const path = typeof args.path === 'string' ? args.path : ''
|
||||
if (!enforceAutonomousPath(path)) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'autonomous_path_denied' })
|
||||
}
|
||||
const maxB =
|
||||
typeof args.max_bytes === 'number' && Number.isFinite(args.max_bytes)
|
||||
? Math.min(Math.floor(args.max_bytes), 1_000_000)
|
||||
@@ -3977,6 +4232,9 @@ async function bareAgentDispatchTool(o) {
|
||||
|
||||
if (toolName === 'write_file') {
|
||||
const path = typeof args.path === 'string' ? args.path : ''
|
||||
if (!enforceAutonomousPath(path)) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'autonomous_path_denied' })
|
||||
}
|
||||
const content = typeof args.content === 'string' ? args.content : ''
|
||||
if (!path.startsWith('/') || path.includes('..')) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'bad_path' })
|
||||
@@ -3997,6 +4255,9 @@ async function bareAgentDispatchTool(o) {
|
||||
|
||||
if (toolName === 'edit_file') {
|
||||
const path = typeof args.path === 'string' ? args.path : ''
|
||||
if (!enforceAutonomousPath(path)) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'autonomous_path_denied' })
|
||||
}
|
||||
if (!bareAgentPathAllowed(path) || !vfs?.readFile || !vfs?.writeFile) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'path_or_vfs' })
|
||||
}
|
||||
@@ -4038,6 +4299,9 @@ async function bareAgentDispatchTool(o) {
|
||||
|
||||
if (toolName === 'create_directory') {
|
||||
const path = typeof args.path === 'string' ? args.path : ''
|
||||
if (!enforceAutonomousPath(path)) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'autonomous_path_denied' })
|
||||
}
|
||||
if (!path.startsWith('/')) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'bad_path' })
|
||||
}
|
||||
@@ -4075,6 +4339,12 @@ async function bareAgentDispatchTool(o) {
|
||||
|
||||
if (toolName === 'run_command') {
|
||||
const command = typeof args.command === 'string' ? args.command : ''
|
||||
if ((configRef.current || {}).autonomous_active) {
|
||||
const cmd = command.trim().toLowerCase()
|
||||
if (cmd.includes('rm ') || cmd.includes(' git reset') || cmd.includes(' git clean')) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'autonomous_command_denied' })
|
||||
}
|
||||
}
|
||||
const timeoutMs =
|
||||
typeof args.timeout_ms === 'number' && Number.isFinite(args.timeout_ms)
|
||||
? Math.min(Math.floor(args.timeout_ms), 600000)
|
||||
@@ -5188,7 +5458,18 @@ function bareAgentMergeConfigPatch(base, patch) {
|
||||
'allow_bridge_mutations',
|
||||
'allow_host_notifications',
|
||||
'allow_host_actions',
|
||||
'emergency_stop_mutations'
|
||||
'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'
|
||||
]
|
||||
const numKeys = new Set([
|
||||
'max_tokens',
|
||||
@@ -5196,7 +5477,9 @@ function bareAgentMergeConfigPatch(base, patch) {
|
||||
'max_iterations',
|
||||
'tool_parallelism',
|
||||
'request_timeout_ms',
|
||||
'reasoning_max_chars'
|
||||
'reasoning_max_chars',
|
||||
'autonomous_max_runtime_ms',
|
||||
'autonomous_started_at_ms'
|
||||
])
|
||||
for (const k of keys) {
|
||||
if (Object.prototype.hasOwnProperty.call(patch, k)) {
|
||||
@@ -5205,6 +5488,12 @@ function bareAgentMergeConfigPatch(base, patch) {
|
||||
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'
|
||||
) {
|
||||
out[k] = Array.isArray(v) ? v.map((x) => String(x ?? '')).filter(Boolean) : out[k]
|
||||
} else if (
|
||||
k === 'stream' ||
|
||||
k === 'allow_delete' ||
|
||||
@@ -5213,7 +5502,10 @@ function bareAgentMergeConfigPatch(base, patch) {
|
||||
k === 'allow_bridge_mutations' ||
|
||||
k === 'allow_host_notifications' ||
|
||||
k === 'allow_host_actions' ||
|
||||
k === 'emergency_stop_mutations'
|
||||
k === 'emergency_stop_mutations' ||
|
||||
k === 'autonomous_mode_enabled' ||
|
||||
k === 'autonomous_active' ||
|
||||
k === 'autonomous_stop_requested'
|
||||
) {
|
||||
out[k] = Boolean(v)
|
||||
} else if (k === 'reasoning_mode') {
|
||||
@@ -5559,6 +5851,27 @@ function bareAgentApplyProviderProfile(cfg) {
|
||||
return out
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Record<string, unknown>} 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<string, unknown>} ctx
|
||||
* @param {string} argv0
|
||||
@@ -5724,7 +6037,7 @@ async function bareAgentRunSetupOnly(ctx, argv0) {
|
||||
}
|
||||
try {
|
||||
await bareAgentEnsureWorkspace(ctx, paths)
|
||||
await bareAgentEnsureSkillTemplates(ctx, paths)
|
||||
await bareAgentEnsureSkillTemplates(ctx, paths, config)
|
||||
await bareAgentSyncWorkspaceFromConfig(ctx, paths, config)
|
||||
} catch {
|
||||
bareAgentErr(
|
||||
@@ -5826,7 +6139,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
const manDigest = await bareAgentManDigest(ctx)
|
||||
|
||||
await bareAgentEnsureWorkspace(ctx, paths)
|
||||
await bareAgentEnsureSkillTemplates(ctx, paths)
|
||||
await bareAgentEnsureSkillTemplates(ctx, paths, config)
|
||||
if (needWizard) await bareAgentSyncWorkspaceFromConfig(ctx, paths, config)
|
||||
const workspacePromptBlock = await bareAgentLoadWorkspacePrompt(
|
||||
ctx,
|
||||
@@ -5893,6 +6206,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
'/chat/completions'
|
||||
const tools = bareAgentToolDefinitions()
|
||||
let reasoningSettings = bareAgentReasoningSettings(configRef.current)
|
||||
let autonomousSettings = bareAgentAutonomousSettings(configRef.current)
|
||||
let reasoningCharCount = 0
|
||||
|
||||
let suspended = replSuspendedForSetup
|
||||
@@ -5929,7 +6243,39 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
|
||||
for (;;) {
|
||||
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')
|
||||
@@ -6086,6 +6432,52 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"schema": 2,
|
||||
"profileId": "bare-os-posix-like",
|
||||
"generatedAt": "2026-04-26T09:01:31.590Z",
|
||||
"generatedAt": "2026-04-26T09:08:53.424Z",
|
||||
"note": "Sparse POSIX Issue 7 coverage hints for /bin utilities. Omitted command names are not yet profiled here.",
|
||||
"commandIndex": [
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"atMs": 1777194091590,
|
||||
"atMs": 1777194533423,
|
||||
"commands": [
|
||||
"agent",
|
||||
"arch",
|
||||
|
||||
@@ -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`**, **`xai-compat`**, **`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`**, **`holesail`** (managed **`state.json`**, **`seed`**/**`key`**, stock **`bare-www-*`** / **`bare-ssh-*`**), and **`hdms`** (Hyperdrive mounts and invite/pair). Provider-specific skill **`xai-compat`** is only seeded when `provider` is configured as `xai`.
|
||||
|
||||
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.
|
||||
|
||||
@@ -40,6 +40,15 @@ Max-autonomy bridge policy switches are also in `config.json`:
|
||||
- `allow_host_actions` — host action route gate
|
||||
- `emergency_stop_mutations` — kill switch for mutating bridge tools
|
||||
|
||||
Autonomous coding runner controls in `config.json`:
|
||||
|
||||
- `autonomous_mode_enabled` — master toggle for autonomous loop mode
|
||||
- `autonomous_max_runtime_ms` — runtime timebox for autonomous sessions
|
||||
- `autonomous_completion_required_checks` — quality gates that must pass before done
|
||||
- `autonomous_allow_paths` — allowed write/shell scope paths (`*` for unrestricted by path policy)
|
||||
- `autonomous_deny_ops` — hard denylist of tool operations blocked during autonomous runs
|
||||
- `autonomous_active`, `autonomous_stop_requested`, `autonomous_status`, `autonomous_last_error` — run state/status fields managed by tools/runtime
|
||||
|
||||
Provider profile notes:
|
||||
|
||||
- `groq` profile defaults to `https://api.groq.com/openai/v1` and OpenAI-compatible chat completions/tool calling semantics.
|
||||
|
||||
@@ -32,6 +32,14 @@
|
||||
- `allow_host_actions`: required for `request_host_action`.
|
||||
- `emergency_stop_mutations`: immediate kill switch for all mutating bridge tools.
|
||||
|
||||
## Autonomous coding runner
|
||||
|
||||
- `autonomous_run` starts autonomous project execution with a goal, runtime cap, and required checks.
|
||||
- `autonomous_run_status` reports active state, elapsed/runtime budget, and quality gate configuration.
|
||||
- `autonomous_run_stop` requests a safe stop at the next loop checkpoint.
|
||||
- Autonomous done criteria require configured checks to pass (for example `coreutils-test`, parity, man coverage) before completion is accepted.
|
||||
- Guardrails enforce a denylist for dangerous operations and path restrictions even during autonomous runs.
|
||||
|
||||
## Skills system
|
||||
|
||||
The agent has access to modular **skills** under `~/.agent/workspace/skills/` (and optionally shared skills under `~/.agent/skills/`).
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -65,7 +65,18 @@ function bareAgentDefaultConfig() {
|
||||
allow_bridge_mutations: false,
|
||||
allow_host_notifications: false,
|
||||
allow_host_actions: false,
|
||||
emergency_stop_mutations: false
|
||||
emergency_stop_mutations: false,
|
||||
autonomous_mode_enabled: false,
|
||||
autonomous_max_runtime_ms: 1800000,
|
||||
autonomous_completion_required_checks: ['coreutils-test', 'verify-kernel-seeder-parity', 'verify-man-coverage'],
|
||||
autonomous_allow_paths: ['*'],
|
||||
autonomous_deny_ops: ['delete_path', 'request_host_action', 'emit_host_notification'],
|
||||
autonomous_active: false,
|
||||
autonomous_started_at_ms: 0,
|
||||
autonomous_stop_requested: false,
|
||||
autonomous_goal: '',
|
||||
autonomous_status: 'idle',
|
||||
autonomous_last_error: ''
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +118,18 @@ function bareAgentMergeConfig(defaults, src) {
|
||||
'allow_bridge_mutations',
|
||||
'allow_host_notifications',
|
||||
'allow_host_actions',
|
||||
'emergency_stop_mutations'
|
||||
'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'
|
||||
])
|
||||
for (const k of Object.keys(src)) {
|
||||
if (k.startsWith('x-')) continue
|
||||
@@ -123,7 +145,10 @@ function bareAgentMergeConfig(defaults, src) {
|
||||
k === 'model' ||
|
||||
k === 'provider' ||
|
||||
k === 'owner_name' ||
|
||||
k === 'agent_label'
|
||||
k === 'agent_label' ||
|
||||
k === 'autonomous_goal' ||
|
||||
k === 'autonomous_status' ||
|
||||
k === 'autonomous_last_error'
|
||||
) {
|
||||
out[k] = String(val ?? '')
|
||||
continue
|
||||
@@ -140,12 +165,22 @@ function bareAgentMergeConfig(defaults, src) {
|
||||
k === 'max_iterations' ||
|
||||
k === 'tool_parallelism' ||
|
||||
k === 'request_timeout_ms' ||
|
||||
k === 'reasoning_max_chars'
|
||||
k === 'reasoning_max_chars' ||
|
||||
k === 'autonomous_max_runtime_ms' ||
|
||||
k === 'autonomous_started_at_ms'
|
||||
) {
|
||||
const n = Number(val)
|
||||
out[k] = Number.isFinite(n) ? n : defaults[k]
|
||||
continue
|
||||
}
|
||||
if (
|
||||
k === 'autonomous_completion_required_checks' ||
|
||||
k === 'autonomous_allow_paths' ||
|
||||
k === 'autonomous_deny_ops'
|
||||
) {
|
||||
out[k] = Array.isArray(val) ? val.map((x) => String(x ?? '')).filter(Boolean) : defaults[k]
|
||||
continue
|
||||
}
|
||||
if (
|
||||
k === 'stream' ||
|
||||
k === 'allow_delete' ||
|
||||
@@ -154,7 +189,10 @@ function bareAgentMergeConfig(defaults, src) {
|
||||
k === 'allow_bridge_mutations' ||
|
||||
k === 'allow_host_notifications' ||
|
||||
k === 'allow_host_actions' ||
|
||||
k === 'emergency_stop_mutations'
|
||||
k === 'emergency_stop_mutations' ||
|
||||
k === 'autonomous_mode_enabled' ||
|
||||
k === 'autonomous_active' ||
|
||||
k === 'autonomous_stop_requested'
|
||||
) {
|
||||
out[k] = Boolean(val)
|
||||
continue
|
||||
@@ -197,7 +235,18 @@ function bareAgentValidateConfigShape(raw) {
|
||||
'allow_bridge_mutations',
|
||||
'allow_host_notifications',
|
||||
'allow_host_actions',
|
||||
'emergency_stop_mutations'
|
||||
'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'
|
||||
]
|
||||
if (!known.includes(k)) {
|
||||
throw new Error('unknown config key: ' + k)
|
||||
|
||||
@@ -776,6 +776,54 @@ function bareAgentToolDefinitions() {
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'autonomous_run',
|
||||
description:
|
||||
'Start an autonomous coding run with goal, optional scope path, and runtime cap. This enables autonomous mode in config.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
goal: { type: 'string', description: 'Task goal the agent should complete autonomously' },
|
||||
scope_path: { type: 'string', description: 'Preferred working scope path (optional)' },
|
||||
max_runtime_ms: { type: 'integer', description: 'Optional runtime cap override' },
|
||||
required_checks: {
|
||||
type: 'array',
|
||||
items: { type: 'string' },
|
||||
description: 'Optional quality gates (allowlisted check ids)'
|
||||
}
|
||||
},
|
||||
required: ['goal']
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'autonomous_run_status',
|
||||
description:
|
||||
'Return current autonomous run state, elapsed/runtime budget, configured checks, and latest status.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'autonomous_run_stop',
|
||||
description:
|
||||
'Request manual stop for an autonomous run; loop will stop safely on next control checkpoint.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
reason: { type: 'string' }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
@@ -855,8 +903,32 @@ async function bareAgentDispatchTool(o) {
|
||||
} catch {
|
||||
return bareAgentJsonResult({ ok: false, error: 'invalid_tool_arguments_json' })
|
||||
}
|
||||
const cfgNow = configRef.current || {}
|
||||
if (
|
||||
cfgNow.autonomous_active &&
|
||||
Array.isArray(cfgNow.autonomous_deny_ops) &&
|
||||
cfgNow.autonomous_deny_ops.map((x) => String(x)).includes(toolName)
|
||||
) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'autonomous_op_denied', tool: toolName })
|
||||
}
|
||||
|
||||
const vfs = ctx.vfs
|
||||
const AUTONOMOUS_DENY_PATH_PREFIXES = [
|
||||
'/.git',
|
||||
'/proc',
|
||||
'/dev',
|
||||
'/sys',
|
||||
'/run',
|
||||
'/boot',
|
||||
'/lib',
|
||||
'/usr/lib'
|
||||
]
|
||||
const AUTONOMOUS_CHECK_ALLOW = {
|
||||
'coreutils-test': 'npm test -w bare-os-coreutils',
|
||||
'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'
|
||||
}
|
||||
const execLine =
|
||||
typeof ctx.execLine === 'function'
|
||||
? /** @type {(s: string, opts?: unknown) => Promise<unknown>} */ (
|
||||
@@ -985,6 +1057,38 @@ async function bareAgentDispatchTool(o) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} p
|
||||
*/
|
||||
function autonomousPathAllowed(p) {
|
||||
const s = String(p || '').trim()
|
||||
if (!s) return true
|
||||
if (!s.startsWith('/')) return false
|
||||
for (const pref of AUTONOMOUS_DENY_PATH_PREFIXES) {
|
||||
if (s === pref || s.startsWith(pref + '/')) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} p
|
||||
*/
|
||||
function enforceAutonomousPath(p) {
|
||||
const cfg = configRef.current || {}
|
||||
if (!cfg.autonomous_active) return true
|
||||
const path = String(p || '').trim()
|
||||
if (!path) return true
|
||||
if (!autonomousPathAllowed(path)) return false
|
||||
const allowList = Array.isArray(cfg.autonomous_allow_paths)
|
||||
? cfg.autonomous_allow_paths.map((x) => String(x || '').trim()).filter(Boolean)
|
||||
: []
|
||||
if (!allowList.length || allowList.includes('*')) return true
|
||||
for (const pref of allowList) {
|
||||
if (path === pref || path.startsWith(pref.endsWith('/') ? pref : pref + '/')) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
if (toolName === 'read_skill') {
|
||||
const skill = typeof args.skill === 'string' ? args.skill.trim() : ''
|
||||
@@ -1035,8 +1139,97 @@ async function bareAgentDispatchTool(o) {
|
||||
})
|
||||
}
|
||||
|
||||
if (toolName === 'autonomous_run') {
|
||||
const goal = typeof args.goal === 'string' ? args.goal.trim() : ''
|
||||
const scopePath = typeof args.scope_path === 'string' ? args.scope_path.trim() : ''
|
||||
const cfg = configRef.current || {}
|
||||
if (!cfg.autonomous_mode_enabled) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'autonomous_mode_disabled' })
|
||||
}
|
||||
if (!goal) return bareAgentJsonResult({ ok: false, error: 'goal_required' })
|
||||
if (scopePath && !autonomousPathAllowed(scopePath)) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'scope_path_denied' })
|
||||
}
|
||||
const maxRuntimeMsRaw =
|
||||
typeof args.max_runtime_ms === 'number' && Number.isFinite(args.max_runtime_ms)
|
||||
? args.max_runtime_ms
|
||||
: cfg.autonomous_max_runtime_ms
|
||||
const maxRuntimeMs = Math.min(Math.max(Math.floor(Number(maxRuntimeMsRaw) || 0), 60000), 7_200_000)
|
||||
const requiredChecks = Array.isArray(args.required_checks)
|
||||
? args.required_checks.map((x) => String(x || '').trim()).filter(Boolean)
|
||||
: Array.isArray(cfg.autonomous_completion_required_checks)
|
||||
? cfg.autonomous_completion_required_checks.map((x) => String(x || '').trim()).filter(Boolean)
|
||||
: []
|
||||
const unknown = requiredChecks.filter((x) => !Object.prototype.hasOwnProperty.call(AUTONOMOUS_CHECK_ALLOW, x))
|
||||
if (unknown.length) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'unknown_required_checks', unknown, allowlist: Object.keys(AUTONOMOUS_CHECK_ALLOW) })
|
||||
}
|
||||
const merged = bareAgentMergeConfigPatch(cfg, {
|
||||
autonomous_active: true,
|
||||
autonomous_stop_requested: false,
|
||||
autonomous_started_at_ms: Date.now(),
|
||||
autonomous_goal: goal,
|
||||
autonomous_status: 'running',
|
||||
autonomous_last_error: '',
|
||||
autonomous_max_runtime_ms: maxRuntimeMs,
|
||||
autonomous_completion_required_checks: requiredChecks
|
||||
})
|
||||
await bareAgentSaveConfigFromTools(ctx, paths, merged)
|
||||
configRef.current = merged
|
||||
appendProgress('autonomous_run start goal=' + goal.slice(0, 160))
|
||||
return bareAgentJsonResult({
|
||||
ok: true,
|
||||
active: true,
|
||||
goal,
|
||||
scope_path: scopePath || null,
|
||||
max_runtime_ms: maxRuntimeMs,
|
||||
required_checks: requiredChecks,
|
||||
status: 'running'
|
||||
})
|
||||
}
|
||||
|
||||
if (toolName === 'autonomous_run_status') {
|
||||
const cfg = configRef.current || {}
|
||||
const started = Number(cfg.autonomous_started_at_ms) || 0
|
||||
const elapsed = started > 0 ? Math.max(0, Date.now() - started) : 0
|
||||
const maxRuntime = Number(cfg.autonomous_max_runtime_ms) || 0
|
||||
return bareAgentJsonResult({
|
||||
ok: true,
|
||||
autonomous_mode_enabled: Boolean(cfg.autonomous_mode_enabled),
|
||||
active: Boolean(cfg.autonomous_active),
|
||||
stop_requested: Boolean(cfg.autonomous_stop_requested),
|
||||
goal: String(cfg.autonomous_goal || ''),
|
||||
status: String(cfg.autonomous_status || 'idle'),
|
||||
last_error: String(cfg.autonomous_last_error || ''),
|
||||
started_at_ms: started,
|
||||
elapsed_ms: elapsed,
|
||||
max_runtime_ms: maxRuntime,
|
||||
remaining_ms: maxRuntime > 0 ? Math.max(0, maxRuntime - elapsed) : 0,
|
||||
required_checks: Array.isArray(cfg.autonomous_completion_required_checks)
|
||||
? cfg.autonomous_completion_required_checks
|
||||
: []
|
||||
})
|
||||
}
|
||||
|
||||
if (toolName === 'autonomous_run_stop') {
|
||||
const cfg = configRef.current || {}
|
||||
const reason = typeof args.reason === 'string' ? args.reason.trim() : ''
|
||||
const merged = bareAgentMergeConfigPatch(cfg, {
|
||||
autonomous_stop_requested: true,
|
||||
autonomous_status: 'stopped',
|
||||
autonomous_last_error: reason || String(cfg.autonomous_last_error || '')
|
||||
})
|
||||
await bareAgentSaveConfigFromTools(ctx, paths, merged)
|
||||
configRef.current = merged
|
||||
appendProgress('autonomous_run_stop ' + (reason || 'requested'))
|
||||
return bareAgentJsonResult({ ok: true, stop_requested: true, reason: reason || null })
|
||||
}
|
||||
|
||||
if (toolName === 'read_file') {
|
||||
const path = typeof args.path === 'string' ? args.path : ''
|
||||
if (!enforceAutonomousPath(path)) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'autonomous_path_denied' })
|
||||
}
|
||||
const maxB =
|
||||
typeof args.max_bytes === 'number' && Number.isFinite(args.max_bytes)
|
||||
? Math.min(Math.floor(args.max_bytes), 1_000_000)
|
||||
@@ -1060,6 +1253,9 @@ async function bareAgentDispatchTool(o) {
|
||||
|
||||
if (toolName === 'write_file') {
|
||||
const path = typeof args.path === 'string' ? args.path : ''
|
||||
if (!enforceAutonomousPath(path)) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'autonomous_path_denied' })
|
||||
}
|
||||
const content = typeof args.content === 'string' ? args.content : ''
|
||||
if (!path.startsWith('/') || path.includes('..')) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'bad_path' })
|
||||
@@ -1080,6 +1276,9 @@ async function bareAgentDispatchTool(o) {
|
||||
|
||||
if (toolName === 'edit_file') {
|
||||
const path = typeof args.path === 'string' ? args.path : ''
|
||||
if (!enforceAutonomousPath(path)) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'autonomous_path_denied' })
|
||||
}
|
||||
if (!bareAgentPathAllowed(path) || !vfs?.readFile || !vfs?.writeFile) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'path_or_vfs' })
|
||||
}
|
||||
@@ -1121,6 +1320,9 @@ async function bareAgentDispatchTool(o) {
|
||||
|
||||
if (toolName === 'create_directory') {
|
||||
const path = typeof args.path === 'string' ? args.path : ''
|
||||
if (!enforceAutonomousPath(path)) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'autonomous_path_denied' })
|
||||
}
|
||||
if (!path.startsWith('/')) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'bad_path' })
|
||||
}
|
||||
@@ -1158,6 +1360,12 @@ async function bareAgentDispatchTool(o) {
|
||||
|
||||
if (toolName === 'run_command') {
|
||||
const command = typeof args.command === 'string' ? args.command : ''
|
||||
if ((configRef.current || {}).autonomous_active) {
|
||||
const cmd = command.trim().toLowerCase()
|
||||
if (cmd.includes('rm ') || cmd.includes(' git reset') || cmd.includes(' git clean')) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'autonomous_command_denied' })
|
||||
}
|
||||
}
|
||||
const timeoutMs =
|
||||
typeof args.timeout_ms === 'number' && Number.isFinite(args.timeout_ms)
|
||||
? Math.min(Math.floor(args.timeout_ms), 600000)
|
||||
@@ -2271,7 +2479,18 @@ function bareAgentMergeConfigPatch(base, patch) {
|
||||
'allow_bridge_mutations',
|
||||
'allow_host_notifications',
|
||||
'allow_host_actions',
|
||||
'emergency_stop_mutations'
|
||||
'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'
|
||||
]
|
||||
const numKeys = new Set([
|
||||
'max_tokens',
|
||||
@@ -2279,7 +2498,9 @@ function bareAgentMergeConfigPatch(base, patch) {
|
||||
'max_iterations',
|
||||
'tool_parallelism',
|
||||
'request_timeout_ms',
|
||||
'reasoning_max_chars'
|
||||
'reasoning_max_chars',
|
||||
'autonomous_max_runtime_ms',
|
||||
'autonomous_started_at_ms'
|
||||
])
|
||||
for (const k of keys) {
|
||||
if (Object.prototype.hasOwnProperty.call(patch, k)) {
|
||||
@@ -2288,6 +2509,12 @@ function bareAgentMergeConfigPatch(base, patch) {
|
||||
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'
|
||||
) {
|
||||
out[k] = Array.isArray(v) ? v.map((x) => String(x ?? '')).filter(Boolean) : out[k]
|
||||
} else if (
|
||||
k === 'stream' ||
|
||||
k === 'allow_delete' ||
|
||||
@@ -2296,7 +2523,10 @@ function bareAgentMergeConfigPatch(base, patch) {
|
||||
k === 'allow_bridge_mutations' ||
|
||||
k === 'allow_host_notifications' ||
|
||||
k === 'allow_host_actions' ||
|
||||
k === 'emergency_stop_mutations'
|
||||
k === 'emergency_stop_mutations' ||
|
||||
k === 'autonomous_mode_enabled' ||
|
||||
k === 'autonomous_active' ||
|
||||
k === 'autonomous_stop_requested'
|
||||
) {
|
||||
out[k] = Boolean(v)
|
||||
} else if (k === 'reasoning_mode') {
|
||||
|
||||
@@ -305,6 +305,27 @@ function bareAgentApplyProviderProfile(cfg) {
|
||||
return out
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Record<string, unknown>} 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<string, unknown>} ctx
|
||||
* @param {string} argv0
|
||||
@@ -639,6 +660,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
'/chat/completions'
|
||||
const tools = bareAgentToolDefinitions()
|
||||
let reasoningSettings = bareAgentReasoningSettings(configRef.current)
|
||||
let autonomousSettings = bareAgentAutonomousSettings(configRef.current)
|
||||
let reasoningCharCount = 0
|
||||
|
||||
let suspended = replSuspendedForSetup
|
||||
@@ -675,7 +697,39 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
|
||||
for (;;) {
|
||||
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')
|
||||
@@ -832,6 +886,52 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -40,6 +40,15 @@ Max-autonomy bridge policy switches are also in `config.json`:
|
||||
- `allow_host_actions` — host action route gate
|
||||
- `emergency_stop_mutations` — kill switch for mutating bridge tools
|
||||
|
||||
Autonomous coding runner controls in `config.json`:
|
||||
|
||||
- `autonomous_mode_enabled` — master toggle for autonomous loop mode
|
||||
- `autonomous_max_runtime_ms` — runtime timebox for autonomous sessions
|
||||
- `autonomous_completion_required_checks` — quality gates that must pass before done
|
||||
- `autonomous_allow_paths` — allowed write/shell scope paths (`*` for unrestricted by path policy)
|
||||
- `autonomous_deny_ops` — hard denylist of tool operations blocked during autonomous runs
|
||||
- `autonomous_active`, `autonomous_stop_requested`, `autonomous_status`, `autonomous_last_error` — run state/status fields managed by tools/runtime
|
||||
|
||||
Provider profile notes:
|
||||
|
||||
- `groq` profile defaults to `https://api.groq.com/openai/v1` and OpenAI-compatible chat completions/tool calling semantics.
|
||||
|
||||
@@ -32,6 +32,14 @@
|
||||
- `allow_host_actions`: required for `request_host_action`.
|
||||
- `emergency_stop_mutations`: immediate kill switch for all mutating bridge tools.
|
||||
|
||||
## Autonomous coding runner
|
||||
|
||||
- `autonomous_run` starts autonomous project execution with a goal, runtime cap, and required checks.
|
||||
- `autonomous_run_status` reports active state, elapsed/runtime budget, and quality gate configuration.
|
||||
- `autonomous_run_stop` requests a safe stop at the next loop checkpoint.
|
||||
- Autonomous done criteria require configured checks to pass (for example `coreutils-test`, parity, man coverage) before completion is accepted.
|
||||
- Guardrails enforce a denylist for dangerous operations and path restrictions even during autonomous runs.
|
||||
|
||||
## Skills system
|
||||
|
||||
The agent has access to modular **skills** under `~/.agent/workspace/skills/` (and optionally shared skills under `~/.agent/skills/`).
|
||||
|
||||
@@ -28,6 +28,24 @@ test('agent-state exposes bridge policy keys', async (t) => {
|
||||
}
|
||||
})
|
||||
|
||||
test('agent-state exposes autonomous mode config keys', async (t) => {
|
||||
for (const key of [
|
||||
'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'
|
||||
]) {
|
||||
t.ok(STATE.includes(key), 'missing autonomous key in agent-state.js: ' + key)
|
||||
}
|
||||
})
|
||||
|
||||
test('agent-tools exposes agent ops tools', async (t) => {
|
||||
for (const toolName of [
|
||||
'list_services',
|
||||
@@ -50,7 +68,10 @@ test('agent-tools exposes agent ops tools', async (t) => {
|
||||
'get_hrpc_bridge_health',
|
||||
'get_hrpc_allowlist_status',
|
||||
'emit_host_notification',
|
||||
'request_host_action'
|
||||
'request_host_action',
|
||||
'autonomous_run',
|
||||
'autonomous_run_status',
|
||||
'autonomous_run_stop'
|
||||
]) {
|
||||
t.ok(TOOLS.includes("name: '" + toolName + "'"), 'missing tool definition: ' + toolName)
|
||||
}
|
||||
@@ -69,3 +90,11 @@ test('agent-tui contains groq provider profile/request shaping', async (t) => {
|
||||
t.ok(TUI.includes('body.parallel_tool_calls'))
|
||||
t.ok(TUI.includes('body.max_completion_tokens'))
|
||||
})
|
||||
|
||||
test('agent-tui contains autonomous loop controls and completion gates', async (t) => {
|
||||
t.ok(TUI.includes('bareAgentAutonomousSettings'))
|
||||
t.ok(TUI.includes('autonomous run stopped by request'))
|
||||
t.ok(TUI.includes('autonomous run stopped (timebox expired)'))
|
||||
t.ok(TUI.includes('Autonomous completion gates failed for checks'))
|
||||
t.ok(TUI.includes('autonomous completion gates passed'))
|
||||
})
|
||||
|
||||
@@ -1100,7 +1100,18 @@ function bareAgentDefaultConfig() {
|
||||
allow_bridge_mutations: false,
|
||||
allow_host_notifications: false,
|
||||
allow_host_actions: false,
|
||||
emergency_stop_mutations: false
|
||||
emergency_stop_mutations: false,
|
||||
autonomous_mode_enabled: false,
|
||||
autonomous_max_runtime_ms: 1800000,
|
||||
autonomous_completion_required_checks: ['coreutils-test', 'verify-kernel-seeder-parity', 'verify-man-coverage'],
|
||||
autonomous_allow_paths: ['*'],
|
||||
autonomous_deny_ops: ['delete_path', 'request_host_action', 'emit_host_notification'],
|
||||
autonomous_active: false,
|
||||
autonomous_started_at_ms: 0,
|
||||
autonomous_stop_requested: false,
|
||||
autonomous_goal: '',
|
||||
autonomous_status: 'idle',
|
||||
autonomous_last_error: ''
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1142,7 +1153,18 @@ function bareAgentMergeConfig(defaults, src) {
|
||||
'allow_bridge_mutations',
|
||||
'allow_host_notifications',
|
||||
'allow_host_actions',
|
||||
'emergency_stop_mutations'
|
||||
'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'
|
||||
])
|
||||
for (const k of Object.keys(src)) {
|
||||
if (k.startsWith('x-')) continue
|
||||
@@ -1158,7 +1180,10 @@ function bareAgentMergeConfig(defaults, src) {
|
||||
k === 'model' ||
|
||||
k === 'provider' ||
|
||||
k === 'owner_name' ||
|
||||
k === 'agent_label'
|
||||
k === 'agent_label' ||
|
||||
k === 'autonomous_goal' ||
|
||||
k === 'autonomous_status' ||
|
||||
k === 'autonomous_last_error'
|
||||
) {
|
||||
out[k] = String(val ?? '')
|
||||
continue
|
||||
@@ -1175,12 +1200,22 @@ function bareAgentMergeConfig(defaults, src) {
|
||||
k === 'max_iterations' ||
|
||||
k === 'tool_parallelism' ||
|
||||
k === 'request_timeout_ms' ||
|
||||
k === 'reasoning_max_chars'
|
||||
k === 'reasoning_max_chars' ||
|
||||
k === 'autonomous_max_runtime_ms' ||
|
||||
k === 'autonomous_started_at_ms'
|
||||
) {
|
||||
const n = Number(val)
|
||||
out[k] = Number.isFinite(n) ? n : defaults[k]
|
||||
continue
|
||||
}
|
||||
if (
|
||||
k === 'autonomous_completion_required_checks' ||
|
||||
k === 'autonomous_allow_paths' ||
|
||||
k === 'autonomous_deny_ops'
|
||||
) {
|
||||
out[k] = Array.isArray(val) ? val.map((x) => String(x ?? '')).filter(Boolean) : defaults[k]
|
||||
continue
|
||||
}
|
||||
if (
|
||||
k === 'stream' ||
|
||||
k === 'allow_delete' ||
|
||||
@@ -1189,7 +1224,10 @@ function bareAgentMergeConfig(defaults, src) {
|
||||
k === 'allow_bridge_mutations' ||
|
||||
k === 'allow_host_notifications' ||
|
||||
k === 'allow_host_actions' ||
|
||||
k === 'emergency_stop_mutations'
|
||||
k === 'emergency_stop_mutations' ||
|
||||
k === 'autonomous_mode_enabled' ||
|
||||
k === 'autonomous_active' ||
|
||||
k === 'autonomous_stop_requested'
|
||||
) {
|
||||
out[k] = Boolean(val)
|
||||
continue
|
||||
@@ -1232,7 +1270,18 @@ function bareAgentValidateConfigShape(raw) {
|
||||
'allow_bridge_mutations',
|
||||
'allow_host_notifications',
|
||||
'allow_host_actions',
|
||||
'emergency_stop_mutations'
|
||||
'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'
|
||||
]
|
||||
if (!known.includes(k)) {
|
||||
throw new Error('unknown config key: ' + k)
|
||||
@@ -1623,8 +1672,9 @@ async function bareAgentEnsureWorkspace(ctx, paths) {
|
||||
* Ensure workspace/skills templates and ~/.agent/skill-loader.js exist (idempotent; for upgrades).
|
||||
* @param {Record<string, unknown>} ctx
|
||||
* @param {{ workspace: string, workspaceSkills: string, dir: string }} paths
|
||||
* @param {Record<string, unknown>} [config]
|
||||
*/
|
||||
async function bareAgentEnsureSkillTemplates(ctx, paths) {
|
||||
async function bareAgentEnsureSkillTemplates(ctx, paths, config) {
|
||||
const vfs = ctx.vfs
|
||||
if (
|
||||
!vfs ||
|
||||
@@ -1633,6 +1683,8 @@ async function bareAgentEnsureSkillTemplates(ctx, paths) {
|
||||
typeof vfs.mkdir !== 'function'
|
||||
)
|
||||
return
|
||||
const provider =
|
||||
config && typeof config === 'object' ? String(config.provider || '').trim().toLowerCase() : ''
|
||||
try {
|
||||
await vfs.mkdir(paths.workspaceSkills, { recursive: true })
|
||||
} catch {
|
||||
@@ -1640,6 +1692,16 @@ async function bareAgentEnsureSkillTemplates(ctx, paths) {
|
||||
}
|
||||
const share = BARE_AGENT_WORKSPACE_SHARE
|
||||
for (const rel of BARE_AGENT_SKILL_SEED_REL) {
|
||||
if (rel === 'skills/xai-compat/SKILL.md' && provider !== 'xai') {
|
||||
if (typeof vfs.unlink === 'function') {
|
||||
try {
|
||||
await vfs.unlink(paths.workspace + '/' + rel)
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
const dest = paths.workspace + '/' + rel
|
||||
try {
|
||||
const b = await vfs.readFile(dest)
|
||||
@@ -3693,6 +3755,54 @@ function bareAgentToolDefinitions() {
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'autonomous_run',
|
||||
description:
|
||||
'Start an autonomous coding run with goal, optional scope path, and runtime cap. This enables autonomous mode in config.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
goal: { type: 'string', description: 'Task goal the agent should complete autonomously' },
|
||||
scope_path: { type: 'string', description: 'Preferred working scope path (optional)' },
|
||||
max_runtime_ms: { type: 'integer', description: 'Optional runtime cap override' },
|
||||
required_checks: {
|
||||
type: 'array',
|
||||
items: { type: 'string' },
|
||||
description: 'Optional quality gates (allowlisted check ids)'
|
||||
}
|
||||
},
|
||||
required: ['goal']
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'autonomous_run_status',
|
||||
description:
|
||||
'Return current autonomous run state, elapsed/runtime budget, configured checks, and latest status.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'autonomous_run_stop',
|
||||
description:
|
||||
'Request manual stop for an autonomous run; loop will stop safely on next control checkpoint.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
reason: { type: 'string' }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'function',
|
||||
function: {
|
||||
@@ -3772,8 +3882,32 @@ async function bareAgentDispatchTool(o) {
|
||||
} catch {
|
||||
return bareAgentJsonResult({ ok: false, error: 'invalid_tool_arguments_json' })
|
||||
}
|
||||
const cfgNow = configRef.current || {}
|
||||
if (
|
||||
cfgNow.autonomous_active &&
|
||||
Array.isArray(cfgNow.autonomous_deny_ops) &&
|
||||
cfgNow.autonomous_deny_ops.map((x) => String(x)).includes(toolName)
|
||||
) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'autonomous_op_denied', tool: toolName })
|
||||
}
|
||||
|
||||
const vfs = ctx.vfs
|
||||
const AUTONOMOUS_DENY_PATH_PREFIXES = [
|
||||
'/.git',
|
||||
'/proc',
|
||||
'/dev',
|
||||
'/sys',
|
||||
'/run',
|
||||
'/boot',
|
||||
'/lib',
|
||||
'/usr/lib'
|
||||
]
|
||||
const AUTONOMOUS_CHECK_ALLOW = {
|
||||
'coreutils-test': 'npm test -w bare-os-coreutils',
|
||||
'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'
|
||||
}
|
||||
const execLine =
|
||||
typeof ctx.execLine === 'function'
|
||||
? /** @type {(s: string, opts?: unknown) => Promise<unknown>} */ (
|
||||
@@ -3902,6 +4036,38 @@ async function bareAgentDispatchTool(o) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} p
|
||||
*/
|
||||
function autonomousPathAllowed(p) {
|
||||
const s = String(p || '').trim()
|
||||
if (!s) return true
|
||||
if (!s.startsWith('/')) return false
|
||||
for (const pref of AUTONOMOUS_DENY_PATH_PREFIXES) {
|
||||
if (s === pref || s.startsWith(pref + '/')) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} p
|
||||
*/
|
||||
function enforceAutonomousPath(p) {
|
||||
const cfg = configRef.current || {}
|
||||
if (!cfg.autonomous_active) return true
|
||||
const path = String(p || '').trim()
|
||||
if (!path) return true
|
||||
if (!autonomousPathAllowed(path)) return false
|
||||
const allowList = Array.isArray(cfg.autonomous_allow_paths)
|
||||
? cfg.autonomous_allow_paths.map((x) => String(x || '').trim()).filter(Boolean)
|
||||
: []
|
||||
if (!allowList.length || allowList.includes('*')) return true
|
||||
for (const pref of allowList) {
|
||||
if (path === pref || path.startsWith(pref.endsWith('/') ? pref : pref + '/')) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
try {
|
||||
if (toolName === 'read_skill') {
|
||||
const skill = typeof args.skill === 'string' ? args.skill.trim() : ''
|
||||
@@ -3952,8 +4118,97 @@ async function bareAgentDispatchTool(o) {
|
||||
})
|
||||
}
|
||||
|
||||
if (toolName === 'autonomous_run') {
|
||||
const goal = typeof args.goal === 'string' ? args.goal.trim() : ''
|
||||
const scopePath = typeof args.scope_path === 'string' ? args.scope_path.trim() : ''
|
||||
const cfg = configRef.current || {}
|
||||
if (!cfg.autonomous_mode_enabled) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'autonomous_mode_disabled' })
|
||||
}
|
||||
if (!goal) return bareAgentJsonResult({ ok: false, error: 'goal_required' })
|
||||
if (scopePath && !autonomousPathAllowed(scopePath)) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'scope_path_denied' })
|
||||
}
|
||||
const maxRuntimeMsRaw =
|
||||
typeof args.max_runtime_ms === 'number' && Number.isFinite(args.max_runtime_ms)
|
||||
? args.max_runtime_ms
|
||||
: cfg.autonomous_max_runtime_ms
|
||||
const maxRuntimeMs = Math.min(Math.max(Math.floor(Number(maxRuntimeMsRaw) || 0), 60000), 7_200_000)
|
||||
const requiredChecks = Array.isArray(args.required_checks)
|
||||
? args.required_checks.map((x) => String(x || '').trim()).filter(Boolean)
|
||||
: Array.isArray(cfg.autonomous_completion_required_checks)
|
||||
? cfg.autonomous_completion_required_checks.map((x) => String(x || '').trim()).filter(Boolean)
|
||||
: []
|
||||
const unknown = requiredChecks.filter((x) => !Object.prototype.hasOwnProperty.call(AUTONOMOUS_CHECK_ALLOW, x))
|
||||
if (unknown.length) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'unknown_required_checks', unknown, allowlist: Object.keys(AUTONOMOUS_CHECK_ALLOW) })
|
||||
}
|
||||
const merged = bareAgentMergeConfigPatch(cfg, {
|
||||
autonomous_active: true,
|
||||
autonomous_stop_requested: false,
|
||||
autonomous_started_at_ms: Date.now(),
|
||||
autonomous_goal: goal,
|
||||
autonomous_status: 'running',
|
||||
autonomous_last_error: '',
|
||||
autonomous_max_runtime_ms: maxRuntimeMs,
|
||||
autonomous_completion_required_checks: requiredChecks
|
||||
})
|
||||
await bareAgentSaveConfigFromTools(ctx, paths, merged)
|
||||
configRef.current = merged
|
||||
appendProgress('autonomous_run start goal=' + goal.slice(0, 160))
|
||||
return bareAgentJsonResult({
|
||||
ok: true,
|
||||
active: true,
|
||||
goal,
|
||||
scope_path: scopePath || null,
|
||||
max_runtime_ms: maxRuntimeMs,
|
||||
required_checks: requiredChecks,
|
||||
status: 'running'
|
||||
})
|
||||
}
|
||||
|
||||
if (toolName === 'autonomous_run_status') {
|
||||
const cfg = configRef.current || {}
|
||||
const started = Number(cfg.autonomous_started_at_ms) || 0
|
||||
const elapsed = started > 0 ? Math.max(0, Date.now() - started) : 0
|
||||
const maxRuntime = Number(cfg.autonomous_max_runtime_ms) || 0
|
||||
return bareAgentJsonResult({
|
||||
ok: true,
|
||||
autonomous_mode_enabled: Boolean(cfg.autonomous_mode_enabled),
|
||||
active: Boolean(cfg.autonomous_active),
|
||||
stop_requested: Boolean(cfg.autonomous_stop_requested),
|
||||
goal: String(cfg.autonomous_goal || ''),
|
||||
status: String(cfg.autonomous_status || 'idle'),
|
||||
last_error: String(cfg.autonomous_last_error || ''),
|
||||
started_at_ms: started,
|
||||
elapsed_ms: elapsed,
|
||||
max_runtime_ms: maxRuntime,
|
||||
remaining_ms: maxRuntime > 0 ? Math.max(0, maxRuntime - elapsed) : 0,
|
||||
required_checks: Array.isArray(cfg.autonomous_completion_required_checks)
|
||||
? cfg.autonomous_completion_required_checks
|
||||
: []
|
||||
})
|
||||
}
|
||||
|
||||
if (toolName === 'autonomous_run_stop') {
|
||||
const cfg = configRef.current || {}
|
||||
const reason = typeof args.reason === 'string' ? args.reason.trim() : ''
|
||||
const merged = bareAgentMergeConfigPatch(cfg, {
|
||||
autonomous_stop_requested: true,
|
||||
autonomous_status: 'stopped',
|
||||
autonomous_last_error: reason || String(cfg.autonomous_last_error || '')
|
||||
})
|
||||
await bareAgentSaveConfigFromTools(ctx, paths, merged)
|
||||
configRef.current = merged
|
||||
appendProgress('autonomous_run_stop ' + (reason || 'requested'))
|
||||
return bareAgentJsonResult({ ok: true, stop_requested: true, reason: reason || null })
|
||||
}
|
||||
|
||||
if (toolName === 'read_file') {
|
||||
const path = typeof args.path === 'string' ? args.path : ''
|
||||
if (!enforceAutonomousPath(path)) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'autonomous_path_denied' })
|
||||
}
|
||||
const maxB =
|
||||
typeof args.max_bytes === 'number' && Number.isFinite(args.max_bytes)
|
||||
? Math.min(Math.floor(args.max_bytes), 1_000_000)
|
||||
@@ -3977,6 +4232,9 @@ async function bareAgentDispatchTool(o) {
|
||||
|
||||
if (toolName === 'write_file') {
|
||||
const path = typeof args.path === 'string' ? args.path : ''
|
||||
if (!enforceAutonomousPath(path)) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'autonomous_path_denied' })
|
||||
}
|
||||
const content = typeof args.content === 'string' ? args.content : ''
|
||||
if (!path.startsWith('/') || path.includes('..')) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'bad_path' })
|
||||
@@ -3997,6 +4255,9 @@ async function bareAgentDispatchTool(o) {
|
||||
|
||||
if (toolName === 'edit_file') {
|
||||
const path = typeof args.path === 'string' ? args.path : ''
|
||||
if (!enforceAutonomousPath(path)) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'autonomous_path_denied' })
|
||||
}
|
||||
if (!bareAgentPathAllowed(path) || !vfs?.readFile || !vfs?.writeFile) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'path_or_vfs' })
|
||||
}
|
||||
@@ -4038,6 +4299,9 @@ async function bareAgentDispatchTool(o) {
|
||||
|
||||
if (toolName === 'create_directory') {
|
||||
const path = typeof args.path === 'string' ? args.path : ''
|
||||
if (!enforceAutonomousPath(path)) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'autonomous_path_denied' })
|
||||
}
|
||||
if (!path.startsWith('/')) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'bad_path' })
|
||||
}
|
||||
@@ -4075,6 +4339,12 @@ async function bareAgentDispatchTool(o) {
|
||||
|
||||
if (toolName === 'run_command') {
|
||||
const command = typeof args.command === 'string' ? args.command : ''
|
||||
if ((configRef.current || {}).autonomous_active) {
|
||||
const cmd = command.trim().toLowerCase()
|
||||
if (cmd.includes('rm ') || cmd.includes(' git reset') || cmd.includes(' git clean')) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'autonomous_command_denied' })
|
||||
}
|
||||
}
|
||||
const timeoutMs =
|
||||
typeof args.timeout_ms === 'number' && Number.isFinite(args.timeout_ms)
|
||||
? Math.min(Math.floor(args.timeout_ms), 600000)
|
||||
@@ -5188,7 +5458,18 @@ function bareAgentMergeConfigPatch(base, patch) {
|
||||
'allow_bridge_mutations',
|
||||
'allow_host_notifications',
|
||||
'allow_host_actions',
|
||||
'emergency_stop_mutations'
|
||||
'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'
|
||||
]
|
||||
const numKeys = new Set([
|
||||
'max_tokens',
|
||||
@@ -5196,7 +5477,9 @@ function bareAgentMergeConfigPatch(base, patch) {
|
||||
'max_iterations',
|
||||
'tool_parallelism',
|
||||
'request_timeout_ms',
|
||||
'reasoning_max_chars'
|
||||
'reasoning_max_chars',
|
||||
'autonomous_max_runtime_ms',
|
||||
'autonomous_started_at_ms'
|
||||
])
|
||||
for (const k of keys) {
|
||||
if (Object.prototype.hasOwnProperty.call(patch, k)) {
|
||||
@@ -5205,6 +5488,12 @@ function bareAgentMergeConfigPatch(base, patch) {
|
||||
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'
|
||||
) {
|
||||
out[k] = Array.isArray(v) ? v.map((x) => String(x ?? '')).filter(Boolean) : out[k]
|
||||
} else if (
|
||||
k === 'stream' ||
|
||||
k === 'allow_delete' ||
|
||||
@@ -5213,7 +5502,10 @@ function bareAgentMergeConfigPatch(base, patch) {
|
||||
k === 'allow_bridge_mutations' ||
|
||||
k === 'allow_host_notifications' ||
|
||||
k === 'allow_host_actions' ||
|
||||
k === 'emergency_stop_mutations'
|
||||
k === 'emergency_stop_mutations' ||
|
||||
k === 'autonomous_mode_enabled' ||
|
||||
k === 'autonomous_active' ||
|
||||
k === 'autonomous_stop_requested'
|
||||
) {
|
||||
out[k] = Boolean(v)
|
||||
} else if (k === 'reasoning_mode') {
|
||||
@@ -5559,6 +5851,27 @@ function bareAgentApplyProviderProfile(cfg) {
|
||||
return out
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Record<string, unknown>} 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<string, unknown>} ctx
|
||||
* @param {string} argv0
|
||||
@@ -5724,7 +6037,7 @@ async function bareAgentRunSetupOnly(ctx, argv0) {
|
||||
}
|
||||
try {
|
||||
await bareAgentEnsureWorkspace(ctx, paths)
|
||||
await bareAgentEnsureSkillTemplates(ctx, paths)
|
||||
await bareAgentEnsureSkillTemplates(ctx, paths, config)
|
||||
await bareAgentSyncWorkspaceFromConfig(ctx, paths, config)
|
||||
} catch {
|
||||
bareAgentErr(
|
||||
@@ -5826,7 +6139,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
const manDigest = await bareAgentManDigest(ctx)
|
||||
|
||||
await bareAgentEnsureWorkspace(ctx, paths)
|
||||
await bareAgentEnsureSkillTemplates(ctx, paths)
|
||||
await bareAgentEnsureSkillTemplates(ctx, paths, config)
|
||||
if (needWizard) await bareAgentSyncWorkspaceFromConfig(ctx, paths, config)
|
||||
const workspacePromptBlock = await bareAgentLoadWorkspacePrompt(
|
||||
ctx,
|
||||
@@ -5893,6 +6206,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
'/chat/completions'
|
||||
const tools = bareAgentToolDefinitions()
|
||||
let reasoningSettings = bareAgentReasoningSettings(configRef.current)
|
||||
let autonomousSettings = bareAgentAutonomousSettings(configRef.current)
|
||||
let reasoningCharCount = 0
|
||||
|
||||
let suspended = replSuspendedForSetup
|
||||
@@ -5929,7 +6243,39 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
|
||||
for (;;) {
|
||||
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')
|
||||
@@ -6086,6 +6432,52 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"schema": 2,
|
||||
"profileId": "bare-os-posix-like",
|
||||
"generatedAt": "2026-04-26T09:01:31.590Z",
|
||||
"generatedAt": "2026-04-26T09:08:53.424Z",
|
||||
"note": "Sparse POSIX Issue 7 coverage hints for /bin utilities. Omitted command names are not yet profiled here.",
|
||||
"commandIndex": [
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"atMs": 1777194091590,
|
||||
"atMs": 1777194533423,
|
||||
"commands": [
|
||||
"agent",
|
||||
"arch",
|
||||
|
||||
@@ -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`**, **`xai-compat`**, **`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`**, **`holesail`** (managed **`state.json`**, **`seed`**/**`key`**, stock **`bare-www-*`** / **`bare-ssh-*`**), and **`hdms`** (Hyperdrive mounts and invite/pair). Provider-specific skill **`xai-compat`** is only seeded when `provider` is configured as `xai`.
|
||||
|
||||
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.
|
||||
|
||||
@@ -40,6 +40,15 @@ Max-autonomy bridge policy switches are also in `config.json`:
|
||||
- `allow_host_actions` — host action route gate
|
||||
- `emergency_stop_mutations` — kill switch for mutating bridge tools
|
||||
|
||||
Autonomous coding runner controls in `config.json`:
|
||||
|
||||
- `autonomous_mode_enabled` — master toggle for autonomous loop mode
|
||||
- `autonomous_max_runtime_ms` — runtime timebox for autonomous sessions
|
||||
- `autonomous_completion_required_checks` — quality gates that must pass before done
|
||||
- `autonomous_allow_paths` — allowed write/shell scope paths (`*` for unrestricted by path policy)
|
||||
- `autonomous_deny_ops` — hard denylist of tool operations blocked during autonomous runs
|
||||
- `autonomous_active`, `autonomous_stop_requested`, `autonomous_status`, `autonomous_last_error` — run state/status fields managed by tools/runtime
|
||||
|
||||
Provider profile notes:
|
||||
|
||||
- `groq` profile defaults to `https://api.groq.com/openai/v1` and OpenAI-compatible chat completions/tool calling semantics.
|
||||
|
||||
@@ -32,6 +32,14 @@
|
||||
- `allow_host_actions`: required for `request_host_action`.
|
||||
- `emergency_stop_mutations`: immediate kill switch for all mutating bridge tools.
|
||||
|
||||
## Autonomous coding runner
|
||||
|
||||
- `autonomous_run` starts autonomous project execution with a goal, runtime cap, and required checks.
|
||||
- `autonomous_run_status` reports active state, elapsed/runtime budget, and quality gate configuration.
|
||||
- `autonomous_run_stop` requests a safe stop at the next loop checkpoint.
|
||||
- Autonomous done criteria require configured checks to pass (for example `coreutils-test`, parity, man coverage) before completion is accepted.
|
||||
- Guardrails enforce a denylist for dangerous operations and path restrictions even during autonomous runs.
|
||||
|
||||
## Skills system
|
||||
|
||||
The agent has access to modular **skills** under `~/.agent/workspace/skills/` (and optionally shared skills under `~/.agent/skills/`).
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user