Further Agent updates
This commit is contained in:
@@ -611,6 +611,182 @@ test('gitignore, fuzzy, undo, history search', async (t) => {
|
||||
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(
|
||||
|
||||
Reference in New Issue
Block a user