Files
bare-operating-system/packages/bare-os-coreutils/test/agent-grok-port.test.mjs
T
snxraven d3aa66b0cc
Release rolling / release (push) Successful in 10m20s
Updates
2026-08-18 20:01:31 -04:00

968 lines
28 KiB
JavaScript

/**
* Guest-safe Grok Build ports: glob, todos, plan mode, memory, hooks, edits.
*/
import test from 'brittle'
import { readFileSync } from 'node:fs'
import vm from 'node:vm'
const STATE_SRC = readFileSync(new URL('../lib/agent/agent-state.js', import.meta.url), 'utf8')
const HELPERS_SRC = readFileSync(
new URL('../lib/agent/agent-helpers.js', import.meta.url),
'utf8'
)
const PORT_SRC = readFileSync(
new URL('../lib/agent/agent-grok-port.js', import.meta.url),
'utf8'
)
const TOOLS_SRC =
readFileSync(new URL('../lib/agent/agent-tools.js', import.meta.url), 'utf8') +
'\n' +
readFileSync(
new URL('../lib/agent/agent-tool-definitions.js', import.meta.url),
'utf8'
) +
'\n' +
readFileSync(
new URL('../lib/agent/agent-tool-dispatch.js', import.meta.url),
'utf8'
)
function loadPort() {
const sandbox = { TextDecoder, TextEncoder, Uint8Array, console }
vm.createContext(sandbox)
vm.runInContext(PORT_SRC, sandbox, { filename: 'agent-grok-port.js' })
return sandbox
}
function loadDispatch() {
const sandbox = { TextDecoder, TextEncoder, Uint8Array, console }
vm.createContext(sandbox)
vm.runInContext(STATE_SRC, sandbox, { filename: 'agent-state.js' })
vm.runInContext(HELPERS_SRC, sandbox, { filename: 'agent-helpers.js' })
vm.runInContext(PORT_SRC, sandbox, { filename: 'agent-grok-port.js' })
vm.runInContext(TOOLS_SRC, sandbox, { filename: 'agent-tools.js' })
return sandbox
}
function makeVfs(initial) {
/** @type {Map<string, string>} */
const files = new Map(Object.entries(initial || {}))
/** @type {Set<string>} */
const dirs = new Set(['/'])
for (const p of files.keys()) {
let dir = String(p).replace(/\/[^/]+$/, '')
while (dir && dir !== '/') {
dirs.add(dir)
dir = dir.replace(/\/[^/]+$/, '')
}
}
const b4a = {
from(v) {
return new TextEncoder().encode(typeof v === 'string' ? v : String(v))
},
toString(v) {
return new TextDecoder().decode(v)
}
}
const vfs = {
async mkdir(p) {
dirs.add(String(p).replace(/\/+$/, '') || '/')
},
async readFile(p) {
if (!files.has(p)) throw new Error('enoent:' + p)
return b4a.from(files.get(p) || '')
},
async writeFile(p, buf) {
const txt = buf instanceof Uint8Array ? new TextDecoder().decode(buf) : String(buf)
files.set(p, txt)
},
async unlink(p) {
if (!files.has(p)) throw new Error('enoent:' + p)
files.delete(p)
},
async rm(p, opts) {
if (opts && opts.recursive) {
for (const key of [...files.keys()]) {
if (key === p || String(key).startsWith(String(p) + '/')) files.delete(key)
}
return
}
if (!files.has(p)) throw new Error('enoent:' + p)
files.delete(p)
},
async readdir(p) {
const root = String(p).replace(/\/+$/, '') || '/'
if (!dirs.has(root) && ![...files.keys()].some((k) => k.startsWith(root + '/'))) {
throw new Error('enoent:' + p)
}
const prefix = root === '/' ? '/' : root + '/'
const names = new Set()
for (const key of [...files.keys(), ...dirs]) {
if (key === root) continue
if (!String(key).startsWith(prefix)) continue
const rest = String(key).slice(prefix.length)
const name = rest.split('/')[0]
if (name) names.add(name)
}
return [...names]
},
async lstat(p) {
const n = String(p).replace(/\/+$/, '') || '/'
if (dirs.has(n)) return { isDirectory: () => true }
if (files.has(n)) return { isDirectory: () => false }
throw new Error('enoent:' + p)
}
}
return { vfs, files, dirs, b4a }
}
async function dispatch(s, o) {
const fn = /** @type {(arg: object) => Promise<string>} */ (s.bareAgentDispatchTool)
const raw = await fn({
ctx: o.ctx,
paths: o.paths,
toolName: o.toolName,
argsJson: JSON.stringify(o.args || {}),
configRef: o.configRef || { current: {} },
appendProgress: o.appendProgress || (() => {}),
home: o.home || '/home/guest',
signal: null,
onTaskComplete: o.onTaskComplete || (() => {})
})
return JSON.parse(raw)
}
test('glob ** and * match relative and basename paths', async (t) => {
const s = loadPort()
t.ok(s.bareAgentGlobMatch('src/foo.js', '**/*.js'))
t.ok(s.bareAgentGlobMatch('foo.js', '*.js'))
t.ok(s.bareAgentGlobMatch('src/foo.js', '*.js'))
t.absent(s.bareAgentGlobMatch('src/foo.md', '**/*.js'))
})
test('todo merge updates by id and summarize counts open items', async (t) => {
const s = loadPort()
const first = s.bareAgentTodoApply(
[
{ id: 'a', content: 'one', status: 'pending' },
{ id: 'b', content: 'two', status: 'in_progress' }
],
{ merge: false },
[]
)
const next = s.bareAgentTodoApply(
[{ id: 'a', status: 'completed' }],
{ merge: true },
first
)
t.is(next.length, 2)
t.is(next[0].status, 'completed')
t.is(next[0].content, 'one')
const sum = s.bareAgentTodoSummarize(next)
t.is(sum.open, 1)
t.is(sum.completed, 1)
})
test('todo_write rejects empty and duplicate ids', async (t) => {
const s = loadPort()
t.exception(() => s.bareAgentTodoApply([{ content: 'x' }], { merge: false }, []))
t.exception(() =>
s.bareAgentTodoApply(
[
{ id: 'a', content: 'one' },
{ id: 'a', content: 'two' }
],
{ merge: false },
[]
)
)
})
test('search_replace requires unique old_string unless replace_all', async (t) => {
const s = loadPort()
const text = 'aaa\nbbb\naaa\n'
const miss = s.bareAgentSearchReplaceApply(text, 'zzz', 'q', false)
t.absent(miss.ok)
t.is(miss.error, 'old_string not found')
const many = s.bareAgentSearchReplaceApply(text, 'aaa', 'ccc', false)
t.absent(many.ok)
t.is(many.error, 'old_string not unique')
t.is(many.count, 2)
const all = s.bareAgentSearchReplaceApply(text, 'aaa', 'ccc', true)
t.ok(all.ok)
t.is(all.replacements, 2)
t.ok(String(all.next).includes('ccc\nbbb\nccc'))
})
test('slice file lines is 1-based and numbered', async (t) => {
const s = loadPort()
const sliced = s.bareAgentSliceFileLines('a\nb\nc\nd\n', { offset: 2, limit: 2 })
t.is(sliced.start_line, 2)
t.is(sliced.end_line, 3)
t.is(sliced.total_lines, 4)
t.ok(sliced.truncated)
t.is(sliced.content, '2→b\n3→c')
})
test('plan mode allows reads and plan.md writes only', async (t) => {
const s = loadPort()
const plan = '/home/guest/.agent/plan.md'
t.ok(s.bareAgentPlanModeToolAllowed('read_file', { path: '/etc/hosts' }, { plan }))
t.ok(s.bareAgentPlanModeToolAllowed('write_file', { path: plan }, { plan }))
t.absent(
s.bareAgentPlanModeToolAllowed('write_file', { path: '/home/guest/x.js' }, { plan })
)
t.ok(s.bareAgentPlanModeToolAllowed('todo_write', {}, { plan }))
})
test('todo nudge appears after idle turns with open items', async (t) => {
const s = loadPort()
t.is(s.bareAgentTodoNudgeText({ open: 2, turnsSinceTodoWrite: 2 }), '')
t.ok(s.bareAgentTodoNudgeText({ open: 2, turnsSinceTodoWrite: 3 }).includes('Open todos'))
t.ok(
s.bareAgentTodoNudgeText({ open: 0, turnsSinceTodoWrite: 5 }).includes('todo_write')
)
t.is(s.bareAgentTodoNudgeText({ open: 2, turnsSinceTodoWrite: 9, nudgeEnabled: false }), '')
})
test('pre-tool hook denies matching tool + regex', async (t) => {
const s = loadPort()
const reason = s.bareAgentHookDenies(
{
event: 'PreToolUse',
tools: ['run_command'],
deny_regex: 'rm\\s+-rf',
reason: 'no recursive delete'
},
'run_command',
{ command: 'rm -rf /tmp/x' }
)
t.is(reason, 'no recursive delete')
t.is(
s.bareAgentHookDenies(
{ event: 'PreToolUse', tools: ['run_command'], deny_regex: 'rm\\s+-rf' },
'read_file',
{ path: '/tmp/x' }
),
''
)
})
test('memory score ranks files that contain more query tokens', async (t) => {
const s = loadPort()
const tokens = s.bareAgentMemoryTokens('holesail discord whitelist')
t.ok(tokens.includes('holesail'))
t.ok(
s.bareAgentMemoryScore('holesail discord whitelist notes', tokens) >
s.bareAgentMemoryScore('unrelated pear notes', tokens)
)
})
test('walk-up discovers AGENTS.md and .grok/rules', async (t) => {
const s = loadPort()
const { vfs, b4a } = makeVfs({
'/home/guest/proj/src/AGENTS.md': 'leaf',
'/home/guest/proj/AGENTS.md': 'mid',
'/home/guest/proj/.grok/rules/style.md': 'rule'
})
await vfs.mkdir('/home/guest/proj/src')
await vfs.mkdir('/home/guest/proj/.grok/rules')
const found = await s.bareAgentDiscoverAgentsMdPaths(
{ vfs, b4a },
'/home/guest/proj/src',
8
)
t.ok(found.includes('/home/guest/proj/src/AGENTS.md'))
t.ok(found.includes('/home/guest/proj/AGENTS.md'))
t.ok(found.includes('/home/guest/proj/.grok/rules/style.md'))
})
test('dispatch glob_files / todo_write / plan_mode / unique edit', async (t) => {
const s = loadDispatch()
const { vfs, files, b4a } = makeVfs({
'/home/guest/src/a.js': 'const x = 1\nconst y = 1\n',
'/home/guest/src/b.md': '# hi\n',
'/home/guest/.agent/workspace/memory/notes.md': 'remember holesail keys',
'/home/guest/.agent/workspace/MEMORY.md': 'top memory holesail'
})
await vfs.mkdir('/home/guest/src')
await vfs.mkdir('/home/guest/.agent')
await vfs.mkdir('/home/guest/.agent/workspace')
await vfs.mkdir('/home/guest/.agent/workspace/memory')
const ctx = { vfs, b4a }
const paths = {
dir: '/home/guest/.agent',
config: '/home/guest/.agent/config.json',
todos: '/home/guest/.agent/todos.json',
plan: '/home/guest/.agent/plan.md',
hooks: '/home/guest/.agent/hooks',
ask: '/home/guest/.agent/ask.json',
workspace: '/home/guest/.agent/workspace',
workspaceMemory: '/home/guest/.agent/workspace/memory',
compact: '/home/guest/.agent/compact.md'
}
const configRef = { current: { plan_mode_active: false } }
const globbed = await dispatch(s, {
ctx,
paths,
toolName: 'glob_files',
args: { pattern: '**/*.js', root: '/home/guest' },
configRef,
home: '/home/guest'
})
t.ok(globbed.ok)
t.ok(globbed.files.includes('/home/guest/src/a.js'))
const todos = await dispatch(s, {
ctx,
paths,
toolName: 'todo_write',
args: {
merge: false,
todos: [{ id: 'g1', content: 'port glob', status: 'in_progress' }]
},
configRef,
home: '/home/guest'
})
t.ok(todos.ok)
t.is(todos.summary.open, 1)
t.ok(String(files.get(paths.todos) || '').includes('port glob'))
const mem = await dispatch(s, {
ctx,
paths,
toolName: 'memory_search',
args: { query: 'holesail' },
configRef,
home: '/home/guest'
})
t.ok(mem.ok)
t.ok(Array.isArray(mem.hits) && mem.hits.length >= 1)
configRef.current.plan_mode_active = true
const denied = await dispatch(s, {
ctx,
paths,
toolName: 'write_file',
args: { path: '/home/guest/src/a.js', content: 'nope' },
configRef,
home: '/home/guest'
})
t.absent(denied.ok)
t.is(denied.error, 'plan_mode_readonly')
const planned = await dispatch(s, {
ctx,
paths,
toolName: 'enter_plan_mode',
args: { note: 'draft first' },
configRef,
home: '/home/guest'
})
t.ok(planned.ok)
t.ok(String(files.get(paths.plan) || '').includes('draft first'))
const exited = await dispatch(s, {
ctx,
paths,
toolName: 'exit_plan_mode',
args: { summary: 'ready' },
configRef,
home: '/home/guest'
})
t.ok(exited.ok)
t.absent(configRef.current.plan_mode_active)
const uniqueFail = await dispatch(s, {
ctx,
paths,
toolName: 'search_replace',
args: {
path: '/home/guest/src/a.js',
old_string: 'const',
new_string: 'let'
},
configRef,
home: '/home/guest'
})
t.absent(uniqueFail.ok)
t.is(uniqueFail.error, 'old_string not unique')
const replaced = await dispatch(s, {
ctx,
paths,
toolName: 'search_replace',
args: {
path: '/home/guest/src/a.js',
old_string: 'const x = 1',
new_string: 'const x = 2'
},
configRef,
home: '/home/guest'
})
t.ok(replaced.ok)
t.is(replaced.replacements, 1)
t.ok(String(files.get('/home/guest/src/a.js') || '').includes('const x = 2'))
const sliced = await dispatch(s, {
ctx,
paths,
toolName: 'read_file',
args: { path: '/home/guest/src/a.js', offset: 1, limit: 1 },
configRef,
home: '/home/guest'
})
t.ok(sliced.ok)
t.ok(String(sliced.content || '').startsWith('1→'))
})
test('grep finds lines with glob and context', async (t) => {
const s = loadPort()
const { vfs, b4a } = makeVfs({
'/home/guest/src/a.js': 'const alpha = 1\nconst beta = 2\nconst gamma = 3\n',
'/home/guest/src/b.md': '# alpha docs\n'
})
await vfs.mkdir('/home/guest/src')
const out = await s.bareAgentGrepFiles(
{ vfs, b4a },
{ pattern: 'alpha', root: '/home/guest', glob: '**/*.js', context: 1 }
)
t.ok(out.ok)
t.is(out.count, 1)
t.ok(out.matches[0].path.endsWith('a.js'))
t.ok(String(out.matches[0].text).includes('beta'))
})
test('apply_patch add/update/delete', async (t) => {
const s = loadPort()
const { vfs, files, b4a } = makeVfs({
'/home/guest/src/a.js': 'const x = 1\nconst y = 2\n'
})
await vfs.mkdir('/home/guest/src')
const parsed = s.bareAgentParseApplyPatch(
[
'*** Begin Patch',
'*** Add File: /home/guest/src/new.js',
'+export const n = 1',
'*** Update File: /home/guest/src/a.js',
'@@',
' const x = 1',
'-const y = 2',
'+const y = 3',
'*** Delete File: /home/guest/src/gone.js',
'*** End Patch'
].join('\n')
)
t.ok(parsed.ok)
t.is(parsed.ops.length, 3)
files.set('/home/guest/src/gone.js', 'bye')
const applied = await s.bareAgentApplyPatchOps({ vfs, b4a }, parsed.ops, {
home: '/home/guest'
})
t.ok(applied.ok)
t.ok(String(files.get('/home/guest/src/new.js') || '').includes('export const n'))
t.ok(String(files.get('/home/guest/src/a.js') || '').includes('const y = 3'))
t.absent(files.has('/home/guest/src/gone.js'))
})
test('update_goal / grep dispatch', async (t) => {
const s = loadDispatch()
const { vfs, b4a } = makeVfs({
'/home/guest/src/a.js': 'hello world\n'
})
await vfs.mkdir('/home/guest/src')
await vfs.mkdir('/home/guest/.agent')
const ctx = { vfs, b4a }
const paths = {
dir: '/home/guest/.agent',
config: '/home/guest/.agent/config.json',
todos: '/home/guest/.agent/todos.json',
plan: '/home/guest/.agent/plan.md'
}
const configRef = { current: { autonomous_active: true, autonomous_status: 'running' } }
const grepped = await dispatch(s, {
ctx,
paths,
toolName: 'grep',
args: { pattern: 'hello', path: '/home/guest' },
configRef,
home: '/home/guest'
})
t.ok(grepped.ok)
t.ok(grepped.count >= 1)
const goal = await dispatch(s, {
ctx,
paths,
toolName: 'update_goal',
args: { completed: true, message: 'shipped' },
configRef,
home: '/home/guest'
})
t.ok(goal.ok)
t.is(configRef.current.autonomous_status, 'completed')
t.absent(configRef.current.autonomous_active)
})
test('schedule interval parse and memory_append', async (t) => {
const s = loadPort()
const five = s.bareAgentParseScheduleInterval('5m')
t.is(five.kind, 'everyMs')
t.is(five.everyMs, 300000)
const cron = s.bareAgentParseScheduleInterval('0 * * * *')
t.is(cron.kind, 'calendar')
t.is(s.bareAgentScheduleId('Ping Host'), 'agent-ping-host')
const d = loadDispatch()
const { vfs, files, b4a } = makeVfs({})
await vfs.mkdir('/home/guest/.agent/workspace')
const ctx = { vfs, b4a }
const paths = {
dir: '/home/guest/.agent',
workspace: '/home/guest/.agent/workspace',
workspaceMemory: '/home/guest/.agent/workspace/memory'
}
const mem = await dispatch(d, {
ctx,
paths,
toolName: 'memory_append',
args: { text: 'holesail keys live in state.json', kind: 'FACT' },
configRef: { current: {} },
home: '/home/guest'
})
t.ok(mem.ok)
t.ok(String(files.get(paths.workspace + '/MEMORY.md') || '').includes('FACT'))
const sched = await dispatch(d, {
ctx,
paths,
toolName: 'schedule_task',
args: { id: 'heartbeat', interval: '30m', prompt: 'report peer count' },
configRef: { current: {} },
home: '/home/guest'
})
t.ok(sched.ok)
t.ok(String(files.get(sched.path) || '').includes('EveryMs=1800000'))
t.ok(String(files.get(sched.path) || '').includes('agent --auto'))
})
test('gitignore, fuzzy, undo, history search', async (t) => {
const s = loadPort()
const rules = s.bareAgentParseIgnoreRules('*.log\n!keep.log\nbuild/\n')
t.ok(s.bareAgentIgnoreMatch('foo.log', false, rules))
t.absent(s.bareAgentIgnoreMatch('keep.log', false, rules))
t.ok(s.bareAgentIgnoreMatch('build/out.js', true, rules) || s.bareAgentIgnoreMatch('build', true, rules))
t.ok(s.bareAgentFuzzyScore('readme', '/home/x/README.md') > s.bareAgentFuzzyScore('readme', '/home/x/a.js'))
const hits = s.bareAgentHistorySearch(
[
{ role: 'user', content: 'fix the holesail tunnel' },
{ role: 'assistant', content: 'ok' }
],
'holesail',
4
)
t.ok(hits.length >= 1)
t.is(hits[0].role, 'user')
const d = loadDispatch()
const { vfs, files, b4a } = makeVfs({
'/home/guest/src/keep.js': 'const keep = 1\n',
'/home/guest/src/skip.log': 'noise\n',
'/home/guest/.gitignore': '*.log\n'
})
await vfs.mkdir('/home/guest/src')
await vfs.mkdir('/home/guest/.agent')
const ctx = { vfs, b4a }
const paths = {
dir: '/home/guest/.agent',
edits: '/home/guest/.agent/edits.json'
}
const globbed = await dispatch(d, {
ctx,
paths,
toolName: 'glob_files',
args: { pattern: '**/*', root: '/home/guest' },
configRef: { current: {} },
home: '/home/guest'
})
t.ok(globbed.ok)
t.ok(globbed.files.some((p) => /keep\.js$/.test(p)))
t.absent(globbed.files.some((p) => /skip\.log$/.test(p)))
const wrote = await dispatch(d, {
ctx,
paths,
toolName: 'write_file',
args: { path: '/home/guest/src/keep.js', content: 'changed\n' },
configRef: { current: {} },
home: '/home/guest'
})
t.ok(wrote.ok)
t.is(files.get('/home/guest/src/keep.js'), 'changed\n')
const undone = await dispatch(d, {
ctx,
paths,
toolName: 'undo_last_edit',
args: {},
configRef: { current: {} },
home: '/home/guest'
})
t.ok(undone.ok)
t.ok(String(files.get('/home/guest/src/keep.js') || '').includes('const keep'))
const waited = await dispatch(d, {
ctx,
paths,
toolName: 'wait_for',
args: { path: '/home/guest/src/keep.js', pattern: 'keep', timeout_ms: 500, interval_ms: 100 },
configRef: { current: {} },
home: '/home/guest'
})
t.ok(waited.ok)
t.ok(waited.matched)
})
test('rewind, export, unified diff, symbol regex', async (t) => {
const s = loadPort()
const hist = [
{ role: 'user', content: 'first' },
{ role: 'assistant', content: 'ok1' },
{ role: 'user', content: 'second' },
{ role: 'assistant', content: 'ok2' }
]
const points = s.bareAgentRewindPoints(hist)
t.is(points.length, 2)
const rewound = s.bareAgentRewindHistory(hist, { steps: 1 })
t.ok(rewound.ok)
t.is(rewound.dropped, 2)
t.is(rewound.messages.length, 2)
t.is(rewound.messages[0].content, 'first')
const md = s.bareAgentExportTranscript(hist)
t.ok(md.includes('# Agent session export'))
t.ok(md.includes('first'))
const diff = s.bareAgentUnifiedDiff('a\nb\nc\n', 'a\nB\nc\n', { from: '/a', to: '/b' })
t.ok(diff.ok)
t.absent(diff.identical)
t.ok(String(diff.text).includes('-b'))
t.ok(String(diff.text).includes('+B'))
const re = new RegExp(s.bareAgentSymbolRegex('bareAgentCopyPath'))
t.ok(re.test('async function bareAgentCopyPath(ctx, from, to) {'))
t.absent(re.test('const x = bareAgentCopyPath'))
})
test('copy, tree, find_symbol, create_skill, rewind dispatch', async (t) => {
const d = loadDispatch()
const { vfs, files, b4a } = makeVfs({
'/home/guest/src/lib.js': 'async function helloWorld() {\n return 1\n}\n',
'/home/guest/src/a.txt': 'alpha\n',
'/home/guest/src/b.txt': 'beta\n',
'/home/guest/.agent/history.json': JSON.stringify(
[
{ role: 'user', content: 'one' },
{ role: 'assistant', content: 'ok' },
{ role: 'user', content: 'two' },
{ role: 'assistant', content: 'ok2' }
],
null,
2
)
})
await vfs.mkdir('/home/guest/src')
await vfs.mkdir('/home/guest/.agent')
const ctx = { vfs, b4a }
const paths = {
dir: '/home/guest/.agent',
history: '/home/guest/.agent/history.json',
workspace: '/home/guest/.agent/workspace',
workspaceSkills: '/home/guest/.agent/workspace/skills',
workspaceMemory: '/home/guest/.agent/workspace/memory'
}
const copied = await dispatch(d, {
ctx,
paths,
toolName: 'copy_path',
args: { from_path: '/home/guest/src/a.txt', to_path: '/home/guest/src/a.copy.txt' },
home: '/home/guest'
})
t.ok(copied.ok)
t.is(files.get('/home/guest/src/a.copy.txt'), 'alpha\n')
const tree = await dispatch(d, {
ctx,
paths,
toolName: 'list_directory',
args: { path: '/home/guest/src', tree: true },
home: '/home/guest'
})
t.ok(tree.ok)
t.ok(String(tree.tree || '').includes('lib.js'))
const found = await dispatch(d, {
ctx,
paths,
toolName: 'find_symbol',
args: { name: 'helloWorld', root: '/home/guest/src' },
home: '/home/guest'
})
t.ok(found.ok)
t.ok(found.count >= 1)
const counted = await dispatch(d, {
ctx,
paths,
toolName: 'grep',
args: { pattern: 'hello', root: '/home/guest/src', output_mode: 'count' },
home: '/home/guest'
})
t.ok(counted.ok)
t.is(counted.output_mode, 'count')
t.ok(counted.count >= 1)
const skill = await dispatch(d, {
ctx,
paths,
toolName: 'create_skill',
args: { id: 'demo-skill', description: 'Demo', body: '# Demo\n\nDo the thing.\n' },
home: '/home/guest'
})
t.ok(skill.ok)
t.ok(String(files.get('/home/guest/.agent/workspace/skills/demo-skill/SKILL.md') || '').includes('name: demo-skill'))
const rewound = await dispatch(d, {
ctx,
paths,
toolName: 'rewind_session',
args: { steps: 1 },
home: '/home/guest'
})
t.ok(rewound.ok)
t.is(rewound.dropped, 2)
const hist = JSON.parse(files.get('/home/guest/.agent/history.json') || '[]')
t.is(hist.length, 2)
const remembered = await dispatch(d, {
ctx,
paths,
toolName: 'remember',
args: { text: 'holesail keys live in ~/.holesail' },
home: '/home/guest'
})
t.ok(remembered.ok)
t.is(remembered.kind, 'FACT')
t.ok(String(files.get('/home/guest/.agent/workspace/MEMORY.md') || '').includes('holesail keys'))
const exported = await dispatch(d, {
ctx,
paths,
toolName: 'export_session',
args: { path: '/home/guest/.agent/export.md' },
home: '/home/guest'
})
t.ok(exported.ok)
t.ok(String(files.get('/home/guest/.agent/export.md') || '').includes('# Agent session export'))
})
test('copy from read-only system path and project skill roots', async (t) => {
const d = loadDispatch()
const { vfs, files, b4a } = makeVfs({
'/bin/sh': 'readonly-shell\n',
'/home/guest/proj/.grok/skills/demo/SKILL.md':
'---\nname: demo\ndescription: Project skill\n---\n\n# Demo\n'
})
await vfs.mkdir('/bin')
await vfs.mkdir('/home/guest')
const ctx = { vfs, b4a, env: { PWD: '/home/guest/proj', HOME: '/home/guest' } }
const copied = await dispatch(d, {
ctx,
paths: { dir: '/home/guest/.agent', workspace: '/home/guest/.agent/workspace' },
toolName: 'copy_path',
args: { from_path: '/bin/sh', to_path: '/home/guest/sh.copy' },
home: '/home/guest'
})
t.ok(copied.ok)
t.is(files.get('/home/guest/sh.copy'), 'readonly-shell\n')
const blocked = await dispatch(d, {
ctx,
paths: { dir: '/home/guest/.agent' },
toolName: 'copy_path',
args: { from_path: '/home/guest/sh.copy', to_path: '/bin/sh.hijack' },
home: '/home/guest'
})
t.absent(blocked.ok)
t.is(blocked.error, 'path_not_allowed')
const s = loadPort()
const roots = await s.bareAgentDiscoverProjectSkillRoots(ctx, '/home/guest/proj/src')
t.ok(roots.some((r) => String(r.path || '').endsWith('/.grok/skills')))
})
test('search result parser extracts DDG-style topics', async (t) => {
const s = loadPort()
const rows = s.bareAgentParseSearchResults(
{
Heading: 'Bare',
AbstractURL: 'https://example.com/bare',
Abstract: 'A JS runtime',
RelatedTopics: [
{ Text: 'Pear', FirstURL: 'https://example.com/pear' },
{ Topics: [{ Text: 'Holepunch', FirstURL: 'https://example.com/hp' }] }
]
},
8
)
t.ok(rows.length >= 2)
t.ok(rows.some((r) => r.url === 'https://example.com/bare'))
})
test('dispatch delete / proc / run_command are open by default', async (t) => {
const s = loadDispatch()
const { vfs, files, b4a } = makeVfs({
'/home/guest/wipe-me.txt': 'gone soon',
'/bin/sh': 'readonly',
'/proc/bare_os/extra.json': '{"ok":true}'
})
await vfs.mkdir('/home/guest')
const ctx = { vfs, b4a }
const paths = {
dir: '/home/guest/.agent',
config: '/home/guest/.agent/config.json',
cmdOut: '/tmp/agent.out'
}
const configRef = {
current: {
allow_delete: true,
command_deny: [],
autonomous_active: true,
autonomous_allow_paths: ['*'],
autonomous_deny_ops: []
}
}
const del = await dispatch(s, {
ctx,
paths,
toolName: 'delete_path',
args: { path: '/home/guest/wipe-me.txt' },
configRef,
home: '/home/guest'
})
t.ok(del.ok)
t.absent(files.has('/home/guest/wipe-me.txt'))
const deniedSys = await dispatch(s, {
ctx,
paths,
toolName: 'write_file',
args: { path: '/bin/sh', content: 'nope' },
configRef,
home: '/home/guest'
})
t.absent(deniedSys.ok)
t.is(deniedSys.error, 'path_not_allowed')
const proc = await dispatch(s, {
ctx,
paths,
toolName: 'read_proc_file',
args: { path: '/proc/bare_os/extra.json' },
configRef,
home: '/home/guest'
})
t.ok(proc.ok)
t.ok(proc.json && proc.json.ok === true)
const seen = []
ctx.execLine = async (cmd) => {
seen.push(String(cmd))
await vfs.writeFile('/tmp/agent.out', b4a.from('ok\n'))
}
const run = await dispatch(s, {
ctx,
paths,
toolName: 'run_command',
args: { command: 'git status && rm -rf /tmp/x' },
configRef,
home: '/home/guest'
})
t.ok(run.ok)
t.ok(seen.length >= 1)
configRef.current.command_deny = ['rm -rf']
const blocked = await dispatch(s, {
ctx,
paths,
toolName: 'run_command',
args: { command: 'rm -rf /tmp/x' },
configRef,
home: '/home/guest'
})
t.absent(blocked.ok)
t.is(blocked.error, 'command_denied')
})
test('discord_send_message queues or uses the live DM hook', async (t) => {
const s = loadDispatch()
const { vfs, files, b4a } = makeVfs({})
await vfs.writeFile(
'~/.discord/channel.json',
b4a.from(JSON.stringify({ userId: '111', open: true }) + '\n')
)
const paths = {
dir: '/home/guest/.agent',
config: '/home/guest/.agent/config.json',
cmdOut: '/tmp/agent.out'
}
const queued = await dispatch(s, {
ctx: { vfs, b4a },
paths,
toolName: 'discord_send_message',
args: { text: 'hello from agent' },
home: '/home/guest'
})
t.ok(queued.ok)
t.ok(queued.queued)
t.ok(files.has('~/.discord/outbox.json'))
t.ok(String(files.get('~/.discord/outbox.json')).includes('hello from agent'))
const sent = []
const live = await dispatch(s, {
ctx: {
vfs,
b4a,
bareOsDiscordSendDm: async (opts) => {
sent.push(opts)
return { ok: true, queued: false, userId: '111' }
}
},
paths,
toolName: 'discord_send_message',
args: { text: 'live ping' },
home: '/home/guest'
})
t.ok(live.ok)
t.absent(live.queued)
t.is(sent[0].text, 'live ping')
const empty = await dispatch(s, {
ctx: { vfs, b4a },
paths,
toolName: 'discord_send_message',
args: { text: ' ' },
home: '/home/guest'
})
t.absent(empty.ok)
t.is(empty.error, 'empty_message')
const st = await dispatch(s, {
ctx: { vfs, b4a },
paths,
toolName: 'discord_channel_status',
args: {},
home: '/home/guest'
})
t.ok(st.ok)
t.is(st.userId, '111')
})