diff options
Diffstat (limited to 'modules/core/module-agent-registry.js')
| -rw-r--r-- | modules/core/module-agent-registry.js | 62 |
1 files changed, 47 insertions, 15 deletions
diff --git a/modules/core/module-agent-registry.js b/modules/core/module-agent-registry.js index 68dca66..b2c4797 100644 --- a/modules/core/module-agent-registry.js +++ b/modules/core/module-agent-registry.js @@ -35,8 +35,8 @@ class AgentRegistryModule { } init(terminal) { this.terminal = terminal; - if (!this.terminal.fs.readDir('/agents/')) this.terminal.fs.mkdir('agents'); - if (!this.terminal.fs.readDir('/.contracts/')) this.terminal.fs.mkdir('.contracts'); + if (!this.terminal.fs.readDir('/agents/')) this.terminal.fs.mkdirRecursive('/agents'); + if (!this.terminal.fs.readDir('/.contracts/')) this.terminal.fs.mkdirRecursive('/.contracts'); this._loadContracts(); // Initialize scratchpad this._ensureScratchpad(); @@ -50,7 +50,7 @@ class AgentRegistryModule { _saveDeltaTable() { try { const fs = this.terminal.fs; - if (!fs.readDir('/_docs/')) fs.mkdir('_docs'); + if (!fs.readDir('/_docs/')) fs.mkdirRecursive('/_docs'); const delta = `# Spec-vs-Reality Delta — 2026-06-17\n\n` + `| # | Whitepaper Concept | Spec'd Behavior | Current Reality | Δ | Priority |\n` + `|---|---|---|---|---|---|\n` + @@ -77,7 +77,7 @@ class AgentRegistryModule { const data = this.terminal.fs.fsReadJSON('/_agents/scratchpad.json'); if (!data) throw new Error('no scratchpad'); } catch (e) { - this.terminal.fs.mkdir('_agents'); + this.terminal.fs.mkdirRecursive('/_agents'); this.terminal.fs.fsWriteJSON('/_agents/scratchpad.json', { messages: [], seq: 0, @@ -94,7 +94,7 @@ class AgentRegistryModule { } _saveScratchpad(sp) { try { - this.terminal.fs.mkdir('_agents'); + this.terminal.fs.mkdirRecursive('/_agents'); this.terminal.fs.fsWriteJSON('/_agents/scratchpad.json', sp); } catch (e) { console.warn('Scratchpad save error:', e.message); @@ -353,11 +353,24 @@ class AgentRegistryModule { this.terminal.fs.fsWriteJSON('/.contracts/index.json', this._contracts); } catch (e) { console.error('Contract save error:', e); } } - registerAgent(args) { + registerAgent(rawArgs) { + // MAINT-008: callers pass the command word first (['register', id, cap, cap…]), and the + // old code took args[0] as the id and only args[1] as the capability list — so the boot + // log read `Agent 'register' registered with 1 capabilities`. Strip the command word and + // accept both `cap1,cap2` and `cap1 cap2 cap3`. + const args = (rawArgs && rawArgs[0] === 'register') ? rawArgs.slice(1) : (rawArgs || []); const id = args[0] || `agent-${Date.now()}`; - const capabilities = args[1] ? args[1].split(',').map(s => s.trim()).filter(Boolean) : ['list_files', 'read_file']; - const agentDir = `agents/${id}`; - if (!this.terminal.fs.readDir('/' + agentDir + '/')) this.terminal.fs.mkdir(agentDir); + const capsArg = args.slice(1).join(','); + const capabilities = capsArg + ? capsArg.split(',').map(s => s.trim()).filter(Boolean) + : ['list_files', 'read_file']; + // MAINT-007/009: relative mkdir/rm resolved against the user's cwd, so a registration made + // from /docs created /docs/agents/<id> and an unregister from anywhere else removed + // nothing. Every path here is absolute. + const agentDir = `/agents/${id}`; + if (!this.terminal.fs.readDir(agentDir + '/')) { + this.terminal.fs.mkdirRecursive(agentDir); + } this.terminal.fs.writeFile(`${agentDir}/status.json`, JSON.stringify({ id, status: 'idle', @@ -431,19 +444,38 @@ class AgentRegistryModule { if (!agents || agents.length === 0) { return this.terminal.print('No agents registered', 'info'); } - this.terminal.print(`📋 Registered Agents (${agents.length}):`, 'header'); + // MAINT-009: a directory alone is not a registration — anything without a readable + // status.json is a leftover directory, not an agent. Listing them as agents made a + // hand-made /agents/foo look like a live agent with unknown status. + const registered = [], orphaned = []; agents.forEach(a => { - if (a.type === 'dir' && a.name !== 'configured') { + if (a.type !== 'dir' || a.name === 'configured') return; + let s = null; + try { const data = this.terminal.fs.readFile(`/agents/${a.name}/status.json`); - const s = data ? JSON.parse(data) : { status: 'unknown', capabilities: [] }; - this.terminal.print(` ${a.name} — ${s.status} (${(s.capabilities || []).length} capabilities)${s.contract ? ` → contract:${s.contract}` : ''}`, 'info'); - } + if (data) s = JSON.parse(data); + } catch (e) { s = null; } + if (s && s.id) registered.push({ name: a.name, s }); + else orphaned.push(a.name); }); + if (registered.length) { + this.terminal.print(`📋 Registered Agents (${registered.length}):`, 'header'); + registered.forEach(({ name, s }) => { + this.terminal.print(` ${name} — ${s.status} (${(s.capabilities || []).length} capabilities)${s.contract ? ` → contract:${s.contract}` : ''}`, 'info'); + }); + } else { + this.terminal.print('No agents registered', 'info'); + } + if (orphaned.length) { + this.terminal.print(`⚠️ ${orphaned.length} directory(ies) in /agents have no status.json (not registered): ${orphaned.join(', ')}`, 'warning'); + } } unregisterAgent(id) { // Unregister from A2A scratchpad this.a2aUnregister(id); - const ok = this.terminal.fs.deleteFile('agents/' + id); + // MAINT-007: absolute path — a relative rm resolved against the cwd and left the + // directory behind whenever the user was not sitting in /. + const ok = this.terminal.fs.rm('/agents/' + id); if (ok) this.terminal.print(`✅ Agent '${id}' unregistered`, 'success'); else this.terminal.print(`❌ Agent '${id}' not found`, 'error'); } |