agent-manager-p1.test.js
5.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
/**
* Unit Tests — AgentManager.getAgentConfig() P1 虚拟引用寻址
*
* 覆盖 docs/todo/system-design/27-p1-virtual-reference.md §4.2 / §6-1,2,4:
* - config.json 带 systemAgentRef 且 system_agents 表命中 template 行 → prompt 指向共享
* agent.md,companyContextPath 指向私有 company-context.md
* - config.json 没有 systemAgentRef(legacy agent) → prompt 仍指向私有 agent.md,行为不变
*
* Uses GRAPH_DB_PATH env override (same pattern as graph-service.test.js) so the real
* data/graph.sqlite is never touched. Also creates a temporary company directory under
* data/companies/ that is removed in afterAll.
*/
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import fs from 'fs';
import path from 'path';
describe('AgentManager.getAgentConfig() — P1 virtual reference addressing', () => {
const TEST_DB_PATH = '/tmp/test-agent-manager-p1-graph.sqlite';
const REPO_ROOT = path.resolve(__dirname, '../../..');
const TMP_COMPANY_ID = `tmp-p1-test-co-${Date.now()}`;
const AGENTS_ROOT = path.join(REPO_ROOT, 'data/companies', TMP_COMPANY_ID, 'agents');
let graphDb;
let AgentManager;
beforeAll(() => {
if (fs.existsSync(TEST_DB_PATH)) fs.unlinkSync(TEST_DB_PATH);
process.env.GRAPH_DB_PATH = TEST_DB_PATH;
delete require.cache[require.resolve('../../services/graph-db')];
delete require.cache[require.resolve('../../services/graph-service')];
delete require.cache[require.resolve('../../src/core/agent-manager')];
graphDb = require('../../services/graph-db');
AgentManager = require('../../src/core/agent-manager');
// Seed a template row for "qa-judge-p1-test" pointing at a fixture-free legacy_path
// (the shared agent.md just needs to exist so lookupSharedAgentMd's result is a real file).
const db = graphDb.getDb();
const now = new Date().toISOString();
db.prepare(`
INSERT INTO system_agents (id, name, scope, owner_company_id, source_kind, legacy_path, status, benchmark_score, created_at, updated_at)
VALUES (?, ?, 'public', NULL, 'template', ?, 'active', NULL, ?, ?)
`).run('qa-judge', 'qa-judge', 'system/templates/qa-judge', now, now);
fs.mkdirSync(AGENTS_ROOT, { recursive: true });
});
afterAll(() => {
try { fs.rmSync(path.join(REPO_ROOT, 'data/companies', TMP_COMPANY_ID), { recursive: true, force: true }); } catch {}
try { fs.unlinkSync(TEST_DB_PATH); } catch {}
try { fs.unlinkSync(`${TEST_DB_PATH}-shm`); } catch {}
try { fs.unlinkSync(`${TEST_DB_PATH}-wal`); } catch {}
delete process.env.GRAPH_DB_PATH;
});
function makeAgentDir(role, configJson) {
const dir = path.join(AGENTS_ROOT, role);
fs.mkdirSync(dir, { recursive: true });
if (configJson !== undefined) {
fs.writeFileSync(path.join(dir, 'config.json'), JSON.stringify(configJson), 'utf-8');
}
// Legacy agent.md always present in the private dir (P1 doesn't delete it if it exists
// for the reference-mode case — company-service.hire() simply never writes it there).
fs.writeFileSync(path.join(dir, 'agent.md'), '# legacy private agent.md\n', 'utf-8');
return dir;
}
it('reference mode: systemAgentRef present + template row found -> prompt points at shared agent.md, companyContextPath set', () => {
const role = 'qa-judge-referenced';
const agentDir = makeAgentDir(role, { systemAgentRef: 'qa-judge' });
const manager = new AgentManager({}, REPO_ROOT);
const { agent } = manager.getAgentConfig(`${TMP_COMPANY_ID}:${role}`);
const expectedSharedPath = path.resolve(REPO_ROOT, 'system/templates/qa-judge', 'agent.md');
expect(agent.prompt).toBe(expectedSharedPath);
expect(agent.companyContextPath).toBe(path.join(agentDir, 'company-context.md'));
// Hard constraint: workDir must remain the private directory (CLI cwd for soul.md/identity.md/.claude/skills/).
expect(agent.workDir).toBe(agentDir);
});
it('legacy mode: no systemAgentRef -> prompt points at private agent.md, companyContextPath falsy', () => {
const role = 'legacy-role';
const agentDir = makeAgentDir(role, { someOtherField: true });
const manager = new AgentManager({}, REPO_ROOT);
const { agent } = manager.getAgentConfig(`${TMP_COMPANY_ID}:${role}`);
expect(agent.prompt).toBe(path.join(agentDir, 'agent.md'));
expect(agent.companyContextPath).toBeFalsy();
expect(agent.workDir).toBe(agentDir);
});
it('legacy mode: no config.json at all -> prompt points at private agent.md, companyContextPath falsy', () => {
const role = 'no-config-role';
const agentDir = makeAgentDir(role, undefined);
const manager = new AgentManager({}, REPO_ROOT);
const { agent } = manager.getAgentConfig(`${TMP_COMPANY_ID}:${role}`);
expect(agent.prompt).toBe(path.join(agentDir, 'agent.md'));
expect(agent.companyContextPath).toBeFalsy();
expect(agent.workDir).toBe(agentDir);
});
it('defensive fallback: systemAgentRef present but no matching template row -> falls back to private agent.md', () => {
const role = 'dangling-ref-role';
const agentDir = makeAgentDir(role, { systemAgentRef: 'does-not-exist-in-system-agents' });
const manager = new AgentManager({}, REPO_ROOT);
const { agent } = manager.getAgentConfig(`${TMP_COMPANY_ID}:${role}`);
expect(agent.prompt).toBe(path.join(agentDir, 'agent.md'));
expect(agent.companyContextPath).toBeNull();
expect(agent.workDir).toBe(agentDir);
});
});