277 lines
8.8 KiB
JavaScript
277 lines
8.8 KiB
JavaScript
import test from 'brittle'
|
|
import { readFileSync } from 'node:fs'
|
|
|
|
const STATE = readFileSync(
|
|
new URL('../lib/agent/agent-state.js', import.meta.url),
|
|
'utf8'
|
|
)
|
|
const TOOLS =
|
|
readFileSync(new URL('../lib/agent/agent-tools.js', import.meta.url), 'utf8') +
|
|
readFileSync(
|
|
new URL('../lib/agent/agent-tool-definitions.js', import.meta.url),
|
|
'utf8'
|
|
) +
|
|
readFileSync(
|
|
new URL('../lib/agent/agent-tool-dispatch.js', import.meta.url),
|
|
'utf8'
|
|
)
|
|
const OPENAI = readFileSync(
|
|
new URL('../lib/agent/agent-openai.js', import.meta.url),
|
|
'utf8'
|
|
)
|
|
const TUI = readFileSync(
|
|
new URL('../lib/agent/agent-tui.js', import.meta.url),
|
|
'utf8'
|
|
)
|
|
const CLI = readFileSync(new URL('../src/agent.js', import.meta.url), 'utf8')
|
|
|
|
test('agent-state exposes QVAC backend config keys', async (t) => {
|
|
for (const key of [
|
|
'backend',
|
|
'qvac_model',
|
|
'qvac_profile',
|
|
'qvac_ctx_size',
|
|
'qvac_device',
|
|
'qvac_main_gpu',
|
|
'qvac_gpu_layers'
|
|
]) {
|
|
t.ok(STATE.includes(key), 'missing QVAC key in agent-state.js: ' + key)
|
|
}
|
|
t.ok(STATE.includes("backend: 'qvac'"), 'default backend should be qvac')
|
|
t.ok(STATE.includes("provider: 'qvac'"), 'default provider should be qvac')
|
|
t.ok(STATE.includes('QWEN3_1_7B_INST_Q4'), 'default qvac model id')
|
|
})
|
|
|
|
test('agent-tui wizard offers QVAC vs REST and backend-specific setup', async (t) => {
|
|
t.ok(TUI.includes('Backend [1=QVAC, 2=REST]'))
|
|
t.ok(TUI.includes('bareAgentSetupRestFieldsPlain'))
|
|
t.ok(TUI.includes('bareAgentSetupQvacFieldsPlain'))
|
|
t.ok(TUI.includes('Provider [1=Groq, 2=xAI, 3=OpenAI, 4=Custom]'))
|
|
t.ok(TUI.includes('API key (required, input hidden)'))
|
|
t.ok(TUI.includes('Base URL ['))
|
|
t.ok(TUI.includes('QVAC model profile'))
|
|
t.ok(!TUI.includes('Enable autonomous coding mode?'))
|
|
t.ok(TUI.includes('bareOsQvacComplete'))
|
|
t.ok(TUI.includes("backendIter === 'qvac'"))
|
|
t.ok(
|
|
TUI.includes('bareAgentInteractiveSetupTui'),
|
|
'setup prefers ctx.tui.form'
|
|
)
|
|
t.ok(TUI.includes('ctx.tui.form.run'), 'setup form run')
|
|
t.ok(TUI.includes('bareAgentSanitizeConfigForBackend'))
|
|
})
|
|
|
|
test('agent-tools merge patch allows backend / qvac keys', async (t) => {
|
|
t.ok(TOOLS.includes("'backend'"))
|
|
t.ok(TOOLS.includes("'qvac_model'"))
|
|
t.ok(TOOLS.includes("'qvac_profile'"))
|
|
t.ok(TOOLS.includes("'qvac_ctx_size'"))
|
|
t.ok(TOOLS.includes("'qvac_device'"))
|
|
t.ok(TOOLS.includes("'qvac_main_gpu'"))
|
|
t.ok(TUI.includes('bareAgentQvacResolveDeviceOpts'))
|
|
})
|
|
|
|
test('agent-state exposes bridge policy keys', async (t) => {
|
|
for (const key of [
|
|
'allow_bridge_mutations',
|
|
'allow_host_notifications',
|
|
'allow_host_actions',
|
|
'emergency_stop_mutations',
|
|
'access_policy',
|
|
'command_deny',
|
|
'mutate_deny_prefixes'
|
|
]) {
|
|
t.ok(STATE.includes(key), 'missing key in agent-state.js: ' + key)
|
|
}
|
|
})
|
|
|
|
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',
|
|
'plan_mode_active',
|
|
'todo_nudge_enabled',
|
|
'access_policy',
|
|
'command_deny',
|
|
'mutate_deny_prefixes'
|
|
]) {
|
|
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',
|
|
'service_status',
|
|
'list_timers',
|
|
'read_cron_log',
|
|
'read_audit_log',
|
|
'read_boot_policy',
|
|
'read_kernel_extension_resolution',
|
|
'get_initd_graph',
|
|
'read_unit_journal',
|
|
'inspect_ipc_backpressure',
|
|
'get_network_summary',
|
|
'tail_telemetry_streams',
|
|
'pkg_index_lookup',
|
|
'list_verification_scripts',
|
|
'run_maintenance_gate',
|
|
'run_contract_checks',
|
|
'summarize_build_drift',
|
|
'get_hrpc_bridge_health',
|
|
'get_hrpc_allowlist_status',
|
|
'emit_host_notification',
|
|
'request_host_action',
|
|
'discord_send_message',
|
|
'discord_channel_status',
|
|
'discord_read_inbox',
|
|
'autonomous_run',
|
|
'autonomous_run_status',
|
|
'autonomous_run_stop',
|
|
'verification_hints',
|
|
'runtime_diagnostic_bundle',
|
|
'search_replace',
|
|
'glob_files',
|
|
'todo_write',
|
|
'memory_search',
|
|
'memory_get',
|
|
'enter_plan_mode',
|
|
'exit_plan_mode',
|
|
'ask_user_question',
|
|
'grep',
|
|
'apply_patch',
|
|
'update_goal',
|
|
'web_search',
|
|
'git_status',
|
|
'memory_append',
|
|
'list_skills',
|
|
'list_agent_tools',
|
|
'schedule_task',
|
|
'unschedule_task',
|
|
'list_scheduled',
|
|
'fuzzy_find',
|
|
'read_many',
|
|
'wait_for',
|
|
'undo_last_edit',
|
|
'git_diff',
|
|
'history_search',
|
|
'git_log',
|
|
'git_show',
|
|
'git_blame',
|
|
'copy_path',
|
|
'diff_files',
|
|
'find_symbol',
|
|
'create_skill',
|
|
'remember',
|
|
'rewind_session',
|
|
'export_session'
|
|
]) {
|
|
t.ok(
|
|
TOOLS.includes("name: '" + toolName + "'"),
|
|
'missing tool definition: ' + toolName
|
|
)
|
|
}
|
|
})
|
|
|
|
test('agent-openai parses reasoning deltas when present', async (t) => {
|
|
t.ok(OPENAI.includes('delta_reasoning'))
|
|
t.ok(OPENAI.includes('reasoning_content'))
|
|
t.ok(OPENAI.includes('response.reasoning_summary_text.delta'))
|
|
t.ok(OPENAI.includes('response_shape_keys'))
|
|
})
|
|
|
|
test('agent-tui contains groq provider profile/request shaping', async (t) => {
|
|
t.ok(TUI.includes("spec.id === 'groq'") || TUI.includes("provider === 'groq'"))
|
|
t.ok(TUI.includes('https://api.groq.com/openai/v1'))
|
|
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'))
|
|
t.ok(TUI.includes('bareAgentAutonomousShouldContinue'))
|
|
t.ok(TUI.includes('bareAgentAutonomousContinuationPrompt'))
|
|
t.ok(TUI.includes('bareAgentBeginAutonomousRun'))
|
|
t.ok(TUI.includes('internalGate: true'))
|
|
t.ok(TUI.includes('enter_plan_mode'))
|
|
t.ok(TUI.includes('todo_write'))
|
|
})
|
|
|
|
test('agent-tui embeds operating contract appendix', async (t) => {
|
|
t.ok(TUI.includes('BARE_AGENT_OPERATING_CONTRACT'))
|
|
t.ok(TUI.includes('BARE_AGENT_DISCORD_REPLY_FORMAT'))
|
|
t.ok(TUI.includes('BARE_OS_AGENT_DISCORD'))
|
|
t.ok(TUI.includes('verification_hints'))
|
|
t.ok(TUI.includes('NEVER ASK'))
|
|
t.ok(TUI.includes('YOU RUN THE TOOLS'))
|
|
t.ok(TUI.includes('The user never runs your tools'))
|
|
t.ok(TUI.includes('discord_send_message'))
|
|
t.ok(TUI.includes('DISCORD CHANNEL'))
|
|
t.ok(TUI.includes('The user is not your tool runner'))
|
|
t.ok(TUI.includes('You can create files, edit files'))
|
|
t.ok(TUI.includes('Never say you cannot write files'))
|
|
t.ok(TUI.includes('ONLY your final answer'))
|
|
t.ok(TUI.includes('Lead with tools'))
|
|
t.ok(TUI.includes('TOOL DISCIPLINE'))
|
|
t.ok(TUI.includes('WORK POLICY'))
|
|
t.ok(TUI.includes('apply_patch'))
|
|
t.ok(TUI.includes('overrides the TTY plain-text rule'))
|
|
t.ok(!TUI.includes('ASK FIRST'))
|
|
t.ok(!TUI.includes('Prefer least-privilege'))
|
|
})
|
|
|
|
test('agent CLI exposes production flags and inspect subcommands', async (t) => {
|
|
t.ok(CLI.includes('--plan'))
|
|
t.ok(CLI.includes('--max-turns'))
|
|
t.ok(CLI.includes('--model'))
|
|
t.ok(CLI.includes('--system'))
|
|
t.ok(CLI.includes('--new'))
|
|
t.ok(CLI.includes('--compact'))
|
|
t.ok(CLI.includes("sub === 'skills'"))
|
|
t.ok(CLI.includes("sub === 'todos'"))
|
|
t.ok(CLI.includes("sub === 'undo'"))
|
|
t.ok(CLI.includes("sub === 'hooks'"))
|
|
t.ok(CLI.includes("sub === 'history'"))
|
|
t.ok(CLI.includes("sub === 'rewind'"))
|
|
t.ok(CLI.includes("sub === 'export'"))
|
|
t.ok(CLI.includes("sub === 'remember'"))
|
|
t.ok(CLI.includes("sub === 'recap'"))
|
|
t.ok(CLI.includes("sub === 'models'"))
|
|
t.ok(CLI.includes("sub === 'models' && rest[0] === 'models'"))
|
|
t.ok(CLI.includes('bareAgentApplyLiveModel'))
|
|
t.ok(TUI.includes('planMode'))
|
|
t.ok(TUI.includes('modelOverride'))
|
|
t.ok(TUI.includes('bareAgentHonorExplicitQvacModel'))
|
|
t.ok(TUI.includes('bareAgentSaveConfig'))
|
|
t.ok(TUI.includes('extraSystemFile'))
|
|
})
|
|
|
|
test('agent defaults are full guest admin (denylist, not allowlist)', async (t) => {
|
|
t.ok(STATE.includes("access_policy: 'full'"))
|
|
t.ok(STATE.includes('allow_delete: true'))
|
|
t.ok(STATE.includes('allow_bridge_mutations: true'))
|
|
t.ok(STATE.includes('allow_host_notifications: true'))
|
|
t.ok(STATE.includes('allow_host_actions: true'))
|
|
t.ok(STATE.includes('autonomous_mode_enabled: true'))
|
|
t.ok(STATE.includes('autonomous_deny_ops: []'))
|
|
t.ok(STATE.includes('command_deny: []'))
|
|
t.ok(TOOLS.includes("error: 'command_denied'"))
|
|
t.ok(!TOOLS.includes("error: 'autonomous_command_denied'"))
|
|
})
|