@ai-devkit/agent-manager 0.41.0 → 0.42.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AgentManager.d.ts.map +1 -1
- package/dist/AgentManager.js +25 -9
- package/dist/AgentManager.js.map +1 -1
- package/dist/__tests__/fixtures.test.js.map +1 -1
- package/dist/__tests__/harnesses/devin/DevinAdapter.test.js.map +1 -1
- package/dist/__tests__/harnesses/devin/DevinSessionParser.test.js.map +1 -1
- package/dist/__tests__/harnesses/muse/MuseAdapter.test.js +247 -0
- package/dist/__tests__/harnesses/muse/MuseAdapter.test.js.map +1 -0
- package/dist/__tests__/harnesses/muse/MuseAgentMapper.test.js +81 -0
- package/dist/__tests__/harnesses/muse/MuseAgentMapper.test.js.map +1 -0
- package/dist/__tests__/harnesses/muse/MuseSessionLocator.test.js +185 -0
- package/dist/__tests__/harnesses/muse/MuseSessionLocator.test.js.map +1 -0
- package/dist/__tests__/harnesses/muse/MuseSessionParser.test.js +328 -0
- package/dist/__tests__/harnesses/muse/MuseSessionParser.test.js.map +1 -0
- package/dist/__tests__/harnesses/opencode/OpenCodeSessionParser.test.js.map +1 -1
- package/dist/__tests__/harnesses/readiness/AgentReadiness.test.js +42 -1
- package/dist/__tests__/harnesses/readiness/AgentReadiness.test.js.map +1 -1
- package/dist/__tests__/harnesses/runtimeProfiles.test.js +12 -0
- package/dist/__tests__/harnesses/runtimeProfiles.test.js.map +1 -1
- package/dist/__tests__/utils/process.test.js +71 -1
- package/dist/__tests__/utils/process.test.js.map +1 -1
- package/dist/adapters/AgentAdapter.d.ts +8 -1
- package/dist/adapters/AgentAdapter.d.ts.map +1 -1
- package/dist/adapters/AgentAdapter.js +2 -1
- package/dist/adapters/AgentAdapter.js.map +1 -1
- package/dist/fixtures/capture.d.ts.map +1 -1
- package/dist/fixtures/capture.js +5 -4
- package/dist/fixtures/capture.js.map +1 -1
- package/dist/harnesses/devin/DevinAdapter.d.ts.map +1 -1
- package/dist/harnesses/devin/DevinAdapter.js.map +1 -1
- package/dist/harnesses/devin/DevinAgentMapper.d.ts.map +1 -1
- package/dist/harnesses/devin/DevinAgentMapper.js.map +1 -1
- package/dist/harnesses/devin/DevinSessionParser.d.ts.map +1 -1
- package/dist/harnesses/devin/DevinSessionParser.js.map +1 -1
- package/dist/harnesses/index.d.ts.map +1 -1
- package/dist/harnesses/index.js +3 -1
- package/dist/harnesses/index.js.map +1 -1
- package/dist/harnesses/muse/MuseAdapter.d.ts +33 -0
- package/dist/harnesses/muse/MuseAdapter.d.ts.map +1 -0
- package/dist/harnesses/muse/MuseAdapter.js +122 -0
- package/dist/harnesses/muse/MuseAdapter.js.map +1 -0
- package/dist/harnesses/muse/MuseAgentMapper.d.ts +15 -0
- package/dist/harnesses/muse/MuseAgentMapper.d.ts.map +1 -0
- package/dist/harnesses/muse/MuseAgentMapper.js +29 -0
- package/dist/harnesses/muse/MuseAgentMapper.js.map +1 -0
- package/dist/harnesses/muse/MuseSessionLocator.d.ts +61 -0
- package/dist/harnesses/muse/MuseSessionLocator.d.ts.map +1 -0
- package/dist/harnesses/muse/MuseSessionLocator.js +233 -0
- package/dist/harnesses/muse/MuseSessionLocator.js.map +1 -0
- package/dist/harnesses/muse/MuseSessionParser.d.ts +77 -0
- package/dist/harnesses/muse/MuseSessionParser.d.ts.map +1 -0
- package/dist/harnesses/muse/MuseSessionParser.js +350 -0
- package/dist/harnesses/muse/MuseSessionParser.js.map +1 -0
- package/dist/harnesses/muse/readiness.d.ts +3 -0
- package/dist/harnesses/muse/readiness.d.ts.map +1 -0
- package/dist/harnesses/muse/readiness.js +37 -0
- package/dist/harnesses/muse/readiness.js.map +1 -0
- package/dist/harnesses/readiness/AgentReadiness.d.ts.map +1 -1
- package/dist/harnesses/readiness/AgentReadiness.js +3 -1
- package/dist/harnesses/readiness/AgentReadiness.js.map +1 -1
- package/dist/harnesses/readiness/types.d.ts +1 -1
- package/dist/harnesses/readiness/types.d.ts.map +1 -1
- package/dist/harnesses/runtimeProfiles.d.ts.map +1 -1
- package/dist/harnesses/runtimeProfiles.js +2 -1
- package/dist/harnesses/runtimeProfiles.js.map +1 -1
- package/dist/harnesses/shared.d.ts +1 -1
- package/dist/harnesses/shared.d.ts.map +1 -1
- package/dist/harnesses/shared.js +6 -5
- package/dist/harnesses/shared.js.map +1 -1
- package/dist/utils/process.d.ts +23 -1
- package/dist/utils/process.d.ts.map +1 -1
- package/dist/utils/process.js +44 -8
- package/dist/utils/process.js.map +1 -1
- package/package.json +1 -1
- package/src/AgentManager.ts +34 -8
- package/src/__tests__/fixtures.test.ts +15 -17
- package/src/__tests__/harnesses/devin/DevinAdapter.test.ts +1 -3
- package/src/__tests__/harnesses/devin/DevinSessionParser.test.ts +3 -9
- package/src/__tests__/harnesses/muse/MuseAdapter.test.ts +197 -0
- package/src/__tests__/harnesses/muse/MuseAgentMapper.test.ts +83 -0
- package/src/__tests__/harnesses/muse/MuseSessionLocator.test.ts +177 -0
- package/src/__tests__/harnesses/muse/MuseSessionParser.test.ts +279 -0
- package/src/__tests__/harnesses/opencode/OpenCodeSessionParser.test.ts +3 -2
- package/src/__tests__/harnesses/readiness/AgentReadiness.test.ts +36 -0
- package/src/__tests__/harnesses/runtimeProfiles.test.ts +13 -0
- package/src/__tests__/utils/process.test.ts +30 -0
- package/src/adapters/AgentAdapter.ts +9 -0
- package/src/fixtures/capture.ts +32 -17
- package/src/harnesses/devin/DevinAdapter.ts +3 -15
- package/src/harnesses/devin/DevinAgentMapper.ts +1 -5
- package/src/harnesses/devin/DevinSessionParser.ts +3 -12
- package/src/harnesses/index.ts +2 -0
- package/src/harnesses/muse/MuseAdapter.ts +157 -0
- package/src/harnesses/muse/MuseAgentMapper.ts +33 -0
- package/src/harnesses/muse/MuseSessionLocator.ts +286 -0
- package/src/harnesses/muse/MuseSessionParser.ts +435 -0
- package/src/harnesses/muse/readiness.ts +38 -0
- package/src/harnesses/readiness/AgentReadiness.ts +2 -0
- package/src/harnesses/runtimeProfiles.ts +1 -0
- package/src/harnesses/shared.ts +10 -5
- package/src/utils/process.ts +61 -9
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for harnesses/muse/MuseAdapter.ts — detection through the real
|
|
3
|
+
* locator/parser/mapper against a fake Muse home (no mocks).
|
|
4
|
+
*/ import * as fs from "fs";
|
|
5
|
+
import * as os from "os";
|
|
6
|
+
import * as path from "path";
|
|
7
|
+
import { AgentStatus } from "../../../adapters/AgentAdapter.js";
|
|
8
|
+
import { MuseAdapter } from "../../../harnesses/muse/MuseAdapter.js";
|
|
9
|
+
const SID = "01a12618-d918-7b63-91c8-4e896326eb1a";
|
|
10
|
+
const PROJECT = "codeaholicguy/Code/ai-devkit";
|
|
11
|
+
function frame(inner) {
|
|
12
|
+
return JSON.stringify(inner);
|
|
13
|
+
}
|
|
14
|
+
function intent(text, recordedAtUs) {
|
|
15
|
+
return {
|
|
16
|
+
schema_version: 1,
|
|
17
|
+
stream: {
|
|
18
|
+
kind: "session",
|
|
19
|
+
id: SID
|
|
20
|
+
},
|
|
21
|
+
sequence: 2,
|
|
22
|
+
recorded_at: recordedAtUs,
|
|
23
|
+
record_type: "event",
|
|
24
|
+
payload_type: "runtime.user_intent.accepted",
|
|
25
|
+
payload: {
|
|
26
|
+
refill_blocks: [
|
|
27
|
+
{
|
|
28
|
+
kind: "text",
|
|
29
|
+
text
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
describe("MuseAdapter", ()=>{
|
|
36
|
+
let home;
|
|
37
|
+
let adapter;
|
|
38
|
+
beforeEach(()=>{
|
|
39
|
+
home = fs.mkdtempSync(path.join(os.tmpdir(), "muse-adapter-test-"));
|
|
40
|
+
adapter = new MuseAdapter({
|
|
41
|
+
homeDir: home
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
afterEach(()=>{
|
|
45
|
+
fs.rmSync(home, {
|
|
46
|
+
recursive: true,
|
|
47
|
+
force: true
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
function writeTranscript(lines) {
|
|
51
|
+
const dir = path.join(home, ".local", "share", "muse", "sessions", "2026/10/10", SID);
|
|
52
|
+
fs.mkdirSync(dir, {
|
|
53
|
+
recursive: true
|
|
54
|
+
});
|
|
55
|
+
const filePath = path.join(dir, "session.jsonl");
|
|
56
|
+
fs.writeFileSync(filePath, lines.join("\n"));
|
|
57
|
+
return filePath;
|
|
58
|
+
}
|
|
59
|
+
function writeRuntimeFile(pid) {
|
|
60
|
+
const dir = path.join(home, ".local", "share", "muse", "runtime", "muse", "sessions");
|
|
61
|
+
fs.mkdirSync(dir, {
|
|
62
|
+
recursive: true
|
|
63
|
+
});
|
|
64
|
+
fs.writeFileSync(path.join(dir, `${SID}.json`), JSON.stringify({
|
|
65
|
+
schema_version: 1,
|
|
66
|
+
session_id: SID,
|
|
67
|
+
process_generation_hint: `pid=${pid}`
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
function proc(overrides = {}) {
|
|
71
|
+
return {
|
|
72
|
+
pid: 16174,
|
|
73
|
+
command: "/Users/tester/.local/bin/muse-bin-1.4.4-R5419.1",
|
|
74
|
+
cwd: `/Users/tester/${PROJECT}`,
|
|
75
|
+
tty: "ttys001",
|
|
76
|
+
startTime: new Date(),
|
|
77
|
+
...overrides
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
it("handles versioned launcher commands and rejects others", ()=>{
|
|
81
|
+
expect(adapter.canHandle(proc())).toBe(true);
|
|
82
|
+
expect(adapter.canHandle(proc({
|
|
83
|
+
command: "/usr/local/bin/muse"
|
|
84
|
+
}))).toBe(true);
|
|
85
|
+
expect(adapter.canHandle(proc({
|
|
86
|
+
command: "/usr/local/bin/claude"
|
|
87
|
+
}))).toBe(false);
|
|
88
|
+
expect(adapter.canHandle(proc({
|
|
89
|
+
command: "/usr/bin/museums"
|
|
90
|
+
}))).toBe(false);
|
|
91
|
+
});
|
|
92
|
+
it("detects a live session through the runtime file", async ()=>{
|
|
93
|
+
const filePath = writeTranscript([
|
|
94
|
+
frame({
|
|
95
|
+
schema_version: 1,
|
|
96
|
+
stream: {
|
|
97
|
+
kind: "session",
|
|
98
|
+
id: SID
|
|
99
|
+
},
|
|
100
|
+
sequence: 1,
|
|
101
|
+
recorded_at: 1791640525332173,
|
|
102
|
+
record_type: "event",
|
|
103
|
+
payload_type: "runtime.session.metadata",
|
|
104
|
+
payload: {
|
|
105
|
+
kind: "metadata",
|
|
106
|
+
record: {
|
|
107
|
+
workspace_root: `/Users/tester/${PROJECT}`
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}),
|
|
111
|
+
frame(intent("hello", 1791640525332174))
|
|
112
|
+
]);
|
|
113
|
+
writeRuntimeFile(16174);
|
|
114
|
+
const start = new Date(fs.statSync(filePath).birthtimeMs);
|
|
115
|
+
const agents = await adapter.detectAgents({
|
|
116
|
+
processes: [
|
|
117
|
+
proc({
|
|
118
|
+
startTime: start
|
|
119
|
+
})
|
|
120
|
+
]
|
|
121
|
+
});
|
|
122
|
+
expect(agents).toHaveLength(1);
|
|
123
|
+
expect(agents[0]).toMatchObject({
|
|
124
|
+
type: "muse",
|
|
125
|
+
pid: 16174,
|
|
126
|
+
sessionId: SID,
|
|
127
|
+
projectPath: `/Users/tester/${PROJECT}`,
|
|
128
|
+
summary: "hello"
|
|
129
|
+
});
|
|
130
|
+
expect(agents[0].sessionFilePath).toBe(filePath);
|
|
131
|
+
});
|
|
132
|
+
it("matches a process by cwd and birthtime without a runtime file", async ()=>{
|
|
133
|
+
const filePath = writeTranscript([
|
|
134
|
+
frame({
|
|
135
|
+
schema_version: 1,
|
|
136
|
+
stream: {
|
|
137
|
+
kind: "session",
|
|
138
|
+
id: SID
|
|
139
|
+
},
|
|
140
|
+
sequence: 1,
|
|
141
|
+
recorded_at: 1791640525332173,
|
|
142
|
+
record_type: "event",
|
|
143
|
+
payload_type: "runtime.session.metadata",
|
|
144
|
+
payload: {
|
|
145
|
+
kind: "metadata",
|
|
146
|
+
record: {
|
|
147
|
+
workspace_root: `/Users/tester/${PROJECT}`
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}),
|
|
151
|
+
frame(intent("legacy hello", 1791640525332174))
|
|
152
|
+
]);
|
|
153
|
+
const start = new Date(fs.statSync(filePath).birthtimeMs);
|
|
154
|
+
const agents = await adapter.detectAgents({
|
|
155
|
+
processes: [
|
|
156
|
+
proc({
|
|
157
|
+
pid: 7777,
|
|
158
|
+
startTime: start
|
|
159
|
+
})
|
|
160
|
+
]
|
|
161
|
+
});
|
|
162
|
+
expect(agents).toHaveLength(1);
|
|
163
|
+
expect(agents[0]).toMatchObject({
|
|
164
|
+
type: "muse",
|
|
165
|
+
pid: 7777,
|
|
166
|
+
sessionId: SID,
|
|
167
|
+
summary: "legacy hello"
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
it("skips transcripts with no conversation signal in history", async ()=>{
|
|
171
|
+
writeTranscript([
|
|
172
|
+
frame({
|
|
173
|
+
schema_version: 1,
|
|
174
|
+
stream: {
|
|
175
|
+
kind: "session",
|
|
176
|
+
id: SID
|
|
177
|
+
},
|
|
178
|
+
sequence: 1,
|
|
179
|
+
recorded_at: 1791640525332173,
|
|
180
|
+
record_type: "event",
|
|
181
|
+
payload_type: "runtime.session.metadata",
|
|
182
|
+
payload: {
|
|
183
|
+
kind: "metadata",
|
|
184
|
+
record: {
|
|
185
|
+
workspace_root: "/Users/tester/empty"
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
})
|
|
189
|
+
]);
|
|
190
|
+
expect(await adapter.listSessions()).toEqual([]);
|
|
191
|
+
});
|
|
192
|
+
it("reports an unmatched process as a running placeholder", async ()=>{
|
|
193
|
+
const agents = await adapter.detectAgents({
|
|
194
|
+
processes: [
|
|
195
|
+
proc({
|
|
196
|
+
pid: 4242
|
|
197
|
+
})
|
|
198
|
+
]
|
|
199
|
+
});
|
|
200
|
+
expect(agents).toHaveLength(1);
|
|
201
|
+
expect(agents[0]).toMatchObject({
|
|
202
|
+
type: "muse",
|
|
203
|
+
status: AgentStatus.RUNNING,
|
|
204
|
+
sessionId: "pid-4242"
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
it("lists historical sessions with strict cwd filtering", async ()=>{
|
|
208
|
+
writeTranscript([
|
|
209
|
+
frame({
|
|
210
|
+
schema_version: 1,
|
|
211
|
+
stream: {
|
|
212
|
+
kind: "session",
|
|
213
|
+
id: SID
|
|
214
|
+
},
|
|
215
|
+
sequence: 1,
|
|
216
|
+
recorded_at: 1791640525332173,
|
|
217
|
+
record_type: "event",
|
|
218
|
+
payload_type: "runtime.session.metadata",
|
|
219
|
+
payload: {
|
|
220
|
+
kind: "metadata",
|
|
221
|
+
record: {
|
|
222
|
+
workspace_root: `/Users/tester/${PROJECT}`
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}),
|
|
226
|
+
frame(intent("hello", 1791640525332174))
|
|
227
|
+
]);
|
|
228
|
+
const all = await adapter.listSessions();
|
|
229
|
+
expect(all).toHaveLength(1);
|
|
230
|
+
expect(all[0]).toMatchObject({
|
|
231
|
+
type: "muse",
|
|
232
|
+
sessionId: SID,
|
|
233
|
+
cwd: `/Users/tester/${PROJECT}`,
|
|
234
|
+
firstUserMessage: "hello"
|
|
235
|
+
});
|
|
236
|
+
expect(await adapter.listSessions({
|
|
237
|
+
cwd: "/elsewhere"
|
|
238
|
+
})).toEqual([]);
|
|
239
|
+
const found = await adapter.findSessionsById(SID);
|
|
240
|
+
expect(found.map((s)=>s.sessionId)).toEqual([
|
|
241
|
+
SID
|
|
242
|
+
]);
|
|
243
|
+
expect(await adapter.findSessionsById("00000000-0000-0000-0000-000000000000")).toEqual([]);
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
//# sourceMappingURL=MuseAdapter.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/__tests__/harnesses/muse/MuseAdapter.test.ts"],"sourcesContent":["/**\n * Tests for harnesses/muse/MuseAdapter.ts — detection through the real\n * locator/parser/mapper against a fake Muse home (no mocks).\n */\n\nimport * as fs from \"fs\";\nimport * as os from \"os\";\nimport * as path from \"path\";\nimport { AgentStatus } from \"../../../adapters/AgentAdapter.js\";\nimport type { ProcessInfo } from \"../../../adapters/AgentAdapter.js\";\nimport { MuseAdapter } from \"../../../harnesses/muse/MuseAdapter.js\";\n\nconst SID = \"01a12618-d918-7b63-91c8-4e896326eb1a\";\nconst PROJECT = \"codeaholicguy/Code/ai-devkit\";\n\nfunction frame(inner: unknown): string {\n return JSON.stringify(inner);\n}\n\nfunction intent(text: string, recordedAtUs: number): unknown {\n return {\n schema_version: 1,\n stream: { kind: \"session\", id: SID },\n sequence: 2,\n recorded_at: recordedAtUs,\n record_type: \"event\",\n payload_type: \"runtime.user_intent.accepted\",\n payload: { refill_blocks: [{ kind: \"text\", text }] },\n };\n}\n\ndescribe(\"MuseAdapter\", () => {\n let home: string;\n let adapter: MuseAdapter;\n\n beforeEach(() => {\n home = fs.mkdtempSync(path.join(os.tmpdir(), \"muse-adapter-test-\"));\n adapter = new MuseAdapter({ homeDir: home });\n });\n\n afterEach(() => {\n fs.rmSync(home, { recursive: true, force: true });\n });\n\n function writeTranscript(lines: string[]): string {\n const dir = path.join(home, \".local\", \"share\", \"muse\", \"sessions\", \"2026/10/10\", SID);\n fs.mkdirSync(dir, { recursive: true });\n const filePath = path.join(dir, \"session.jsonl\");\n fs.writeFileSync(filePath, lines.join(\"\\n\"));\n return filePath;\n }\n\n function writeRuntimeFile(pid: number): void {\n const dir = path.join(home, \".local\", \"share\", \"muse\", \"runtime\", \"muse\", \"sessions\");\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(\n path.join(dir, `${SID}.json`),\n JSON.stringify({ schema_version: 1, session_id: SID, process_generation_hint: `pid=${pid}` }),\n );\n }\n\n function proc(overrides: Partial<ProcessInfo> = {}): ProcessInfo {\n return {\n pid: 16174,\n command: \"/Users/tester/.local/bin/muse-bin-1.4.4-R5419.1\",\n cwd: `/Users/tester/${PROJECT}`,\n tty: \"ttys001\",\n startTime: new Date(),\n ...overrides,\n };\n }\n\n it(\"handles versioned launcher commands and rejects others\", () => {\n expect(adapter.canHandle(proc())).toBe(true);\n expect(adapter.canHandle(proc({ command: \"/usr/local/bin/muse\" }))).toBe(true);\n expect(adapter.canHandle(proc({ command: \"/usr/local/bin/claude\" }))).toBe(false);\n expect(adapter.canHandle(proc({ command: \"/usr/bin/museums\" }))).toBe(false);\n });\n\n it(\"detects a live session through the runtime file\", async () => {\n const filePath = writeTranscript([\n frame({\n schema_version: 1,\n stream: { kind: \"session\", id: SID },\n sequence: 1,\n recorded_at: 1791640525332173,\n record_type: \"event\",\n payload_type: \"runtime.session.metadata\",\n payload: {\n kind: \"metadata\",\n record: { workspace_root: `/Users/tester/${PROJECT}` },\n },\n }),\n frame(intent(\"hello\", 1791640525332174)),\n ]);\n writeRuntimeFile(16174);\n const start = new Date(fs.statSync(filePath).birthtimeMs);\n\n const agents = await adapter.detectAgents({ processes: [proc({ startTime: start })] });\n expect(agents).toHaveLength(1);\n expect(agents[0]).toMatchObject({\n type: \"muse\",\n pid: 16174,\n sessionId: SID,\n projectPath: `/Users/tester/${PROJECT}`,\n summary: \"hello\",\n });\n expect(agents[0].sessionFilePath).toBe(filePath);\n });\n\n it(\"matches a process by cwd and birthtime without a runtime file\", async () => {\n const filePath = writeTranscript([\n frame({\n schema_version: 1,\n stream: { kind: \"session\", id: SID },\n sequence: 1,\n recorded_at: 1791640525332173,\n record_type: \"event\",\n payload_type: \"runtime.session.metadata\",\n payload: {\n kind: \"metadata\",\n record: { workspace_root: `/Users/tester/${PROJECT}` },\n },\n }),\n frame(intent(\"legacy hello\", 1791640525332174)),\n ]);\n const start = new Date(fs.statSync(filePath).birthtimeMs);\n\n const agents = await adapter.detectAgents({\n processes: [proc({ pid: 7777, startTime: start })],\n });\n expect(agents).toHaveLength(1);\n expect(agents[0]).toMatchObject({\n type: \"muse\",\n pid: 7777,\n sessionId: SID,\n summary: \"legacy hello\",\n });\n });\n\n it(\"skips transcripts with no conversation signal in history\", async () => {\n writeTranscript([\n frame({\n schema_version: 1,\n stream: { kind: \"session\", id: SID },\n sequence: 1,\n recorded_at: 1791640525332173,\n record_type: \"event\",\n payload_type: \"runtime.session.metadata\",\n payload: { kind: \"metadata\", record: { workspace_root: \"/Users/tester/empty\" } },\n }),\n ]);\n expect(await adapter.listSessions()).toEqual([]);\n });\n\n it(\"reports an unmatched process as a running placeholder\", async () => {\n const agents = await adapter.detectAgents({ processes: [proc({ pid: 4242 })] });\n expect(agents).toHaveLength(1);\n expect(agents[0]).toMatchObject({\n type: \"muse\",\n status: AgentStatus.RUNNING,\n sessionId: \"pid-4242\",\n });\n });\n\n it(\"lists historical sessions with strict cwd filtering\", async () => {\n writeTranscript([\n frame({\n schema_version: 1,\n stream: { kind: \"session\", id: SID },\n sequence: 1,\n recorded_at: 1791640525332173,\n record_type: \"event\",\n payload_type: \"runtime.session.metadata\",\n payload: {\n kind: \"metadata\",\n record: { workspace_root: `/Users/tester/${PROJECT}` },\n },\n }),\n frame(intent(\"hello\", 1791640525332174)),\n ]);\n\n const all = await adapter.listSessions();\n expect(all).toHaveLength(1);\n expect(all[0]).toMatchObject({\n type: \"muse\",\n sessionId: SID,\n cwd: `/Users/tester/${PROJECT}`,\n firstUserMessage: \"hello\",\n });\n\n expect(await adapter.listSessions({ cwd: \"/elsewhere\" })).toEqual([]);\n const found = await adapter.findSessionsById(SID);\n expect(found.map((s) => s.sessionId)).toEqual([SID]);\n expect(await adapter.findSessionsById(\"00000000-0000-0000-0000-000000000000\")).toEqual([]);\n });\n});\n"],"names":["fs","os","path","AgentStatus","MuseAdapter","SID","PROJECT","frame","inner","JSON","stringify","intent","text","recordedAtUs","schema_version","stream","kind","id","sequence","recorded_at","record_type","payload_type","payload","refill_blocks","describe","home","adapter","beforeEach","mkdtempSync","join","tmpdir","homeDir","afterEach","rmSync","recursive","force","writeTranscript","lines","dir","mkdirSync","filePath","writeFileSync","writeRuntimeFile","pid","session_id","process_generation_hint","proc","overrides","command","cwd","tty","startTime","Date","it","expect","canHandle","toBe","record","workspace_root","start","statSync","birthtimeMs","agents","detectAgents","processes","toHaveLength","toMatchObject","type","sessionId","projectPath","summary","sessionFilePath","listSessions","toEqual","status","RUNNING","all","firstUserMessage","found","findSessionsById","map","s"],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { AgentStatus } from "../../../adapters/AgentAdapter.js";
|
|
2
|
+
import { MuseAgentMapper } from "../../../harnesses/muse/MuseAgentMapper.js";
|
|
3
|
+
const SID = "01a12618-d918-7b63-91c8-4e896326eb1a";
|
|
4
|
+
function session(overrides = {}) {
|
|
5
|
+
return {
|
|
6
|
+
sessionId: SID,
|
|
7
|
+
projectPath: "/Users/tester/proj",
|
|
8
|
+
sessionStart: new Date("2026-10-10T13:55:25.332Z"),
|
|
9
|
+
lastActive: new Date("2026-10-10T14:00:00.000Z"),
|
|
10
|
+
lastSignal: "assistant",
|
|
11
|
+
lastUserMessage: "what is this repo about",
|
|
12
|
+
firstUserMessage: "what is this repo about",
|
|
13
|
+
...overrides
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
function sessionFile() {
|
|
17
|
+
return {
|
|
18
|
+
sessionId: SID,
|
|
19
|
+
filePath: `/home/tester/.local/share/muse/sessions/2026/10/10/${SID}/session.jsonl`,
|
|
20
|
+
projectDir: `/home/tester/.local/share/muse/sessions/2026/10/10/${SID}`,
|
|
21
|
+
birthtimeMs: 1791640525332,
|
|
22
|
+
resolvedCwd: "/Users/tester/proj"
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function proc() {
|
|
26
|
+
return {
|
|
27
|
+
pid: 16174,
|
|
28
|
+
command: "/Users/tester/.local/bin/muse-bin-1.4.4-R5419.1",
|
|
29
|
+
cwd: "/Users/tester/proj",
|
|
30
|
+
tty: "ttys001"
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
describe("MuseAgentMapper", ()=>{
|
|
34
|
+
const mapper = new MuseAgentMapper();
|
|
35
|
+
it("maps a session to a project-named agent", ()=>{
|
|
36
|
+
const agent = mapper.mapSessionToAgent({
|
|
37
|
+
session: session(),
|
|
38
|
+
processInfo: proc(),
|
|
39
|
+
sessionFile: sessionFile()
|
|
40
|
+
});
|
|
41
|
+
expect(agent).toMatchObject({
|
|
42
|
+
name: "proj-16174",
|
|
43
|
+
type: "muse",
|
|
44
|
+
status: AgentStatus.WAITING,
|
|
45
|
+
summary: "what is this repo about",
|
|
46
|
+
pid: 16174,
|
|
47
|
+
projectPath: "/Users/tester/proj",
|
|
48
|
+
sessionId: SID
|
|
49
|
+
});
|
|
50
|
+
expect(agent.lastActive).toEqual(new Date("2026-10-10T14:00:00.000Z"));
|
|
51
|
+
expect(agent.sessionFilePath).toContain("session.jsonl");
|
|
52
|
+
});
|
|
53
|
+
it("falls back to a started summary and process cwd without workspace root", ()=>{
|
|
54
|
+
const agent = mapper.mapSessionToAgent({
|
|
55
|
+
session: session({
|
|
56
|
+
projectPath: "",
|
|
57
|
+
lastUserMessage: undefined,
|
|
58
|
+
lastSignal: undefined
|
|
59
|
+
}),
|
|
60
|
+
processInfo: proc(),
|
|
61
|
+
sessionFile: {
|
|
62
|
+
...sessionFile(),
|
|
63
|
+
resolvedCwd: ""
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
expect(agent.summary).toBe("Session started");
|
|
67
|
+
expect(agent.projectPath).toBe("/Users/tester/proj");
|
|
68
|
+
expect(agent.status).toBe(AgentStatus.UNKNOWN);
|
|
69
|
+
});
|
|
70
|
+
it("maps an unmatched process to a running placeholder", ()=>{
|
|
71
|
+
const agent = mapper.mapProcessOnlyAgent(proc());
|
|
72
|
+
expect(agent).toMatchObject({
|
|
73
|
+
type: "muse",
|
|
74
|
+
status: AgentStatus.RUNNING,
|
|
75
|
+
pid: 16174,
|
|
76
|
+
sessionId: "pid-16174"
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
//# sourceMappingURL=MuseAgentMapper.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/__tests__/harnesses/muse/MuseAgentMapper.test.ts"],"sourcesContent":["import { AgentStatus } from \"../../../adapters/AgentAdapter.js\";\nimport type { ProcessInfo } from \"../../../adapters/AgentAdapter.js\";\nimport type { SessionFile } from \"../../../utils/session.js\";\nimport { MuseAgentMapper } from \"../../../harnesses/muse/MuseAgentMapper.js\";\nimport type { MuseSession } from \"../../../harnesses/muse/MuseSessionParser.js\";\n\nconst SID = \"01a12618-d918-7b63-91c8-4e896326eb1a\";\n\nfunction session(overrides: Partial<MuseSession> = {}): MuseSession {\n return {\n sessionId: SID,\n projectPath: \"/Users/tester/proj\",\n sessionStart: new Date(\"2026-10-10T13:55:25.332Z\"),\n lastActive: new Date(\"2026-10-10T14:00:00.000Z\"),\n lastSignal: \"assistant\",\n lastUserMessage: \"what is this repo about\",\n firstUserMessage: \"what is this repo about\",\n ...overrides,\n };\n}\n\nfunction sessionFile(): SessionFile {\n return {\n sessionId: SID,\n filePath: `/home/tester/.local/share/muse/sessions/2026/10/10/${SID}/session.jsonl`,\n projectDir: `/home/tester/.local/share/muse/sessions/2026/10/10/${SID}`,\n birthtimeMs: 1791640525332,\n resolvedCwd: \"/Users/tester/proj\",\n };\n}\n\nfunction proc(): ProcessInfo {\n return {\n pid: 16174,\n command: \"/Users/tester/.local/bin/muse-bin-1.4.4-R5419.1\",\n cwd: \"/Users/tester/proj\",\n tty: \"ttys001\",\n };\n}\n\ndescribe(\"MuseAgentMapper\", () => {\n const mapper = new MuseAgentMapper();\n\n it(\"maps a session to a project-named agent\", () => {\n const agent = mapper.mapSessionToAgent({\n session: session(),\n processInfo: proc(),\n sessionFile: sessionFile(),\n });\n expect(agent).toMatchObject({\n name: \"proj-16174\",\n type: \"muse\",\n status: AgentStatus.WAITING,\n summary: \"what is this repo about\",\n pid: 16174,\n projectPath: \"/Users/tester/proj\",\n sessionId: SID,\n });\n expect(agent.lastActive).toEqual(new Date(\"2026-10-10T14:00:00.000Z\"));\n expect(agent.sessionFilePath).toContain(\"session.jsonl\");\n });\n\n it(\"falls back to a started summary and process cwd without workspace root\", () => {\n const agent = mapper.mapSessionToAgent({\n session: session({ projectPath: \"\", lastUserMessage: undefined, lastSignal: undefined }),\n processInfo: proc(),\n sessionFile: { ...sessionFile(), resolvedCwd: \"\" },\n });\n expect(agent.summary).toBe(\"Session started\");\n expect(agent.projectPath).toBe(\"/Users/tester/proj\");\n expect(agent.status).toBe(AgentStatus.UNKNOWN);\n });\n\n it(\"maps an unmatched process to a running placeholder\", () => {\n const agent = mapper.mapProcessOnlyAgent(proc());\n expect(agent).toMatchObject({\n type: \"muse\",\n status: AgentStatus.RUNNING,\n pid: 16174,\n sessionId: \"pid-16174\",\n });\n });\n});\n"],"names":["AgentStatus","MuseAgentMapper","SID","session","overrides","sessionId","projectPath","sessionStart","Date","lastActive","lastSignal","lastUserMessage","firstUserMessage","sessionFile","filePath","projectDir","birthtimeMs","resolvedCwd","proc","pid","command","cwd","tty","describe","mapper","it","agent","mapSessionToAgent","processInfo","expect","toMatchObject","name","type","status","WAITING","summary","toEqual","sessionFilePath","toContain","undefined","toBe","UNKNOWN","mapProcessOnlyAgent","RUNNING"],"mappings":"AAAA,SAASA,WAAW,QAAQ,oCAAoC;AAGhE,SAASC,eAAe,QAAQ,6CAA6C;AAG7E,MAAMC,MAAM;AAEZ,SAASC,QAAQC,YAAkC,CAAC,CAAC;IACnD,OAAO;QACLC,WAAWH;QACXI,aAAa;QACbC,cAAc,IAAIC,KAAK;QACvBC,YAAY,IAAID,KAAK;QACrBE,YAAY;QACZC,iBAAiB;QACjBC,kBAAkB;QAClB,GAAGR,SAAS;IACd;AACF;AAEA,SAASS;IACP,OAAO;QACLR,WAAWH;QACXY,UAAU,CAAC,mDAAmD,EAAEZ,IAAI,cAAc,CAAC;QACnFa,YAAY,CAAC,mDAAmD,EAAEb,KAAK;QACvEc,aAAa;QACbC,aAAa;IACf;AACF;AAEA,SAASC;IACP,OAAO;QACLC,KAAK;QACLC,SAAS;QACTC,KAAK;QACLC,KAAK;IACP;AACF;AAEAC,SAAS,mBAAmB;IAC1B,MAAMC,SAAS,IAAIvB;IAEnBwB,GAAG,2CAA2C;QAC5C,MAAMC,QAAQF,OAAOG,iBAAiB,CAAC;YACrCxB,SAASA;YACTyB,aAAaV;YACbL,aAAaA;QACf;QACAgB,OAAOH,OAAOI,aAAa,CAAC;YAC1BC,MAAM;YACNC,MAAM;YACNC,QAAQjC,YAAYkC,OAAO;YAC3BC,SAAS;YACThB,KAAK;YACLb,aAAa;YACbD,WAAWH;QACb;QACA2B,OAAOH,MAAMjB,UAAU,EAAE2B,OAAO,CAAC,IAAI5B,KAAK;QAC1CqB,OAAOH,MAAMW,eAAe,EAAEC,SAAS,CAAC;IAC1C;IAEAb,GAAG,0EAA0E;QAC3E,MAAMC,QAAQF,OAAOG,iBAAiB,CAAC;YACrCxB,SAASA,QAAQ;gBAAEG,aAAa;gBAAIK,iBAAiB4B;gBAAW7B,YAAY6B;YAAU;YACtFX,aAAaV;YACbL,aAAa;gBAAE,GAAGA,aAAa;gBAAEI,aAAa;YAAG;QACnD;QACAY,OAAOH,MAAMS,OAAO,EAAEK,IAAI,CAAC;QAC3BX,OAAOH,MAAMpB,WAAW,EAAEkC,IAAI,CAAC;QAC/BX,OAAOH,MAAMO,MAAM,EAAEO,IAAI,CAACxC,YAAYyC,OAAO;IAC/C;IAEAhB,GAAG,sDAAsD;QACvD,MAAMC,QAAQF,OAAOkB,mBAAmB,CAACxB;QACzCW,OAAOH,OAAOI,aAAa,CAAC;YAC1BE,MAAM;YACNC,QAAQjC,YAAY2C,OAAO;YAC3BxB,KAAK;YACLd,WAAW;QACb;IACF;AACF"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for harnesses/muse/MuseSessionLocator.ts — runtime-file matching,
|
|
3
|
+
* dated-archive discovery, and session lookup by id.
|
|
4
|
+
*/ import * as fs from "fs";
|
|
5
|
+
import * as os from "os";
|
|
6
|
+
import * as path from "path";
|
|
7
|
+
import { MuseSessionLocator } from "../../../harnesses/muse/MuseSessionLocator.js";
|
|
8
|
+
const SID = "01a12618-d918-7b63-91c8-4e896326eb1a";
|
|
9
|
+
const OTHER_SID = "2288dbd2-d703-444d-afce-59e40a8ad8b1";
|
|
10
|
+
function frame(inner) {
|
|
11
|
+
return JSON.stringify(inner);
|
|
12
|
+
}
|
|
13
|
+
function intent(text, recordedAtUs) {
|
|
14
|
+
return {
|
|
15
|
+
schema_version: 1,
|
|
16
|
+
stream: {
|
|
17
|
+
kind: "session",
|
|
18
|
+
id: SID
|
|
19
|
+
},
|
|
20
|
+
sequence: 2,
|
|
21
|
+
recorded_at: recordedAtUs,
|
|
22
|
+
record_type: "event",
|
|
23
|
+
payload_type: "runtime.user_intent.accepted",
|
|
24
|
+
payload: {
|
|
25
|
+
refill_blocks: [
|
|
26
|
+
{
|
|
27
|
+
kind: "text",
|
|
28
|
+
text
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function metadata(workspaceRoot, recordedAtUs) {
|
|
35
|
+
return {
|
|
36
|
+
schema_version: 1,
|
|
37
|
+
stream: {
|
|
38
|
+
kind: "session",
|
|
39
|
+
id: SID
|
|
40
|
+
},
|
|
41
|
+
sequence: 1,
|
|
42
|
+
recorded_at: recordedAtUs,
|
|
43
|
+
record_type: "event",
|
|
44
|
+
payload_type: "runtime.session.metadata",
|
|
45
|
+
payload: {
|
|
46
|
+
kind: "metadata",
|
|
47
|
+
record: {
|
|
48
|
+
workspace_root: workspaceRoot
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function proc(pid, cwd, startTime) {
|
|
54
|
+
return {
|
|
55
|
+
pid,
|
|
56
|
+
command: `/Users/tester/.local/bin/muse-bin-1.4.4-R5419.1`,
|
|
57
|
+
cwd,
|
|
58
|
+
tty: "ttys001",
|
|
59
|
+
startTime
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
describe("MuseSessionLocator", ()=>{
|
|
63
|
+
let home;
|
|
64
|
+
let locator;
|
|
65
|
+
beforeEach(()=>{
|
|
66
|
+
home = fs.mkdtempSync(path.join(os.tmpdir(), "muse-locator-test-"));
|
|
67
|
+
locator = new MuseSessionLocator({
|
|
68
|
+
homeDir: home
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
afterEach(()=>{
|
|
72
|
+
fs.rmSync(home, {
|
|
73
|
+
recursive: true,
|
|
74
|
+
force: true
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
function writeTranscript(sessionId, dayDir, lines) {
|
|
78
|
+
const dir = path.join(home, ".local", "share", "muse", "sessions", dayDir, sessionId);
|
|
79
|
+
fs.mkdirSync(dir, {
|
|
80
|
+
recursive: true
|
|
81
|
+
});
|
|
82
|
+
const filePath = path.join(dir, "session.jsonl");
|
|
83
|
+
fs.writeFileSync(filePath, lines.join("\n"));
|
|
84
|
+
return filePath;
|
|
85
|
+
}
|
|
86
|
+
function writeRuntimeFile(sessionId, pid) {
|
|
87
|
+
const dir = path.join(home, ".local", "share", "muse", "runtime", "muse", "sessions");
|
|
88
|
+
fs.mkdirSync(dir, {
|
|
89
|
+
recursive: true
|
|
90
|
+
});
|
|
91
|
+
const filePath = path.join(dir, `${sessionId}.jsonl`.replace(".jsonl", ".json"));
|
|
92
|
+
fs.writeFileSync(filePath, JSON.stringify({
|
|
93
|
+
schema_version: 1,
|
|
94
|
+
session_id: sessionId,
|
|
95
|
+
process_generation_hint: `pid=${pid}`
|
|
96
|
+
}));
|
|
97
|
+
return filePath;
|
|
98
|
+
}
|
|
99
|
+
it("matches a live process to its transcript via the runtime file", ()=>{
|
|
100
|
+
const now = Date.now();
|
|
101
|
+
const filePath = writeTranscript(SID, "2026/10/10", [
|
|
102
|
+
frame(metadata("/Users/tester/proj", now * 1000)),
|
|
103
|
+
frame(intent("hello", now * 1000))
|
|
104
|
+
]);
|
|
105
|
+
writeRuntimeFile(SID, 16174);
|
|
106
|
+
// Align file birthtime with process start for the staleness guard.
|
|
107
|
+
const start = new Date(fs.statSync(filePath).birthtimeMs);
|
|
108
|
+
const { direct } = locator.matchRunningProcesses([
|
|
109
|
+
proc(16174, "/Users/tester/proj", start)
|
|
110
|
+
]);
|
|
111
|
+
expect(direct).toHaveLength(1);
|
|
112
|
+
expect(direct[0].process.pid).toBe(16174);
|
|
113
|
+
expect(direct[0].sessionFile.filePath).toBe(filePath);
|
|
114
|
+
expect(direct[0].sessionFile.sessionId).toBe(SID);
|
|
115
|
+
});
|
|
116
|
+
it("rejects runtime matches when the pid was recycled", ()=>{
|
|
117
|
+
writeTranscript(SID, "2026/10/10", [
|
|
118
|
+
frame(intent("hello", 1791640525332173))
|
|
119
|
+
]);
|
|
120
|
+
writeRuntimeFile(SID, 16174);
|
|
121
|
+
// Process started an hour after the session file was born: recycled pid.
|
|
122
|
+
const { direct, fallback } = locator.matchRunningProcesses([
|
|
123
|
+
proc(16174, "/Users/tester/proj", new Date(Date.now() + 3600_000))
|
|
124
|
+
]);
|
|
125
|
+
expect(direct).toHaveLength(0);
|
|
126
|
+
expect(fallback.map((p)=>p.pid)).toContain(16174);
|
|
127
|
+
});
|
|
128
|
+
it("ignores malformed runtime files (processes fall through to legacy)", ()=>{
|
|
129
|
+
writeTranscript(SID, "2026/10/10", [
|
|
130
|
+
frame(intent("hello", 1791640525332173))
|
|
131
|
+
]);
|
|
132
|
+
const dir = path.join(home, ".local", "share", "muse", "runtime", "muse", "sessions");
|
|
133
|
+
fs.mkdirSync(dir, {
|
|
134
|
+
recursive: true
|
|
135
|
+
});
|
|
136
|
+
fs.writeFileSync(path.join(dir, `${SID}.json`), "not json");
|
|
137
|
+
fs.writeFileSync(path.join(dir, "other.json"), JSON.stringify({
|
|
138
|
+
session_id: "other",
|
|
139
|
+
process_generation_hint: "nonsense"
|
|
140
|
+
}));
|
|
141
|
+
// Non-muse processes never reach the locator: the adapter pre-filters via canHandle.
|
|
142
|
+
const { direct, fallback } = locator.matchRunningProcesses([
|
|
143
|
+
proc(16174, "/Users/tester/proj", new Date())
|
|
144
|
+
]);
|
|
145
|
+
expect(direct).toHaveLength(0);
|
|
146
|
+
expect(fallback.map((p)=>p.pid)).toEqual([
|
|
147
|
+
16174
|
|
148
|
+
]);
|
|
149
|
+
});
|
|
150
|
+
it("discovers historical sessions across dated dirs, skipping noise", ()=>{
|
|
151
|
+
writeTranscript(SID, "2026/10/10", [
|
|
152
|
+
frame(intent("hello", 1791640525332173))
|
|
153
|
+
]);
|
|
154
|
+
writeTranscript(OTHER_SID, "2026/10/09", [
|
|
155
|
+
frame(intent("world", 1791640525332173))
|
|
156
|
+
]);
|
|
157
|
+
// Noise: subagent-style nested dir without session.jsonl, stray files.
|
|
158
|
+
const noise = path.join(home, ".local", "share", "muse", "sessions", "2026/10/10", "not-a-uuid");
|
|
159
|
+
fs.mkdirSync(noise, {
|
|
160
|
+
recursive: true
|
|
161
|
+
});
|
|
162
|
+
fs.writeFileSync(path.join(noise, "notes.txt"), "hi");
|
|
163
|
+
const found = locator.discoverHistoricalSessionFiles();
|
|
164
|
+
expect(found.map((f)=>f.filePath).sort()).toEqual([
|
|
165
|
+
path.join(home, ".local", "share", "muse", "sessions", "2026/10/09", OTHER_SID, "session.jsonl"),
|
|
166
|
+
path.join(home, ".local", "share", "muse", "sessions", "2026/10/10", SID, "session.jsonl")
|
|
167
|
+
].sort());
|
|
168
|
+
});
|
|
169
|
+
it("finds a session file by id without enumerating everything", ()=>{
|
|
170
|
+
const filePath = writeTranscript(SID, "2026/10/10", [
|
|
171
|
+
frame(intent("hello", 1791640525332173))
|
|
172
|
+
]);
|
|
173
|
+
const found = locator.findHistoricalSessionFilesById(SID);
|
|
174
|
+
expect(found).toEqual([
|
|
175
|
+
{
|
|
176
|
+
filePath,
|
|
177
|
+
defaultCwd: ""
|
|
178
|
+
}
|
|
179
|
+
]);
|
|
180
|
+
expect(locator.findHistoricalSessionFilesById("nope")).toEqual([]);
|
|
181
|
+
expect(locator.findHistoricalSessionFilesById("../evil")).toEqual([]);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
//# sourceMappingURL=MuseSessionLocator.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/__tests__/harnesses/muse/MuseSessionLocator.test.ts"],"sourcesContent":["/**\n * Tests for harnesses/muse/MuseSessionLocator.ts — runtime-file matching,\n * dated-archive discovery, and session lookup by id.\n */\n\nimport * as fs from \"fs\";\nimport * as os from \"os\";\nimport * as path from \"path\";\nimport type { ProcessInfo } from \"../../../adapters/AgentAdapter.js\";\nimport { MuseSessionLocator } from \"../../../harnesses/muse/MuseSessionLocator.js\";\n\nconst SID = \"01a12618-d918-7b63-91c8-4e896326eb1a\";\nconst OTHER_SID = \"2288dbd2-d703-444d-afce-59e40a8ad8b1\";\n\nfunction frame(inner: unknown): string {\n return JSON.stringify(inner);\n}\n\nfunction intent(text: string, recordedAtUs: number): unknown {\n return {\n schema_version: 1,\n stream: { kind: \"session\", id: SID },\n sequence: 2,\n recorded_at: recordedAtUs,\n record_type: \"event\",\n payload_type: \"runtime.user_intent.accepted\",\n payload: { refill_blocks: [{ kind: \"text\", text }] },\n };\n}\n\nfunction metadata(workspaceRoot: string, recordedAtUs: number): unknown {\n return {\n schema_version: 1,\n stream: { kind: \"session\", id: SID },\n sequence: 1,\n recorded_at: recordedAtUs,\n record_type: \"event\",\n payload_type: \"runtime.session.metadata\",\n payload: { kind: \"metadata\", record: { workspace_root: workspaceRoot } },\n };\n}\n\nfunction proc(pid: number, cwd: string, startTime: Date): ProcessInfo {\n return {\n pid,\n command: `/Users/tester/.local/bin/muse-bin-1.4.4-R5419.1`,\n cwd,\n tty: \"ttys001\",\n startTime,\n };\n}\n\ndescribe(\"MuseSessionLocator\", () => {\n let home: string;\n let locator: MuseSessionLocator;\n\n beforeEach(() => {\n home = fs.mkdtempSync(path.join(os.tmpdir(), \"muse-locator-test-\"));\n locator = new MuseSessionLocator({ homeDir: home });\n });\n\n afterEach(() => {\n fs.rmSync(home, { recursive: true, force: true });\n });\n\n function writeTranscript(sessionId: string, dayDir: string, lines: string[]): string {\n const dir = path.join(home, \".local\", \"share\", \"muse\", \"sessions\", dayDir, sessionId);\n fs.mkdirSync(dir, { recursive: true });\n const filePath = path.join(dir, \"session.jsonl\");\n fs.writeFileSync(filePath, lines.join(\"\\n\"));\n return filePath;\n }\n\n function writeRuntimeFile(sessionId: string, pid: number): string {\n const dir = path.join(home, \".local\", \"share\", \"muse\", \"runtime\", \"muse\", \"sessions\");\n fs.mkdirSync(dir, { recursive: true });\n const filePath = path.join(dir, `${sessionId}.jsonl`.replace(\".jsonl\", \".json\"));\n fs.writeFileSync(\n filePath,\n JSON.stringify({\n schema_version: 1,\n session_id: sessionId,\n process_generation_hint: `pid=${pid}`,\n }),\n );\n return filePath;\n }\n\n it(\"matches a live process to its transcript via the runtime file\", () => {\n const now = Date.now();\n const filePath = writeTranscript(SID, \"2026/10/10\", [\n frame(metadata(\"/Users/tester/proj\", now * 1000)),\n frame(intent(\"hello\", now * 1000)),\n ]);\n writeRuntimeFile(SID, 16174);\n // Align file birthtime with process start for the staleness guard.\n const start = new Date(fs.statSync(filePath).birthtimeMs);\n\n const { direct } = locator.matchRunningProcesses([proc(16174, \"/Users/tester/proj\", start)]);\n expect(direct).toHaveLength(1);\n expect(direct[0].process.pid).toBe(16174);\n expect(direct[0].sessionFile.filePath).toBe(filePath);\n expect(direct[0].sessionFile.sessionId).toBe(SID);\n });\n\n it(\"rejects runtime matches when the pid was recycled\", () => {\n writeTranscript(SID, \"2026/10/10\", [frame(intent(\"hello\", 1791640525332173))]);\n writeRuntimeFile(SID, 16174);\n\n // Process started an hour after the session file was born: recycled pid.\n const { direct, fallback } = locator.matchRunningProcesses([\n proc(16174, \"/Users/tester/proj\", new Date(Date.now() + 3600_000)),\n ]);\n expect(direct).toHaveLength(0);\n expect(fallback.map((p) => p.pid)).toContain(16174);\n });\n\n it(\"ignores malformed runtime files (processes fall through to legacy)\", () => {\n writeTranscript(SID, \"2026/10/10\", [frame(intent(\"hello\", 1791640525332173))]);\n const dir = path.join(home, \".local\", \"share\", \"muse\", \"runtime\", \"muse\", \"sessions\");\n fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(path.join(dir, `${SID}.json`), \"not json\");\n fs.writeFileSync(\n path.join(dir, \"other.json\"),\n JSON.stringify({ session_id: \"other\", process_generation_hint: \"nonsense\" }),\n );\n\n // Non-muse processes never reach the locator: the adapter pre-filters via canHandle.\n const { direct, fallback } = locator.matchRunningProcesses([\n proc(16174, \"/Users/tester/proj\", new Date()),\n ]);\n expect(direct).toHaveLength(0);\n expect(fallback.map((p) => p.pid)).toEqual([16174]);\n });\n\n it(\"discovers historical sessions across dated dirs, skipping noise\", () => {\n writeTranscript(SID, \"2026/10/10\", [frame(intent(\"hello\", 1791640525332173))]);\n writeTranscript(OTHER_SID, \"2026/10/09\", [frame(intent(\"world\", 1791640525332173))]);\n // Noise: subagent-style nested dir without session.jsonl, stray files.\n const noise = path.join(\n home,\n \".local\",\n \"share\",\n \"muse\",\n \"sessions\",\n \"2026/10/10\",\n \"not-a-uuid\",\n );\n fs.mkdirSync(noise, { recursive: true });\n fs.writeFileSync(path.join(noise, \"notes.txt\"), \"hi\");\n\n const found = locator.discoverHistoricalSessionFiles();\n expect(found.map((f) => f.filePath).sort()).toEqual(\n [\n path.join(\n home,\n \".local\",\n \"share\",\n \"muse\",\n \"sessions\",\n \"2026/10/09\",\n OTHER_SID,\n \"session.jsonl\",\n ),\n path.join(home, \".local\", \"share\", \"muse\", \"sessions\", \"2026/10/10\", SID, \"session.jsonl\"),\n ].sort(),\n );\n });\n\n it(\"finds a session file by id without enumerating everything\", () => {\n const filePath = writeTranscript(SID, \"2026/10/10\", [frame(intent(\"hello\", 1791640525332173))]);\n const found = locator.findHistoricalSessionFilesById(SID);\n expect(found).toEqual([{ filePath, defaultCwd: \"\" }]);\n expect(locator.findHistoricalSessionFilesById(\"nope\")).toEqual([]);\n expect(locator.findHistoricalSessionFilesById(\"../evil\")).toEqual([]);\n });\n});\n"],"names":["fs","os","path","MuseSessionLocator","SID","OTHER_SID","frame","inner","JSON","stringify","intent","text","recordedAtUs","schema_version","stream","kind","id","sequence","recorded_at","record_type","payload_type","payload","refill_blocks","metadata","workspaceRoot","record","workspace_root","proc","pid","cwd","startTime","command","tty","describe","home","locator","beforeEach","mkdtempSync","join","tmpdir","homeDir","afterEach","rmSync","recursive","force","writeTranscript","sessionId","dayDir","lines","dir","mkdirSync","filePath","writeFileSync","writeRuntimeFile","replace","session_id","process_generation_hint","it","now","Date","start","statSync","birthtimeMs","direct","matchRunningProcesses","expect","toHaveLength","process","toBe","sessionFile","fallback","map","p","toContain","toEqual","noise","found","discoverHistoricalSessionFiles","f","sort","findHistoricalSessionFilesById","defaultCwd"],"mappings":"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"}
|