/** * Agent workspace loader (VM, same preamble order as /bin/agent). */ import test from 'brittle' import { readFileSync } from 'node:fs' import vm from 'node:vm' const CODE = readFileSync( new URL('../lib/agent-workspace.js', import.meta.url), 'utf8' ) /** @returns {Record} */ function load(extra = {}) { const sandbox = { TextDecoder, TextEncoder, Uint8Array, console, ...extra } vm.createContext(sandbox) vm.runInContext(CODE, sandbox, { filename: 'agent-workspace.js' }) return sandbox } test('bareAgentLoadWorkspacePrompt concatenates files', async (t) => { const files = new Map() files.set('/home/x/.agent/workspace/SOUL.md', '# Hi') files.set('/home/x/.agent/workspace/AGENTS.md', '# Rules') for (const n of [ 'IDENTITY.md', 'USER.md', 'TOOLS.md', 'MEMORY.md', 'BOOTSTRAP.md', 'HEARTBEAT.md', 'PROMPT.md' ]) files.set('/home/x/.agent/workspace/' + n, '# ' + n) const vfs = { readFile: async (p) => { const u = files.get(p) if (!u) throw new Error('enoent') return new TextEncoder().encode(u) } } const s = load() const loadFn = /** @type {(ctx: object, paths: object, cap?: number) => Promise} */ ( s.bareAgentLoadWorkspacePrompt ) const block = await loadFn( { vfs, b4a: null }, { workspace: '/home/x/.agent/workspace', workspaceMemory: '/home/x/.agent/workspace/memory' }, 50000 ) t.ok(block.includes('=== SOUL.md ===')) t.ok(block.includes('# Hi')) t.ok(block.includes('=== AGENTS.md ===')) }) test('bareAgentEnsureWorkspace seeds from share', async (t) => { const written = [] const files = new Map() const set = (p, txt) => files.set(p, txt) set('/share/agent-workspace/SOUL.md', '# Seeded soul') set('/share/agent-workspace/AGENTS.md', '# A') set('/share/agent-workspace/IDENTITY.md', '# I') set('/share/agent-workspace/USER.md', '# U') set('/share/agent-workspace/TOOLS.md', '# T') set('/share/agent-workspace/MEMORY.md', '# M') set('/share/agent-workspace/BOOTSTRAP.md', '# B') set('/share/agent-workspace/HEARTBEAT.md', '# H') set('/share/agent-workspace/PROMPT.md', '# P') set('/share/agent-workspace/memory/.gitkeep', '') set('/share/agent-workspace/loader.stub.js', '// l') set('/share/agent-workspace/index.stub.js', '// i') set('/share/agent-workspace/skill-loader.stub.js', '// sl') set('/share/agent-workspace/README-agent.md', '# R') const vfs = { async mkdir(_p, _o) {}, async readFile(p) { if (files.has(p)) return new TextEncoder().encode(files.get(p)) throw new Error('enoent') }, async writeFile(p, buf) { const txt = buf instanceof Uint8Array ? new TextDecoder().decode(buf) : String(buf) written.push([p, txt]) files.set(p, txt) } } const s = load() const ensure = /** @type {(ctx: object, paths: object) => Promise} */ ( s.bareAgentEnsureWorkspace ) await ensure( { vfs, b4a: null }, { dir: '/home/x/.agent', workspace: '/home/x/.agent/workspace', workspaceMemory: '/home/x/.agent/workspace/memory', workspaceSkills: '/home/x/.agent/workspace/skills' } ) t.ok(written.some(([p]) => p === '/home/x/.agent/workspace/SOUL.md')) const soul = written.find(([p]) => p === '/home/x/.agent/workspace/SOUL.md') t.ok(soul && soul[1].includes('Seeded soul')) t.ok(written.some(([p]) => p === '/home/x/.agent/skill-loader.js')) }) test('bareAgentEnsureSkillTemplates copies skill seeds when missing for xai provider', async (t) => { const written = [] const files = new Map() const set = (p, txt) => files.set(p, txt) set('/share/agent-workspace/skills/.gitkeep', '') set('/share/agent-workspace/skills/p2p-os-status/SKILL.md', '# Skill') set('/share/agent-workspace/skills/bare-os-kernel-proc/SKILL.md', '# Proc skill') set('/share/agent-workspace/skills/bare-os-super-developer/SKILL.md', '# Dev skill') set('/share/agent-workspace/skills/agent-ops/SKILL.md', '# Ops skill') set('/share/agent-workspace/skills/xai-compat/SKILL.md', '# xAI skill') set('/share/agent-workspace/skills/holesail/SKILL.md', '# Holesail skill') set('/share/agent-workspace/skills/hdms/SKILL.md', '# Hdms skill') set('/share/agent-workspace/skill-loader.stub.js', '// stub') const vfs = { async mkdir(_p, _o) {}, async readFile(p) { if (files.has(p)) return new TextEncoder().encode(files.get(p)) throw new Error('enoent') }, async writeFile(p, buf) { const txt = buf instanceof Uint8Array ? new TextDecoder().decode(buf) : String(buf) written.push([p, txt]) files.set(p, txt) } } const s = load() const fn = /** @type {(ctx: object, paths: object) => Promise} */ ( s.bareAgentEnsureSkillTemplates ) await fn( { vfs, b4a: null }, { dir: '/home/x/.agent', workspace: '/home/x/.agent/workspace', workspaceSkills: '/home/x/.agent/workspace/skills' }, { provider: 'xai' } ) t.ok(written.some(([p]) => p === '/home/x/.agent/workspace/skills/p2p-os-status/SKILL.md')) t.ok(written.some(([p]) => p === '/home/x/.agent/workspace/skills/bare-os-kernel-proc/SKILL.md')) t.ok(written.some(([p]) => p === '/home/x/.agent/workspace/skills/bare-os-super-developer/SKILL.md')) t.ok(written.some(([p]) => p === '/home/x/.agent/workspace/skills/agent-ops/SKILL.md')) t.ok(written.some(([p]) => p === '/home/x/.agent/workspace/skills/xai-compat/SKILL.md')) t.ok(written.some(([p]) => p === '/home/x/.agent/workspace/skills/holesail/SKILL.md')) t.ok(written.some(([p]) => p === '/home/x/.agent/workspace/skills/hdms/SKILL.md')) t.ok(written.some(([p]) => p === '/home/x/.agent/skill-loader.js')) }) test('bareAgentEnsureSkillTemplates skips xai skill for non-xai provider', async (t) => { const written = [] const files = new Map() const set = (p, txt) => files.set(p, txt) set('/share/agent-workspace/skills/.gitkeep', '') set('/share/agent-workspace/skills/p2p-os-status/SKILL.md', '# Skill') set('/share/agent-workspace/skills/bare-os-kernel-proc/SKILL.md', '# Proc skill') set('/share/agent-workspace/skills/bare-os-super-developer/SKILL.md', '# Dev skill') set('/share/agent-workspace/skills/agent-ops/SKILL.md', '# Ops skill') set('/share/agent-workspace/skills/xai-compat/SKILL.md', '# xAI skill') set('/share/agent-workspace/skills/holesail/SKILL.md', '# Holesail skill') set('/share/agent-workspace/skills/hdms/SKILL.md', '# Hdms skill') set('/share/agent-workspace/skill-loader.stub.js', '// stub') const vfs = { async mkdir(_p, _o) {}, async readFile(p) { if (files.has(p)) return new TextEncoder().encode(files.get(p)) throw new Error('enoent') }, async writeFile(p, buf) { const txt = buf instanceof Uint8Array ? new TextDecoder().decode(buf) : String(buf) written.push([p, txt]) files.set(p, txt) } } const s = load() const fn = /** @type {(ctx: object, paths: object, cfg?: object) => Promise} */ ( s.bareAgentEnsureSkillTemplates ) await fn( { vfs, b4a: null }, { dir: '/home/x/.agent', workspace: '/home/x/.agent/workspace', workspaceSkills: '/home/x/.agent/workspace/skills' }, { provider: 'groq' } ) t.ok(written.some(([p]) => p === '/home/x/.agent/workspace/skills/p2p-os-status/SKILL.md')) t.ok(!written.some(([p]) => p === '/home/x/.agent/workspace/skills/xai-compat/SKILL.md')) }) test('bareAgentSyncWorkspaceFromConfig writes IDENTITY and USER from config', async (t) => { const written = [] const vfs = { async mkdir(_p, _o) {}, async writeFile(p, buf) { const txt = buf instanceof Uint8Array ? new TextDecoder().decode(buf) : String(buf) written.push([p, txt]) } } const s = load() const sync = /** @type {(ctx: object, paths: object, cfg: object) => Promise} */ ( s.bareAgentSyncWorkspaceFromConfig ) await sync( { vfs, b4a: null }, { workspace: '/home/x/.agent/workspace' }, { owner_name: 'Raven', agent_label: 'Bear' } ) const id = written.find(([p]) => p === '/home/x/.agent/workspace/IDENTITY.md') t.ok(id && id[1].includes('**Name:** Bear')) t.ok(id && id[1].includes('Raven')) const user = written.find(([p]) => p === '/home/x/.agent/workspace/USER.md') t.ok(user && user[1].includes('Raven')) })