@ai-devkit/agent-manager 0.40.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 +12 -0
- package/dist/AgentManager.d.ts.map +1 -1
- package/dist/AgentManager.js +45 -10
- package/dist/AgentManager.js.map +1 -1
- package/dist/__tests__/AgentManager.test.js +111 -0
- package/dist/__tests__/AgentManager.test.js.map +1 -1
- package/dist/__tests__/fixtures.test.js +218 -0
- package/dist/__tests__/fixtures.test.js.map +1 -0
- 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__/readiness-fixtures.test.js +54 -0
- package/dist/__tests__/readiness-fixtures.test.js.map +1 -0
- 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/bundle.d.ts +67 -0
- package/dist/fixtures/bundle.d.ts.map +1 -0
- package/dist/fixtures/bundle.js +86 -0
- package/dist/fixtures/bundle.js.map +1 -0
- package/dist/fixtures/capture.d.ts +11 -0
- package/dist/fixtures/capture.d.ts.map +1 -0
- package/dist/fixtures/capture.js +318 -0
- package/dist/fixtures/capture.js.map +1 -0
- 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 +7 -6
- 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 +71 -9
- package/src/__tests__/AgentManager.test.ts +84 -0
- package/src/__tests__/fixtures.test.ts +245 -0
- 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__/readiness-fixtures.test.ts +79 -0
- package/src/__tests__/utils/process.test.ts +30 -0
- package/src/adapters/AgentAdapter.ts +9 -0
- package/src/fixtures/bundle.ts +130 -0
- package/src/fixtures/capture.ts +364 -0
- 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 +11 -6
- package/src/utils/process.ts +61 -9
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for harnesses/muse/MuseSessionParser.ts — framed transcript parsing.
|
|
3
|
+
*
|
|
4
|
+
* Muse session.jsonl lines are frames: either a direct inner record, a
|
|
5
|
+
* `record_json` string, or a `retained_frame` wrapper whose children carry
|
|
6
|
+
* `record_json` strings. Inner records dispatch on `payload_type`.
|
|
7
|
+
*/ import * as fs from "fs";
|
|
8
|
+
import * as os from "os";
|
|
9
|
+
import * as path from "path";
|
|
10
|
+
import { AgentStatus } from "../../../adapters/AgentAdapter.js";
|
|
11
|
+
import { MuseSessionParser } from "../../../harnesses/muse/MuseSessionParser.js";
|
|
12
|
+
const SID = "01a12618-d918-7b63-91c8-4e896326eb1a";
|
|
13
|
+
function frame(inner) {
|
|
14
|
+
return JSON.stringify(inner);
|
|
15
|
+
}
|
|
16
|
+
function wrapped(inner) {
|
|
17
|
+
return JSON.stringify({
|
|
18
|
+
record_json: JSON.stringify(inner)
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function retained(inner) {
|
|
22
|
+
return JSON.stringify({
|
|
23
|
+
retained_frame: "session_permission_transaction",
|
|
24
|
+
frame_schema_version: 1,
|
|
25
|
+
children: [
|
|
26
|
+
{
|
|
27
|
+
child_index: 0,
|
|
28
|
+
record_json: JSON.stringify(inner)
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function intent(text, sequence, recordedAtUs) {
|
|
34
|
+
return {
|
|
35
|
+
schema_version: 1,
|
|
36
|
+
stream: {
|
|
37
|
+
kind: "session",
|
|
38
|
+
id: SID
|
|
39
|
+
},
|
|
40
|
+
sequence,
|
|
41
|
+
recorded_at: recordedAtUs,
|
|
42
|
+
record_type: "event",
|
|
43
|
+
payload_type: "runtime.user_intent.accepted",
|
|
44
|
+
payload: {
|
|
45
|
+
intent_id: `intent-${sequence}`,
|
|
46
|
+
refill_blocks: [
|
|
47
|
+
{
|
|
48
|
+
kind: "text",
|
|
49
|
+
text
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function assistantMessage(text, sequence, recordedAtUs) {
|
|
56
|
+
return {
|
|
57
|
+
schema_version: 1,
|
|
58
|
+
stream: {
|
|
59
|
+
kind: "session",
|
|
60
|
+
id: SID
|
|
61
|
+
},
|
|
62
|
+
sequence,
|
|
63
|
+
recorded_at: recordedAtUs,
|
|
64
|
+
record_type: "event",
|
|
65
|
+
payload_type: "runtime.session",
|
|
66
|
+
payload: {
|
|
67
|
+
kind: "run",
|
|
68
|
+
run_id: "run-1",
|
|
69
|
+
event: {
|
|
70
|
+
kind: "assistant_message_committed",
|
|
71
|
+
text
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function metadata(recordedAtUs) {
|
|
77
|
+
return {
|
|
78
|
+
schema_version: 1,
|
|
79
|
+
stream: {
|
|
80
|
+
kind: "session",
|
|
81
|
+
id: SID
|
|
82
|
+
},
|
|
83
|
+
sequence: 1,
|
|
84
|
+
recorded_at: recordedAtUs,
|
|
85
|
+
record_type: "event",
|
|
86
|
+
payload_type: "runtime.session.metadata",
|
|
87
|
+
payload: {
|
|
88
|
+
kind: "metadata",
|
|
89
|
+
record: {
|
|
90
|
+
workspace_root: "/Users/tester/proj",
|
|
91
|
+
model_id: "muse-spark-1.3"
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function nameChanged(name, sequence, recordedAtUs) {
|
|
97
|
+
return {
|
|
98
|
+
schema_version: 1,
|
|
99
|
+
stream: {
|
|
100
|
+
kind: "session",
|
|
101
|
+
id: SID
|
|
102
|
+
},
|
|
103
|
+
sequence,
|
|
104
|
+
recorded_at: recordedAtUs,
|
|
105
|
+
record_type: "event",
|
|
106
|
+
payload_type: "session.name.changed",
|
|
107
|
+
payload: {
|
|
108
|
+
session_id: SID,
|
|
109
|
+
previous_name: null,
|
|
110
|
+
new_name: name
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
describe("MuseSessionParser", ()=>{
|
|
115
|
+
let parser;
|
|
116
|
+
const tempDirs = [];
|
|
117
|
+
beforeEach(()=>{
|
|
118
|
+
parser = new MuseSessionParser();
|
|
119
|
+
});
|
|
120
|
+
afterEach(()=>{
|
|
121
|
+
for (const dir of tempDirs){
|
|
122
|
+
try {
|
|
123
|
+
fs.rmSync(dir, {
|
|
124
|
+
recursive: true,
|
|
125
|
+
force: true
|
|
126
|
+
});
|
|
127
|
+
} catch {
|
|
128
|
+
/* best effort */ }
|
|
129
|
+
}
|
|
130
|
+
tempDirs.length = 0;
|
|
131
|
+
});
|
|
132
|
+
function writeTranscript(lines) {
|
|
133
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "muse-parser-test-"));
|
|
134
|
+
tempDirs.push(dir);
|
|
135
|
+
const filePath = path.join(dir, "session.jsonl");
|
|
136
|
+
fs.writeFileSync(filePath, lines.join("\n"));
|
|
137
|
+
return filePath;
|
|
138
|
+
}
|
|
139
|
+
it("returns null for missing or empty files", ()=>{
|
|
140
|
+
expect(parser.readSession("/nonexistent/session.jsonl", "/proj")).toBeNull();
|
|
141
|
+
expect(parser.readSession(writeTranscript([]), "/proj")).toBeNull();
|
|
142
|
+
});
|
|
143
|
+
it("skips unparseable lines and wrapper frames without records", ()=>{
|
|
144
|
+
const filePath = writeTranscript([
|
|
145
|
+
"not json at all",
|
|
146
|
+
JSON.stringify({
|
|
147
|
+
retained_frame: "x",
|
|
148
|
+
children: []
|
|
149
|
+
}),
|
|
150
|
+
frame(intent("hello", 2, 1791640525332173))
|
|
151
|
+
]);
|
|
152
|
+
const session = parser.readSession(filePath, "/proj");
|
|
153
|
+
expect(session?.lastUserMessage).toBe("hello");
|
|
154
|
+
});
|
|
155
|
+
it("unwraps record_json strings and retained children", ()=>{
|
|
156
|
+
const filePath = writeTranscript([
|
|
157
|
+
wrapped(intent("first", 2, 1791640525332173)),
|
|
158
|
+
retained(intent("second", 3, 1791640525332174))
|
|
159
|
+
]);
|
|
160
|
+
const session = parser.readSession(filePath, "/proj");
|
|
161
|
+
expect(session?.firstUserMessage).toBe("first");
|
|
162
|
+
expect(session?.lastUserMessage).toBe("second");
|
|
163
|
+
});
|
|
164
|
+
it("reads workspace root, model, and display name", ()=>{
|
|
165
|
+
const filePath = writeTranscript([
|
|
166
|
+
frame(metadata(1791640525332173)),
|
|
167
|
+
frame(intent("hi", 2, 1791640525332174)),
|
|
168
|
+
frame(nameChanged("flowing-monoceros", 3, 1791640525332175))
|
|
169
|
+
]);
|
|
170
|
+
// Empty project path lets the recorded workspace root win (archive sessions).
|
|
171
|
+
const session = parser.readSession(filePath, "");
|
|
172
|
+
expect(session?.projectPath).toBe("/Users/tester/proj");
|
|
173
|
+
expect(session?.modelId).toBe("muse-spark-1.3");
|
|
174
|
+
expect(session?.displayName).toBe("flowing-monoceros");
|
|
175
|
+
// First user message is found even when it is not on the first line.
|
|
176
|
+
expect(session?.firstUserMessage).toBe("hi");
|
|
177
|
+
});
|
|
178
|
+
it("normalizes microsecond timestamps to millisecond dates", ()=>{
|
|
179
|
+
const filePath = writeTranscript([
|
|
180
|
+
frame(intent("hi", 2, 1791640525332173))
|
|
181
|
+
]);
|
|
182
|
+
const session = parser.readSession(filePath, "/proj");
|
|
183
|
+
expect(session?.lastActive).toEqual(new Date(1791640525332));
|
|
184
|
+
});
|
|
185
|
+
it("maps last user intent to RUNNING and committed reply to WAITING", ()=>{
|
|
186
|
+
const running = parser.readSession(writeTranscript([
|
|
187
|
+
frame(intent("q", 2, 1791640525332173)),
|
|
188
|
+
frame(assistantMessage("a", 3, 1791640525332174)),
|
|
189
|
+
frame(intent("q2", 4, 1791640525332175))
|
|
190
|
+
]), "/proj");
|
|
191
|
+
expect(parser.determineStatus(running)).toBe(AgentStatus.RUNNING);
|
|
192
|
+
const waiting = parser.readSession(writeTranscript([
|
|
193
|
+
frame(intent("q", 2, 1791640525332173)),
|
|
194
|
+
frame(assistantMessage("a", 3, 1791640525332174))
|
|
195
|
+
]), "/proj");
|
|
196
|
+
expect(parser.determineStatus(waiting)).toBe(AgentStatus.WAITING);
|
|
197
|
+
});
|
|
198
|
+
it("maps approval waits and terminal runs to WAITING and IDLE", ()=>{
|
|
199
|
+
const waiting = parser.readSession(writeTranscript([
|
|
200
|
+
frame({
|
|
201
|
+
schema_version: 1,
|
|
202
|
+
stream: {
|
|
203
|
+
kind: "session",
|
|
204
|
+
id: SID
|
|
205
|
+
},
|
|
206
|
+
sequence: 2,
|
|
207
|
+
recorded_at: 1791640525332173,
|
|
208
|
+
record_type: "event",
|
|
209
|
+
payload_type: "runtime.session",
|
|
210
|
+
payload: {
|
|
211
|
+
kind: "run",
|
|
212
|
+
run_id: "run-1",
|
|
213
|
+
event: {
|
|
214
|
+
kind: "approval_wait.effect.started",
|
|
215
|
+
tool_name: "edit"
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
})
|
|
219
|
+
]), "/proj");
|
|
220
|
+
expect(parser.determineStatus(waiting)).toBe(AgentStatus.WAITING);
|
|
221
|
+
const idle = parser.readSession(writeTranscript([
|
|
222
|
+
frame({
|
|
223
|
+
schema_version: 1,
|
|
224
|
+
stream: {
|
|
225
|
+
kind: "session",
|
|
226
|
+
id: SID
|
|
227
|
+
},
|
|
228
|
+
sequence: 2,
|
|
229
|
+
recorded_at: 1791640525332173,
|
|
230
|
+
record_type: "event",
|
|
231
|
+
payload_type: "runtime.session",
|
|
232
|
+
payload: {
|
|
233
|
+
kind: "run",
|
|
234
|
+
run_id: "run-1",
|
|
235
|
+
event: {
|
|
236
|
+
kind: "terminal"
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
})
|
|
240
|
+
]), "/proj");
|
|
241
|
+
expect(parser.determineStatus(idle)).toBe(AgentStatus.IDLE);
|
|
242
|
+
});
|
|
243
|
+
it("returns UNKNOWN when no conversation signal exists", ()=>{
|
|
244
|
+
const session = parser.readSession(writeTranscript([
|
|
245
|
+
frame(metadata(1791640525332173))
|
|
246
|
+
]), "/proj");
|
|
247
|
+
expect(parser.determineStatus(session)).toBe(AgentStatus.UNKNOWN);
|
|
248
|
+
});
|
|
249
|
+
it("getConversation returns ordered messages and honors tail", ()=>{
|
|
250
|
+
const filePath = writeTranscript([
|
|
251
|
+
frame(metadata(1791640525332173)),
|
|
252
|
+
frame(intent("first question", 2, 1791640525332174)),
|
|
253
|
+
frame(assistantMessage("first answer", 3, 1791640525332175)),
|
|
254
|
+
frame(intent("second question", 4, 1791640525332176))
|
|
255
|
+
]);
|
|
256
|
+
const full = parser.getConversation(filePath);
|
|
257
|
+
expect(full.map((m)=>[
|
|
258
|
+
m.role,
|
|
259
|
+
m.content
|
|
260
|
+
])).toEqual([
|
|
261
|
+
[
|
|
262
|
+
"user",
|
|
263
|
+
"first question"
|
|
264
|
+
],
|
|
265
|
+
[
|
|
266
|
+
"assistant",
|
|
267
|
+
"first answer"
|
|
268
|
+
],
|
|
269
|
+
[
|
|
270
|
+
"user",
|
|
271
|
+
"second question"
|
|
272
|
+
]
|
|
273
|
+
]);
|
|
274
|
+
const tail = parser.getConversation(filePath, {
|
|
275
|
+
tail: 1
|
|
276
|
+
});
|
|
277
|
+
expect(tail).toEqual([
|
|
278
|
+
{
|
|
279
|
+
role: "user",
|
|
280
|
+
content: "second question",
|
|
281
|
+
timestamp: expect.anything()
|
|
282
|
+
}
|
|
283
|
+
]);
|
|
284
|
+
});
|
|
285
|
+
it("returns [] for a missing transcript and tool calls in verbose mode", ()=>{
|
|
286
|
+
expect(parser.getConversation("/nonexistent/session.jsonl")).toEqual([]);
|
|
287
|
+
const filePath = writeTranscript([
|
|
288
|
+
frame({
|
|
289
|
+
schema_version: 1,
|
|
290
|
+
stream: {
|
|
291
|
+
kind: "session",
|
|
292
|
+
id: SID
|
|
293
|
+
},
|
|
294
|
+
sequence: 2,
|
|
295
|
+
recorded_at: 1791640525332173,
|
|
296
|
+
record_type: "event",
|
|
297
|
+
payload_type: "runtime.session",
|
|
298
|
+
payload: {
|
|
299
|
+
kind: "run",
|
|
300
|
+
run_id: "run-1",
|
|
301
|
+
event: {
|
|
302
|
+
kind: "assistant_tool_calls_committed",
|
|
303
|
+
tool_calls: [
|
|
304
|
+
{
|
|
305
|
+
tool_name: "read_file"
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
tool_name: "search"
|
|
309
|
+
}
|
|
310
|
+
]
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
})
|
|
314
|
+
]);
|
|
315
|
+
expect(parser.getConversation(filePath)).toEqual([]);
|
|
316
|
+
expect(parser.getConversation(filePath, {
|
|
317
|
+
verbose: true
|
|
318
|
+
})).toEqual([
|
|
319
|
+
{
|
|
320
|
+
role: "assistant",
|
|
321
|
+
content: "tool calls: read_file, search",
|
|
322
|
+
timestamp: expect.anything()
|
|
323
|
+
}
|
|
324
|
+
]);
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
//# sourceMappingURL=MuseSessionParser.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/__tests__/harnesses/muse/MuseSessionParser.test.ts"],"sourcesContent":["/**\n * Tests for harnesses/muse/MuseSessionParser.ts — framed transcript parsing.\n *\n * Muse session.jsonl lines are frames: either a direct inner record, a\n * `record_json` string, or a `retained_frame` wrapper whose children carry\n * `record_json` strings. Inner records dispatch on `payload_type`.\n */\n\nimport * as fs from \"fs\";\nimport * as os from \"os\";\nimport * as path from \"path\";\nimport { AgentStatus } from \"../../../adapters/AgentAdapter.js\";\nimport { MuseSessionParser } from \"../../../harnesses/muse/MuseSessionParser.js\";\n\nconst SID = \"01a12618-d918-7b63-91c8-4e896326eb1a\";\n\nfunction frame(inner: unknown): string {\n return JSON.stringify(inner);\n}\n\nfunction wrapped(inner: unknown): string {\n return JSON.stringify({ record_json: JSON.stringify(inner) });\n}\n\nfunction retained(inner: unknown): string {\n return JSON.stringify({\n retained_frame: \"session_permission_transaction\",\n frame_schema_version: 1,\n children: [{ child_index: 0, record_json: JSON.stringify(inner) }],\n });\n}\n\nfunction intent(text: string, sequence: number, recordedAtUs: number): unknown {\n return {\n schema_version: 1,\n stream: { kind: \"session\", id: SID },\n sequence,\n recorded_at: recordedAtUs,\n record_type: \"event\",\n payload_type: \"runtime.user_intent.accepted\",\n payload: {\n intent_id: `intent-${sequence}`,\n refill_blocks: [{ kind: \"text\", text }],\n },\n };\n}\n\nfunction assistantMessage(text: string, sequence: number, recordedAtUs: number): unknown {\n return {\n schema_version: 1,\n stream: { kind: \"session\", id: SID },\n sequence,\n recorded_at: recordedAtUs,\n record_type: \"event\",\n payload_type: \"runtime.session\",\n payload: {\n kind: \"run\",\n run_id: \"run-1\",\n event: { kind: \"assistant_message_committed\", text },\n },\n };\n}\n\nfunction metadata(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: {\n kind: \"metadata\",\n record: { workspace_root: \"/Users/tester/proj\", model_id: \"muse-spark-1.3\" },\n },\n };\n}\n\nfunction nameChanged(name: string, sequence: number, recordedAtUs: number): unknown {\n return {\n schema_version: 1,\n stream: { kind: \"session\", id: SID },\n sequence,\n recorded_at: recordedAtUs,\n record_type: \"event\",\n payload_type: \"session.name.changed\",\n payload: { session_id: SID, previous_name: null, new_name: name },\n };\n}\n\ndescribe(\"MuseSessionParser\", () => {\n let parser: MuseSessionParser;\n const tempDirs: string[] = [];\n\n beforeEach(() => {\n parser = new MuseSessionParser();\n });\n\n afterEach(() => {\n for (const dir of tempDirs) {\n try {\n fs.rmSync(dir, { recursive: true, force: true });\n } catch {\n /* best effort */\n }\n }\n tempDirs.length = 0;\n });\n\n function writeTranscript(lines: string[]): string {\n const dir = fs.mkdtempSync(path.join(os.tmpdir(), \"muse-parser-test-\"));\n tempDirs.push(dir);\n const filePath = path.join(dir, \"session.jsonl\");\n fs.writeFileSync(filePath, lines.join(\"\\n\"));\n return filePath;\n }\n\n it(\"returns null for missing or empty files\", () => {\n expect(parser.readSession(\"/nonexistent/session.jsonl\", \"/proj\")).toBeNull();\n expect(parser.readSession(writeTranscript([]), \"/proj\")).toBeNull();\n });\n\n it(\"skips unparseable lines and wrapper frames without records\", () => {\n const filePath = writeTranscript([\n \"not json at all\",\n JSON.stringify({ retained_frame: \"x\", children: [] }),\n frame(intent(\"hello\", 2, 1791640525332173)),\n ]);\n const session = parser.readSession(filePath, \"/proj\");\n expect(session?.lastUserMessage).toBe(\"hello\");\n });\n\n it(\"unwraps record_json strings and retained children\", () => {\n const filePath = writeTranscript([\n wrapped(intent(\"first\", 2, 1791640525332173)),\n retained(intent(\"second\", 3, 1791640525332174)),\n ]);\n const session = parser.readSession(filePath, \"/proj\");\n expect(session?.firstUserMessage).toBe(\"first\");\n expect(session?.lastUserMessage).toBe(\"second\");\n });\n\n it(\"reads workspace root, model, and display name\", () => {\n const filePath = writeTranscript([\n frame(metadata(1791640525332173)),\n frame(intent(\"hi\", 2, 1791640525332174)),\n frame(nameChanged(\"flowing-monoceros\", 3, 1791640525332175)),\n ]);\n // Empty project path lets the recorded workspace root win (archive sessions).\n const session = parser.readSession(filePath, \"\");\n expect(session?.projectPath).toBe(\"/Users/tester/proj\");\n expect(session?.modelId).toBe(\"muse-spark-1.3\");\n expect(session?.displayName).toBe(\"flowing-monoceros\");\n // First user message is found even when it is not on the first line.\n expect(session?.firstUserMessage).toBe(\"hi\");\n });\n\n it(\"normalizes microsecond timestamps to millisecond dates\", () => {\n const filePath = writeTranscript([frame(intent(\"hi\", 2, 1791640525332173))]);\n const session = parser.readSession(filePath, \"/proj\");\n expect(session?.lastActive).toEqual(new Date(1791640525332));\n });\n\n it(\"maps last user intent to RUNNING and committed reply to WAITING\", () => {\n const running = parser.readSession(\n writeTranscript([\n frame(intent(\"q\", 2, 1791640525332173)),\n frame(assistantMessage(\"a\", 3, 1791640525332174)),\n frame(intent(\"q2\", 4, 1791640525332175)),\n ]),\n \"/proj\",\n );\n expect(parser.determineStatus(running!)).toBe(AgentStatus.RUNNING);\n\n const waiting = parser.readSession(\n writeTranscript([\n frame(intent(\"q\", 2, 1791640525332173)),\n frame(assistantMessage(\"a\", 3, 1791640525332174)),\n ]),\n \"/proj\",\n );\n expect(parser.determineStatus(waiting!)).toBe(AgentStatus.WAITING);\n });\n\n it(\"maps approval waits and terminal runs to WAITING and IDLE\", () => {\n const waiting = parser.readSession(\n writeTranscript([\n frame({\n schema_version: 1,\n stream: { kind: \"session\", id: SID },\n sequence: 2,\n recorded_at: 1791640525332173,\n record_type: \"event\",\n payload_type: \"runtime.session\",\n payload: {\n kind: \"run\",\n run_id: \"run-1\",\n event: { kind: \"approval_wait.effect.started\", tool_name: \"edit\" },\n },\n }),\n ]),\n \"/proj\",\n );\n expect(parser.determineStatus(waiting!)).toBe(AgentStatus.WAITING);\n\n const idle = parser.readSession(\n writeTranscript([\n frame({\n schema_version: 1,\n stream: { kind: \"session\", id: SID },\n sequence: 2,\n recorded_at: 1791640525332173,\n record_type: \"event\",\n payload_type: \"runtime.session\",\n payload: { kind: \"run\", run_id: \"run-1\", event: { kind: \"terminal\" } },\n }),\n ]),\n \"/proj\",\n );\n expect(parser.determineStatus(idle!)).toBe(AgentStatus.IDLE);\n });\n\n it(\"returns UNKNOWN when no conversation signal exists\", () => {\n const session = parser.readSession(\n writeTranscript([frame(metadata(1791640525332173))]),\n \"/proj\",\n );\n expect(parser.determineStatus(session!)).toBe(AgentStatus.UNKNOWN);\n });\n\n it(\"getConversation returns ordered messages and honors tail\", () => {\n const filePath = writeTranscript([\n frame(metadata(1791640525332173)),\n frame(intent(\"first question\", 2, 1791640525332174)),\n frame(assistantMessage(\"first answer\", 3, 1791640525332175)),\n frame(intent(\"second question\", 4, 1791640525332176)),\n ]);\n const full = parser.getConversation(filePath);\n expect(full.map((m) => [m.role, m.content])).toEqual([\n [\"user\", \"first question\"],\n [\"assistant\", \"first answer\"],\n [\"user\", \"second question\"],\n ]);\n const tail = parser.getConversation(filePath, { tail: 1 });\n expect(tail).toEqual([\n { role: \"user\", content: \"second question\", timestamp: expect.anything() },\n ]);\n });\n\n it(\"returns [] for a missing transcript and tool calls in verbose mode\", () => {\n expect(parser.getConversation(\"/nonexistent/session.jsonl\")).toEqual([]);\n const filePath = writeTranscript([\n frame({\n schema_version: 1,\n stream: { kind: \"session\", id: SID },\n sequence: 2,\n recorded_at: 1791640525332173,\n record_type: \"event\",\n payload_type: \"runtime.session\",\n payload: {\n kind: \"run\",\n run_id: \"run-1\",\n event: {\n kind: \"assistant_tool_calls_committed\",\n tool_calls: [{ tool_name: \"read_file\" }, { tool_name: \"search\" }],\n },\n },\n }),\n ]);\n expect(parser.getConversation(filePath)).toEqual([]);\n expect(parser.getConversation(filePath, { verbose: true })).toEqual([\n {\n role: \"assistant\",\n content: \"tool calls: read_file, search\",\n timestamp: expect.anything(),\n },\n ]);\n });\n});\n"],"names":["fs","os","path","AgentStatus","MuseSessionParser","SID","frame","inner","JSON","stringify","wrapped","record_json","retained","retained_frame","frame_schema_version","children","child_index","intent","text","sequence","recordedAtUs","schema_version","stream","kind","id","recorded_at","record_type","payload_type","payload","intent_id","refill_blocks","assistantMessage","run_id","event","metadata","record","workspace_root","model_id","nameChanged","name","session_id","previous_name","new_name","describe","parser","tempDirs","beforeEach","afterEach","dir","rmSync","recursive","force","length","writeTranscript","lines","mkdtempSync","join","tmpdir","push","filePath","writeFileSync","it","expect","readSession","toBeNull","session","lastUserMessage","toBe","firstUserMessage","projectPath","modelId","displayName","lastActive","toEqual","Date","running","determineStatus","RUNNING","waiting","WAITING","tool_name","idle","IDLE","UNKNOWN","full","getConversation","map","m","role","content","tail","timestamp","anything","tool_calls","verbose"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/__tests__/harnesses/opencode/OpenCodeSessionParser.test.ts"],"sourcesContent":["import Database from \"better-sqlite3\";\nimport { afterEach, beforeEach, describe, expect, it } from \"vitest\";\nimport { OpenCodeSessionParser } from \"../../../harnesses/opencode/OpenCodeSessionParser.js\";\n\ndescribe(\"OpenCodeSessionParser\", () => {\n let db: Database.Database;\n let parser: OpenCodeSessionParser;\n\n beforeEach(() => {\n db = new Database(\":memory:\");\n db.exec(`\n CREATE TABLE message (id TEXT, session_id TEXT, data TEXT, time_created INTEGER, time_updated INTEGER);\n CREATE TABLE part (message_id TEXT, session_id TEXT, data TEXT, time_created INTEGER);\n `);\n parser = new OpenCodeSessionParser();\n });\n\n afterEach(() => {\n db.close();\n });\n\n it(\"extracts session stats from OpenCode messages and parts\", () => {\n insertMessage(db, {\n id: \"msg-user\",\n sessionId: \"sess-1\",\n role: \"user\",\n timeCreated: 1000,\n timeUpdated: 1000,\n });\n insertMessage(db, {\n id: \"msg-assistant\",\n sessionId: \"sess-1\",\n role: \"assistant\",\n timeCreated: 2000,\n timeUpdated: 2500,\n completed: 2400,\n });\n insertPart(db, {\n messageId: \"msg-user\",\n sessionId: \"sess-1\",\n data: { type: \"text\", text: \"Plan the migration\" },\n timeCreated: 1000,\n });\n\n expect(parser.getSessionStats(db, \"sess-1\")).toEqual({\n lastRole: \"assistant\",\n lastTimeUpdated: 2500,\n lastAssistantCompleted: true,\n lastAssistantErrored: false,\n summary: \"Plan the migration\",\n });\n });\n\n it(\"orders stats by message creation time, not metadata update time\", () => {\n insertMessage(db, {\n id: \"msg-user\",\n sessionId: \"sess-1\",\n role: \"user\",\n timeCreated: 1000,\n timeUpdated: 3000,\n });\n insertMessage(db, {\n id: \"msg-assistant\",\n sessionId: \"sess-1\",\n role: \"assistant\",\n timeCreated: 2000,\n timeUpdated: 2100,\n completed: 2200,\n });\n\n expect(parser.getSessionStats(db, \"sess-1\").lastRole).toBe(\"assistant\");\n });\n\n it(\"reads visible conversation text and hides verbose-only parts by default\", () => {\n insertMessage(db, {\n id: \"msg-user\",\n sessionId: \"sess-1\",\n role: \"user\",\n timeCreated: 1000,\n timeUpdated: 1000,\n });\n insertMessage(db, {\n id: \"msg-assistant\",\n sessionId: \"sess-1\",\n role: \"assistant\",\n timeCreated: 2000,\n timeUpdated: 2000,\n });\n insertPart(db, {\n messageId: \"msg-user\",\n sessionId: \"sess-1\",\n data: { type: \"text\", text: \"Hello\" },\n timeCreated: 1000,\n });\n insertPart(db, {\n messageId: \"msg-assistant\",\n sessionId: \"sess-1\",\n data: { type: \"reasoning\", reasoning: \"private thought\" },\n timeCreated: 2000,\n });\n insertPart(db, {\n messageId: \"msg-assistant\",\n sessionId: \"sess-1\",\n data: { type: \"tool\", tool: \"read_file\" },\n timeCreated: 3000,\n });\n\n expect(parser.getConversation(db, \"sess-1\")).toEqual([{ role: \"user\", content: \"Hello\" }]);\n expect(parser.getConversation(db, \"sess-1\", { verbose: true })).toEqual([\n { role: \"user\", content: \"Hello\" },\n { role: \"assistant\", content: \"[thinking] private thought\" },\n { role: \"assistant\", content: \"[tool: read_file]\" },\n ]);\n });\n});\n\nfunction insertMessage(\n db: Database.Database,\n input: {\n id: string;\n sessionId: string;\n role: string;\n timeCreated: number;\n timeUpdated: number;\n completed?: number;\n },\n): void {\n db.prepare(\n \"INSERT INTO message (id, session_id, data, time_created, time_updated) VALUES (?, ?, ?, ?, ?)\",\n ).run(\n input.id,\n input.sessionId,\n JSON.stringify({\n role: input.role,\n time: input.completed === undefined ? {} : { completed: input.completed },\n }),\n input.timeCreated,\n input.timeUpdated,\n );\n}\n\nfunction insertPart(\n db: Database.Database,\n input: { messageId: string; sessionId: string; data: unknown; timeCreated: number },\n): void {\n db.prepare(\"INSERT INTO part (message_id, session_id, data, time_created) VALUES (?, ?, ?, ?)\")
|
|
1
|
+
{"version":3,"sources":["../../../../src/__tests__/harnesses/opencode/OpenCodeSessionParser.test.ts"],"sourcesContent":["import Database from \"better-sqlite3\";\nimport { afterEach, beforeEach, describe, expect, it } from \"vitest\";\nimport { OpenCodeSessionParser } from \"../../../harnesses/opencode/OpenCodeSessionParser.js\";\n\ndescribe(\"OpenCodeSessionParser\", () => {\n let db: Database.Database;\n let parser: OpenCodeSessionParser;\n\n beforeEach(() => {\n db = new Database(\":memory:\");\n db.exec(`\n CREATE TABLE message (id TEXT, session_id TEXT, data TEXT, time_created INTEGER, time_updated INTEGER);\n CREATE TABLE part (message_id TEXT, session_id TEXT, data TEXT, time_created INTEGER);\n `);\n parser = new OpenCodeSessionParser();\n });\n\n afterEach(() => {\n db.close();\n });\n\n it(\"extracts session stats from OpenCode messages and parts\", () => {\n insertMessage(db, {\n id: \"msg-user\",\n sessionId: \"sess-1\",\n role: \"user\",\n timeCreated: 1000,\n timeUpdated: 1000,\n });\n insertMessage(db, {\n id: \"msg-assistant\",\n sessionId: \"sess-1\",\n role: \"assistant\",\n timeCreated: 2000,\n timeUpdated: 2500,\n completed: 2400,\n });\n insertPart(db, {\n messageId: \"msg-user\",\n sessionId: \"sess-1\",\n data: { type: \"text\", text: \"Plan the migration\" },\n timeCreated: 1000,\n });\n\n expect(parser.getSessionStats(db, \"sess-1\")).toEqual({\n lastRole: \"assistant\",\n lastTimeUpdated: 2500,\n lastAssistantCompleted: true,\n lastAssistantErrored: false,\n summary: \"Plan the migration\",\n });\n });\n\n it(\"orders stats by message creation time, not metadata update time\", () => {\n insertMessage(db, {\n id: \"msg-user\",\n sessionId: \"sess-1\",\n role: \"user\",\n timeCreated: 1000,\n timeUpdated: 3000,\n });\n insertMessage(db, {\n id: \"msg-assistant\",\n sessionId: \"sess-1\",\n role: \"assistant\",\n timeCreated: 2000,\n timeUpdated: 2100,\n completed: 2200,\n });\n\n expect(parser.getSessionStats(db, \"sess-1\").lastRole).toBe(\"assistant\");\n });\n\n it(\"reads visible conversation text and hides verbose-only parts by default\", () => {\n insertMessage(db, {\n id: \"msg-user\",\n sessionId: \"sess-1\",\n role: \"user\",\n timeCreated: 1000,\n timeUpdated: 1000,\n });\n insertMessage(db, {\n id: \"msg-assistant\",\n sessionId: \"sess-1\",\n role: \"assistant\",\n timeCreated: 2000,\n timeUpdated: 2000,\n });\n insertPart(db, {\n messageId: \"msg-user\",\n sessionId: \"sess-1\",\n data: { type: \"text\", text: \"Hello\" },\n timeCreated: 1000,\n });\n insertPart(db, {\n messageId: \"msg-assistant\",\n sessionId: \"sess-1\",\n data: { type: \"reasoning\", reasoning: \"private thought\" },\n timeCreated: 2000,\n });\n insertPart(db, {\n messageId: \"msg-assistant\",\n sessionId: \"sess-1\",\n data: { type: \"tool\", tool: \"read_file\" },\n timeCreated: 3000,\n });\n\n expect(parser.getConversation(db, \"sess-1\")).toEqual([{ role: \"user\", content: \"Hello\" }]);\n expect(parser.getConversation(db, \"sess-1\", { verbose: true })).toEqual([\n { role: \"user\", content: \"Hello\" },\n { role: \"assistant\", content: \"[thinking] private thought\" },\n { role: \"assistant\", content: \"[tool: read_file]\" },\n ]);\n });\n});\n\nfunction insertMessage(\n db: Database.Database,\n input: {\n id: string;\n sessionId: string;\n role: string;\n timeCreated: number;\n timeUpdated: number;\n completed?: number;\n },\n): void {\n db.prepare(\n \"INSERT INTO message (id, session_id, data, time_created, time_updated) VALUES (?, ?, ?, ?, ?)\",\n ).run(\n input.id,\n input.sessionId,\n JSON.stringify({\n role: input.role,\n time: input.completed === undefined ? {} : { completed: input.completed },\n }),\n input.timeCreated,\n input.timeUpdated,\n );\n}\n\nfunction insertPart(\n db: Database.Database,\n input: { messageId: string; sessionId: string; data: unknown; timeCreated: number },\n): void {\n db.prepare(\n \"INSERT INTO part (message_id, session_id, data, time_created) VALUES (?, ?, ?, ?)\",\n ).run(input.messageId, input.sessionId, JSON.stringify(input.data), input.timeCreated);\n}\n"],"names":["Database","afterEach","beforeEach","describe","expect","it","OpenCodeSessionParser","db","parser","exec","close","insertMessage","id","sessionId","role","timeCreated","timeUpdated","completed","insertPart","messageId","data","type","text","getSessionStats","toEqual","lastRole","lastTimeUpdated","lastAssistantCompleted","lastAssistantErrored","summary","toBe","reasoning","tool","getConversation","content","verbose","input","prepare","run","JSON","stringify","time","undefined"],"mappings":"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"}
|
|
@@ -83,6 +83,7 @@ function fixture(overrides = {}) {
|
|
|
83
83
|
".gemini/antigravity-cli",
|
|
84
84
|
".config/opencode",
|
|
85
85
|
".config/devin",
|
|
86
|
+
".config/muse",
|
|
86
87
|
".pi"
|
|
87
88
|
]){
|
|
88
89
|
files[path.join(homeDir, directory)] = "<dir>";
|
|
@@ -154,7 +155,8 @@ describe("agent readiness", ()=>{
|
|
|
154
155
|
"opencode",
|
|
155
156
|
"copilot",
|
|
156
157
|
"pi",
|
|
157
|
-
"devin"
|
|
158
|
+
"devin",
|
|
159
|
+
"muse"
|
|
158
160
|
]);
|
|
159
161
|
for (const [agent, report] of Object.entries(reports)){
|
|
160
162
|
expect(report.type).toBe(agent);
|
|
@@ -197,6 +199,7 @@ describe("agent readiness", ()=>{
|
|
|
197
199
|
expect(reports.kiro.integration).toBeUndefined();
|
|
198
200
|
expect(reports.antigravity_cli.integration).toBeUndefined();
|
|
199
201
|
expect(reports.opencode.integration).toBeUndefined();
|
|
202
|
+
expect(reports.muse.integration).toBeUndefined();
|
|
200
203
|
});
|
|
201
204
|
it("resolves agent executables from PATH directories containing spaces", async ()=>{
|
|
202
205
|
const spacedBin = "/Applications/Some App.app/Contents/Resources";
|
|
@@ -374,6 +377,44 @@ describe("agent readiness", ()=>{
|
|
|
374
377
|
status: "pass"
|
|
375
378
|
});
|
|
376
379
|
});
|
|
380
|
+
it("checks Muse auth from the muse auth.json file", async ()=>{
|
|
381
|
+
const authPath = path.join("/home/test", ".config", "muse", "auth.json");
|
|
382
|
+
const { options, files } = fixture();
|
|
383
|
+
files[authPath] = JSON.stringify({
|
|
384
|
+
meta: {
|
|
385
|
+
token: "secret"
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
const report = await getAgentReadinessReport("muse", options);
|
|
389
|
+
expect(report.auth).toMatchObject({
|
|
390
|
+
state: "authenticated",
|
|
391
|
+
status: "pass"
|
|
392
|
+
});
|
|
393
|
+
expect(report.globalConfig).toMatchObject({
|
|
394
|
+
status: "pass"
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
it("reports Muse as unauthenticated with an empty auth file", async ()=>{
|
|
398
|
+
const authPath = path.join("/home/test", ".config", "muse", "auth.json");
|
|
399
|
+
const { options, files } = fixture();
|
|
400
|
+
files[authPath] = JSON.stringify({});
|
|
401
|
+
const report = await getAgentReadinessReport("muse", options);
|
|
402
|
+
expect(report.auth).toMatchObject({
|
|
403
|
+
state: "unauthenticated",
|
|
404
|
+
status: "fail",
|
|
405
|
+
errors: [
|
|
406
|
+
"Muse credential file has no entries"
|
|
407
|
+
]
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
it("reports Muse as unauthenticated without an auth file", async ()=>{
|
|
411
|
+
const { options } = fixture();
|
|
412
|
+
const report = await getAgentReadinessReport("muse", options);
|
|
413
|
+
expect(report.auth).toMatchObject({
|
|
414
|
+
state: "unauthenticated",
|
|
415
|
+
status: "fail"
|
|
416
|
+
});
|
|
417
|
+
});
|
|
377
418
|
it("reports Devin as unauthenticated without a login line", async ()=>{
|
|
378
419
|
const { options } = fixture({
|
|
379
420
|
runCommand: vi.fn(async (command)=>{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/__tests__/harnesses/readiness/AgentReadiness.test.ts"],"sourcesContent":["import path from \"node:path\";\nimport { describe, expect, it, vi } from \"vitest\";\nimport {\n getAgentReadinessReport,\n getAgentReadinessReports,\n type AgentReadinessOptions,\n type ReadinessAgentType,\n} from \"../../../harnesses/readiness/AgentReadiness.js\";\nimport { HARNESS_RUNTIME_PROFILES } from \"../../../harnesses/runtimeProfiles.js\";\n\ntype Files = Record<string, string>;\n\nconst builtInSkillNames = [\"agent-management\", \"memory\", \"verify\"];\nconst skillRoots: Record<string, string> = {\n claude: \".claude/skills\",\n codex: \".codex/skills\",\n copilot: \".copilot/skills\",\n gemini_cli: \".gemini/skills\",\n grok_cli: \".grok/skills\",\n antigravity_cli: \".gemini/config/skills\",\n opencode: \".config/opencode/skill\",\n pi: \".pi/agent/skills\",\n};\n\nfunction fixture(overrides: Partial<AgentReadinessOptions> = {}) {\n const homeDir = \"/home/test\";\n const assetRoot = \"/assets\";\n const files: Files = {\n [path.join(homeDir, \".codex\", \"hooks\", \"codex-session-mapping.cjs\")]: \"codex-hook\",\n [path.join(assetRoot, \"codex\", \"codex-session-mapping.cjs\")]: \"codex-hook\",\n [path.join(homeDir, \".codex\", \"hooks.json\")]: JSON.stringify({\n hooks: {\n SessionStart: [\n {\n hooks: [{ type: \"command\", command: \"node ~/.codex/hooks/codex-session-mapping.cjs\" }],\n },\n ],\n },\n }),\n [path.join(homeDir, \".codex\", \"ai-devkit\", \"sessions.json\")]: JSON.stringify({\n \"123\": \"/sessions/codex.jsonl\",\n }),\n \"/sessions/codex.jsonl\": \"\",\n [path.join(homeDir, \".claude\", \"hooks\", \"claude-prompt-hook.js\")]: \"claude-hook\",\n [path.join(assetRoot, \"claude\", \"claude-prompt-hook.js\")]: \"claude-hook\",\n [path.join(homeDir, \".claude\", \"settings.json\")]: JSON.stringify({\n hooks: {\n PreToolUse: [\n { hooks: [{ type: \"command\", command: \"node ~/.claude/hooks/claude-prompt-hook.js\" }] },\n ],\n },\n }),\n [path.join(homeDir, \".pi\", \"agent\", \"sessions.json\")]: JSON.stringify({\n \"456\": \"/sessions/missing-pi.jsonl\",\n }),\n [path.join(homeDir, \".pi\", \"agent\", \"auth.json\")]: JSON.stringify({\n anthropic: { access: \"access-secret\", refresh: \"refresh-secret\" },\n litellm: { key: \"litellm-secret\", env: { LITELLM_BASE_URL: \"https://litellm.example.test\" } },\n }),\n };\n for (const directory of [\n \".claude\",\n \".codex\",\n \".copilot\",\n \".gemini\",\n \".grok\",\n \".kiro\",\n \".gemini/antigravity-cli\",\n \".config/opencode\",\n \".config/devin\",\n \".pi\",\n ]) {\n files[path.join(homeDir, directory)] = \"<dir>\";\n }\n for (const root of Object.values(skillRoots)) {\n for (const skill of builtInSkillNames) {\n files[path.join(homeDir, root, skill, \"SKILL.md\")] = \"# skill\";\n }\n }\n const executablePaths = Object.values(HARNESS_RUNTIME_PROFILES).map((agent) =>\n path.join(\"/bin\", agent.command),\n );\n const options: AgentReadinessOptions = {\n homeDir,\n path: \"/bin\",\n assetRoot,\n builtInSkillNames,\n skillRoots,\n readFile: async (target) => {\n if (!(target in files)) throw new Error(\"missing\");\n return files[target];\n },\n access: async (target) => {\n if (executablePaths.includes(target) || target in files) return;\n throw new Error(\"missing\");\n },\n runCommand: vi.fn(async (command) => {\n if (command === \"pi\") return { stdout: \"npm:@ai-devkit/pi-session-tracker\\n\", stderr: \"\" };\n if (command === \"claude\") return { stdout: JSON.stringify({ loggedIn: true }), stderr: \"\" };\n if (command === \"gh\")\n return { stdout: \"github.com\\n Logged in to github.com account test-user\\n\", stderr: \"\" };\n if (command === \"opencode\")\n return { stdout: \"● litellm api\\n● OpenAI oauth\\n\", stderr: \"\" };\n if (command === \"devin\")\n return { stdout: \"Logged in (via Devin).\\n\\nUser:\\n Name: test-user\\n\", stderr: \"\" };\n throw new Error(`unexpected command ${command}`);\n }),\n codexAuth: async () => true,\n ...overrides,\n };\n return { options, files };\n}\n\ndescribe(\"agent readiness\", () => {\n it(\"reports every readiness-supported adapter with generic checks and only real integrations\", async () => {\n const { options } = fixture();\n\n const reports = await getAgentReadinessReports(options);\n\n expect(Object.keys(reports)).toEqual([\n \"claude\",\n \"codex\",\n \"gemini_cli\",\n \"grok_cli\",\n \"kiro\",\n \"antigravity_cli\",\n \"opencode\",\n \"copilot\",\n \"pi\",\n \"devin\",\n ]);\n for (const [agent, report] of Object.entries(reports) as Array<\n [ReadinessAgentType, (typeof reports)[ReadinessAgentType]]\n >) {\n expect(report.type).toBe(agent);\n expect(report.executable.status).toBe(\"pass\");\n expect(report.globalConfig.status).toBe(\"pass\");\n expect(report.builtInSkills.status).toBe(\"info\");\n }\n expect(reports.gemini_cli.builtInSkills).toMatchObject({ present: 3, required: 3 });\n expect(reports.antigravity_cli.builtInSkills).toMatchObject({ present: 3, required: 3 });\n expect(reports.kiro.builtInSkills).toMatchObject({\n path: null,\n present: 0,\n required: 3,\n missing: builtInSkillNames,\n });\n expect(reports.codex.integration).toMatchObject({\n label: \"ai-devkit hook\",\n installed: true,\n status: \"pass\",\n });\n expect(reports.claude.integration).toMatchObject({\n label: \"ai-devkit hook\",\n installed: true,\n status: \"pass\",\n });\n expect(reports.pi.integration).toMatchObject({\n label: \"ai-devkit plugin\",\n installed: true,\n status: \"pass\",\n });\n expect(reports.copilot.integration).toBeUndefined();\n expect(reports.gemini_cli.integration).toBeUndefined();\n expect(reports.grok_cli.integration).toBeUndefined();\n expect(reports.kiro.integration).toBeUndefined();\n expect(reports.antigravity_cli.integration).toBeUndefined();\n expect(reports.opencode.integration).toBeUndefined();\n });\n\n it(\"resolves agent executables from PATH directories containing spaces\", async () => {\n const spacedBin = \"/Applications/Some App.app/Contents/Resources\";\n const codexPath = path.join(spacedBin, HARNESS_RUNTIME_PROFILES.codex.command);\n const { options } = fixture({\n path: [spacedBin, \"/usr/local/bin\"].join(path.delimiter),\n access: async (target) => {\n if (target === codexPath || target.startsWith(\"/home/test\")) return;\n throw new Error(\"missing\");\n },\n });\n\n const report = await getAgentReadinessReport(\"codex\", options);\n\n expect(report.executable).toMatchObject({ command: \"codex\", path: codexPath, status: \"pass\" });\n });\n\n it(\"parses Pi provider names without returning credential values\", async () => {\n const { options } = fixture();\n\n const report = await getAgentReadinessReport(\"pi\", options);\n const serialized = JSON.stringify(report);\n\n expect(report.auth).toMatchObject({\n state: \"authenticated\",\n provider: null,\n availableProviders: [\"anthropic\", \"litellm\"],\n status: \"pass\",\n });\n expect(serialized).not.toContain(\"access-secret\");\n expect(serialized).not.toContain(\"refresh-secret\");\n expect(serialized).not.toContain(\"litellm-secret\");\n expect(serialized).not.toContain(\"litellm.example.test\");\n });\n\n it(\"accepts Pi session tracker display names from pi list output\", async () => {\n const { options } = fixture({\n runCommand: vi.fn(async (command) => {\n if (command === \"pi\") return { stdout: \"session tracker\\n\", stderr: \"\" };\n if (command === \"claude\") return { stdout: JSON.stringify({ loggedIn: true }), stderr: \"\" };\n throw new Error(`unexpected command ${command}`);\n }),\n });\n\n const report = await getAgentReadinessReport(\"pi\", options);\n\n expect(report.integration).toMatchObject({\n label: \"ai-devkit plugin\",\n installed: true,\n status: \"pass\",\n });\n });\n\n it(\"reports missing built-in skills as informational readiness\", async () => {\n const { options, files } = fixture();\n delete files[path.join(options.homeDir!, skillRoots.codex, \"verify\", \"SKILL.md\")];\n\n const report = await getAgentReadinessReport(\"codex\", options);\n\n expect(report.builtInSkills).toMatchObject({\n status: \"info\",\n present: 2,\n required: 3,\n missing: [\"verify\"],\n });\n expect(report.status).toBe(\"pass\");\n });\n\n it(\"checks OpenCode auth from auth list output\", async () => {\n const { options } = fixture({\n runCommand: vi.fn(async (command) => {\n if (command === \"opencode\") {\n return {\n stdout: \"\\x1B[32m●\\x1B[0m litellm api\\n● OpenAI oauth\\n● GitLab Duo GITLAB_TOKEN\\n\",\n stderr: \"\",\n };\n }\n if (command === \"pi\") return { stdout: \"npm:@ai-devkit/pi-session-tracker\\n\", stderr: \"\" };\n if (command === \"claude\") return { stdout: JSON.stringify({ loggedIn: true }), stderr: \"\" };\n throw new Error(`unexpected command ${command}`);\n }),\n });\n\n const report = await getAgentReadinessReport(\"opencode\", options);\n\n expect(report.auth).toMatchObject({\n state: \"authenticated\",\n source: \"opencode auth list\",\n provider: null,\n availableProviders: [\"GitLab Duo\", \"OpenAI\", \"litellm\"],\n status: \"pass\",\n });\n });\n\n it(\"checks Copilot auth through GitHub CLI authentication\", async () => {\n const { options } = fixture({\n runCommand: vi.fn(async (command) => {\n if (command === \"gh\")\n return {\n stdout: \"github.com\\n Logged in to github.com account test-user\\n\",\n stderr: \"\",\n };\n if (command === \"pi\") return { stdout: \"npm:@ai-devkit/pi-session-tracker\\n\", stderr: \"\" };\n if (command === \"claude\") return { stdout: JSON.stringify({ loggedIn: true }), stderr: \"\" };\n if (command === \"opencode\") return { stdout: \"● litellm api\\n\", stderr: \"\" };\n throw new Error(`unexpected command ${command}`);\n }),\n });\n\n const report = await getAgentReadinessReport(\"copilot\", options);\n\n expect(report.auth).toMatchObject({\n state: \"authenticated\",\n source: \"gh auth status --hostname github.com\",\n provider: \"github\",\n availableProviders: [\"GitHub\"],\n status: \"pass\",\n });\n });\n\n it(\"checks Devin auth from devin auth status output\", async () => {\n const { options } = fixture({\n runCommand: vi.fn(async (command) => {\n if (command === \"devin\")\n return { stdout: \"Logged in (via Devin).\\n\\nUser:\\n Name: test-user\\n\", stderr: \"\" };\n if (command === \"pi\") return { stdout: \"npm:@ai-devkit/pi-session-tracker\\n\", stderr: \"\" };\n if (command === \"claude\") return { stdout: JSON.stringify({ loggedIn: true }), stderr: \"\" };\n throw new Error(`unexpected command ${command}`);\n }),\n });\n\n const report = await getAgentReadinessReport(\"devin\", options);\n\n expect(report.auth).toMatchObject({\n state: \"authenticated\",\n source: \"devin auth status\",\n provider: null,\n availableProviders: [\"devin\"],\n status: \"pass\",\n });\n });\n\n it(\"reports Devin as unauthenticated without a login line\", async () => {\n const { options } = fixture({\n runCommand: vi.fn(async (command) => {\n if (command === \"devin\") return { stdout: \"Not logged in\\n\", stderr: \"\" };\n if (command === \"pi\") return { stdout: \"npm:@ai-devkit/pi-session-tracker\\n\", stderr: \"\" };\n if (command === \"claude\") return { stdout: JSON.stringify({ loggedIn: true }), stderr: \"\" };\n throw new Error(`unexpected command ${command}`);\n }),\n });\n\n const report = await getAgentReadinessReport(\"devin\", options);\n\n expect(report.auth).toMatchObject({ state: \"unauthenticated\", status: \"fail\" });\n });\n});\n"],"names":["path","describe","expect","it","vi","getAgentReadinessReport","getAgentReadinessReports","HARNESS_RUNTIME_PROFILES","builtInSkillNames","skillRoots","claude","codex","copilot","gemini_cli","grok_cli","antigravity_cli","opencode","pi","fixture","overrides","homeDir","assetRoot","files","join","JSON","stringify","hooks","SessionStart","type","command","PreToolUse","anthropic","access","refresh","litellm","key","env","LITELLM_BASE_URL","directory","root","Object","values","skill","executablePaths","map","agent","options","readFile","target","Error","includes","runCommand","fn","stdout","stderr","loggedIn","codexAuth","reports","keys","toEqual","report","entries","toBe","executable","status","globalConfig","builtInSkills","toMatchObject","present","required","kiro","missing","integration","label","installed","toBeUndefined","spacedBin","codexPath","delimiter","startsWith","serialized","auth","state","provider","availableProviders","not","toContain","source"],"mappings":"AAAA,OAAOA,UAAU,YAAY;AAC7B,SAASC,QAAQ,EAAEC,MAAM,EAAEC,EAAE,EAAEC,EAAE,QAAQ,SAAS;AAClD,SACEC,uBAAuB,EACvBC,wBAAwB,QAGnB,iDAAiD;AACxD,SAASC,wBAAwB,QAAQ,wCAAwC;AAIjF,MAAMC,oBAAoB;IAAC;IAAoB;IAAU;CAAS;AAClE,MAAMC,aAAqC;IACzCC,QAAQ;IACRC,OAAO;IACPC,SAAS;IACTC,YAAY;IACZC,UAAU;IACVC,iBAAiB;IACjBC,UAAU;IACVC,IAAI;AACN;AAEA,SAASC,QAAQC,YAA4C,CAAC,CAAC;IAC7D,MAAMC,UAAU;IAChB,MAAMC,YAAY;IAClB,MAAMC,QAAe;QACnB,CAACtB,KAAKuB,IAAI,CAACH,SAAS,UAAU,SAAS,6BAA6B,EAAE;QACtE,CAACpB,KAAKuB,IAAI,CAACF,WAAW,SAAS,6BAA6B,EAAE;QAC9D,CAACrB,KAAKuB,IAAI,CAACH,SAAS,UAAU,cAAc,EAAEI,KAAKC,SAAS,CAAC;YAC3DC,OAAO;gBACLC,cAAc;oBACZ;wBACED,OAAO;4BAAC;gCAAEE,MAAM;gCAAWC,SAAS;4BAAgD;yBAAE;oBACxF;iBACD;YACH;QACF;QACA,CAAC7B,KAAKuB,IAAI,CAACH,SAAS,UAAU,aAAa,iBAAiB,EAAEI,KAAKC,SAAS,CAAC;YAC3E,OAAO;QACT;QACA,yBAAyB;QACzB,CAACzB,KAAKuB,IAAI,CAACH,SAAS,WAAW,SAAS,yBAAyB,EAAE;QACnE,CAACpB,KAAKuB,IAAI,CAACF,WAAW,UAAU,yBAAyB,EAAE;QAC3D,CAACrB,KAAKuB,IAAI,CAACH,SAAS,WAAW,iBAAiB,EAAEI,KAAKC,SAAS,CAAC;YAC/DC,OAAO;gBACLI,YAAY;oBACV;wBAAEJ,OAAO;4BAAC;gCAAEE,MAAM;gCAAWC,SAAS;4BAA6C;yBAAE;oBAAC;iBACvF;YACH;QACF;QACA,CAAC7B,KAAKuB,IAAI,CAACH,SAAS,OAAO,SAAS,iBAAiB,EAAEI,KAAKC,SAAS,CAAC;YACpE,OAAO;QACT;QACA,CAACzB,KAAKuB,IAAI,CAACH,SAAS,OAAO,SAAS,aAAa,EAAEI,KAAKC,SAAS,CAAC;YAChEM,WAAW;gBAAEC,QAAQ;gBAAiBC,SAAS;YAAiB;YAChEC,SAAS;gBAAEC,KAAK;gBAAkBC,KAAK;oBAAEC,kBAAkB;gBAA+B;YAAE;QAC9F;IACF;IACA,KAAK,MAAMC,aAAa;QACtB;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;KACD,CAAE;QACDhB,KAAK,CAACtB,KAAKuB,IAAI,CAACH,SAASkB,WAAW,GAAG;IACzC;IACA,KAAK,MAAMC,QAAQC,OAAOC,MAAM,CAAChC,YAAa;QAC5C,KAAK,MAAMiC,SAASlC,kBAAmB;YACrCc,KAAK,CAACtB,KAAKuB,IAAI,CAACH,SAASmB,MAAMG,OAAO,YAAY,GAAG;QACvD;IACF;IACA,MAAMC,kBAAkBH,OAAOC,MAAM,CAAClC,0BAA0BqC,GAAG,CAAC,CAACC,QACnE7C,KAAKuB,IAAI,CAAC,QAAQsB,MAAMhB,OAAO;IAEjC,MAAMiB,UAAiC;QACrC1B;QACApB,MAAM;QACNqB;QACAb;QACAC;QACAsC,UAAU,OAAOC;YACf,IAAI,CAAEA,CAAAA,UAAU1B,KAAI,GAAI,MAAM,IAAI2B,MAAM;YACxC,OAAO3B,KAAK,CAAC0B,OAAO;QACtB;QACAhB,QAAQ,OAAOgB;YACb,IAAIL,gBAAgBO,QAAQ,CAACF,WAAWA,UAAU1B,OAAO;YACzD,MAAM,IAAI2B,MAAM;QAClB;QACAE,YAAY/C,GAAGgD,EAAE,CAAC,OAAOvB;YACvB,IAAIA,YAAY,MAAM,OAAO;gBAAEwB,QAAQ;gBAAuCC,QAAQ;YAAG;YACzF,IAAIzB,YAAY,UAAU,OAAO;gBAAEwB,QAAQ7B,KAAKC,SAAS,CAAC;oBAAE8B,UAAU;gBAAK;gBAAID,QAAQ;YAAG;YAC1F,IAAIzB,YAAY,MACd,OAAO;gBAAEwB,QAAQ;gBAA6DC,QAAQ;YAAG;YAC3F,IAAIzB,YAAY,YACd,OAAO;gBAAEwB,QAAQ;gBAAqCC,QAAQ;YAAG;YACnE,IAAIzB,YAAY,SACd,OAAO;gBAAEwB,QAAQ;gBAAwDC,QAAQ;YAAG;YACtF,MAAM,IAAIL,MAAM,CAAC,mBAAmB,EAAEpB,SAAS;QACjD;QACA2B,WAAW,UAAY;QACvB,GAAGrC,SAAS;IACd;IACA,OAAO;QAAE2B;QAASxB;IAAM;AAC1B;AAEArB,SAAS,mBAAmB;IAC1BE,GAAG,4FAA4F;QAC7F,MAAM,EAAE2C,OAAO,EAAE,GAAG5B;QAEpB,MAAMuC,UAAU,MAAMnD,yBAAyBwC;QAE/C5C,OAAOsC,OAAOkB,IAAI,CAACD,UAAUE,OAAO,CAAC;YACnC;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;YACA;SACD;QACD,KAAK,MAAM,CAACd,OAAOe,OAAO,IAAIpB,OAAOqB,OAAO,CAACJ,SAE1C;YACDvD,OAAO0D,OAAOhC,IAAI,EAAEkC,IAAI,CAACjB;YACzB3C,OAAO0D,OAAOG,UAAU,CAACC,MAAM,EAAEF,IAAI,CAAC;YACtC5D,OAAO0D,OAAOK,YAAY,CAACD,MAAM,EAAEF,IAAI,CAAC;YACxC5D,OAAO0D,OAAOM,aAAa,CAACF,MAAM,EAAEF,IAAI,CAAC;QAC3C;QACA5D,OAAOuD,QAAQ5C,UAAU,CAACqD,aAAa,EAAEC,aAAa,CAAC;YAAEC,SAAS;YAAGC,UAAU;QAAE;QACjFnE,OAAOuD,QAAQ1C,eAAe,CAACmD,aAAa,EAAEC,aAAa,CAAC;YAAEC,SAAS;YAAGC,UAAU;QAAE;QACtFnE,OAAOuD,QAAQa,IAAI,CAACJ,aAAa,EAAEC,aAAa,CAAC;YAC/CnE,MAAM;YACNoE,SAAS;YACTC,UAAU;YACVE,SAAS/D;QACX;QACAN,OAAOuD,QAAQ9C,KAAK,CAAC6D,WAAW,EAAEL,aAAa,CAAC;YAC9CM,OAAO;YACPC,WAAW;YACXV,QAAQ;QACV;QACA9D,OAAOuD,QAAQ/C,MAAM,CAAC8D,WAAW,EAAEL,aAAa,CAAC;YAC/CM,OAAO;YACPC,WAAW;YACXV,QAAQ;QACV;QACA9D,OAAOuD,QAAQxC,EAAE,CAACuD,WAAW,EAAEL,aAAa,CAAC;YAC3CM,OAAO;YACPC,WAAW;YACXV,QAAQ;QACV;QACA9D,OAAOuD,QAAQ7C,OAAO,CAAC4D,WAAW,EAAEG,aAAa;QACjDzE,OAAOuD,QAAQ5C,UAAU,CAAC2D,WAAW,EAAEG,aAAa;QACpDzE,OAAOuD,QAAQ3C,QAAQ,CAAC0D,WAAW,EAAEG,aAAa;QAClDzE,OAAOuD,QAAQa,IAAI,CAACE,WAAW,EAAEG,aAAa;QAC9CzE,OAAOuD,QAAQ1C,eAAe,CAACyD,WAAW,EAAEG,aAAa;QACzDzE,OAAOuD,QAAQzC,QAAQ,CAACwD,WAAW,EAAEG,aAAa;IACpD;IAEAxE,GAAG,sEAAsE;QACvE,MAAMyE,YAAY;QAClB,MAAMC,YAAY7E,KAAKuB,IAAI,CAACqD,WAAWrE,yBAAyBI,KAAK,CAACkB,OAAO;QAC7E,MAAM,EAAEiB,OAAO,EAAE,GAAG5B,QAAQ;YAC1BlB,MAAM;gBAAC4E;gBAAW;aAAiB,CAACrD,IAAI,CAACvB,KAAK8E,SAAS;YACvD9C,QAAQ,OAAOgB;gBACb,IAAIA,WAAW6B,aAAa7B,OAAO+B,UAAU,CAAC,eAAe;gBAC7D,MAAM,IAAI9B,MAAM;YAClB;QACF;QAEA,MAAMW,SAAS,MAAMvD,wBAAwB,SAASyC;QAEtD5C,OAAO0D,OAAOG,UAAU,EAAEI,aAAa,CAAC;YAAEtC,SAAS;YAAS7B,MAAM6E;YAAWb,QAAQ;QAAO;IAC9F;IAEA7D,GAAG,gEAAgE;QACjE,MAAM,EAAE2C,OAAO,EAAE,GAAG5B;QAEpB,MAAM0C,SAAS,MAAMvD,wBAAwB,MAAMyC;QACnD,MAAMkC,aAAaxD,KAAKC,SAAS,CAACmC;QAElC1D,OAAO0D,OAAOqB,IAAI,EAAEd,aAAa,CAAC;YAChCe,OAAO;YACPC,UAAU;YACVC,oBAAoB;gBAAC;gBAAa;aAAU;YAC5CpB,QAAQ;QACV;QACA9D,OAAO8E,YAAYK,GAAG,CAACC,SAAS,CAAC;QACjCpF,OAAO8E,YAAYK,GAAG,CAACC,SAAS,CAAC;QACjCpF,OAAO8E,YAAYK,GAAG,CAACC,SAAS,CAAC;QACjCpF,OAAO8E,YAAYK,GAAG,CAACC,SAAS,CAAC;IACnC;IAEAnF,GAAG,gEAAgE;QACjE,MAAM,EAAE2C,OAAO,EAAE,GAAG5B,QAAQ;YAC1BiC,YAAY/C,GAAGgD,EAAE,CAAC,OAAOvB;gBACvB,IAAIA,YAAY,MAAM,OAAO;oBAAEwB,QAAQ;oBAAqBC,QAAQ;gBAAG;gBACvE,IAAIzB,YAAY,UAAU,OAAO;oBAAEwB,QAAQ7B,KAAKC,SAAS,CAAC;wBAAE8B,UAAU;oBAAK;oBAAID,QAAQ;gBAAG;gBAC1F,MAAM,IAAIL,MAAM,CAAC,mBAAmB,EAAEpB,SAAS;YACjD;QACF;QAEA,MAAM+B,SAAS,MAAMvD,wBAAwB,MAAMyC;QAEnD5C,OAAO0D,OAAOY,WAAW,EAAEL,aAAa,CAAC;YACvCM,OAAO;YACPC,WAAW;YACXV,QAAQ;QACV;IACF;IAEA7D,GAAG,8DAA8D;QAC/D,MAAM,EAAE2C,OAAO,EAAExB,KAAK,EAAE,GAAGJ;QAC3B,OAAOI,KAAK,CAACtB,KAAKuB,IAAI,CAACuB,QAAQ1B,OAAO,EAAGX,WAAWE,KAAK,EAAE,UAAU,YAAY;QAEjF,MAAMiD,SAAS,MAAMvD,wBAAwB,SAASyC;QAEtD5C,OAAO0D,OAAOM,aAAa,EAAEC,aAAa,CAAC;YACzCH,QAAQ;YACRI,SAAS;YACTC,UAAU;YACVE,SAAS;gBAAC;aAAS;QACrB;QACArE,OAAO0D,OAAOI,MAAM,EAAEF,IAAI,CAAC;IAC7B;IAEA3D,GAAG,8CAA8C;QAC/C,MAAM,EAAE2C,OAAO,EAAE,GAAG5B,QAAQ;YAC1BiC,YAAY/C,GAAGgD,EAAE,CAAC,OAAOvB;gBACvB,IAAIA,YAAY,YAAY;oBAC1B,OAAO;wBACLwB,QAAQ;wBACRC,QAAQ;oBACV;gBACF;gBACA,IAAIzB,YAAY,MAAM,OAAO;oBAAEwB,QAAQ;oBAAuCC,QAAQ;gBAAG;gBACzF,IAAIzB,YAAY,UAAU,OAAO;oBAAEwB,QAAQ7B,KAAKC,SAAS,CAAC;wBAAE8B,UAAU;oBAAK;oBAAID,QAAQ;gBAAG;gBAC1F,MAAM,IAAIL,MAAM,CAAC,mBAAmB,EAAEpB,SAAS;YACjD;QACF;QAEA,MAAM+B,SAAS,MAAMvD,wBAAwB,YAAYyC;QAEzD5C,OAAO0D,OAAOqB,IAAI,EAAEd,aAAa,CAAC;YAChCe,OAAO;YACPK,QAAQ;YACRJ,UAAU;YACVC,oBAAoB;gBAAC;gBAAc;gBAAU;aAAU;YACvDpB,QAAQ;QACV;IACF;IAEA7D,GAAG,yDAAyD;QAC1D,MAAM,EAAE2C,OAAO,EAAE,GAAG5B,QAAQ;YAC1BiC,YAAY/C,GAAGgD,EAAE,CAAC,OAAOvB;gBACvB,IAAIA,YAAY,MACd,OAAO;oBACLwB,QAAQ;oBACRC,QAAQ;gBACV;gBACF,IAAIzB,YAAY,MAAM,OAAO;oBAAEwB,QAAQ;oBAAuCC,QAAQ;gBAAG;gBACzF,IAAIzB,YAAY,UAAU,OAAO;oBAAEwB,QAAQ7B,KAAKC,SAAS,CAAC;wBAAE8B,UAAU;oBAAK;oBAAID,QAAQ;gBAAG;gBAC1F,IAAIzB,YAAY,YAAY,OAAO;oBAAEwB,QAAQ;oBAAoBC,QAAQ;gBAAG;gBAC5E,MAAM,IAAIL,MAAM,CAAC,mBAAmB,EAAEpB,SAAS;YACjD;QACF;QAEA,MAAM+B,SAAS,MAAMvD,wBAAwB,WAAWyC;QAExD5C,OAAO0D,OAAOqB,IAAI,EAAEd,aAAa,CAAC;YAChCe,OAAO;YACPK,QAAQ;YACRJ,UAAU;YACVC,oBAAoB;gBAAC;aAAS;YAC9BpB,QAAQ;QACV;IACF;IAEA7D,GAAG,mDAAmD;QACpD,MAAM,EAAE2C,OAAO,EAAE,GAAG5B,QAAQ;YAC1BiC,YAAY/C,GAAGgD,EAAE,CAAC,OAAOvB;gBACvB,IAAIA,YAAY,SACd,OAAO;oBAAEwB,QAAQ;oBAAwDC,QAAQ;gBAAG;gBACtF,IAAIzB,YAAY,MAAM,OAAO;oBAAEwB,QAAQ;oBAAuCC,QAAQ;gBAAG;gBACzF,IAAIzB,YAAY,UAAU,OAAO;oBAAEwB,QAAQ7B,KAAKC,SAAS,CAAC;wBAAE8B,UAAU;oBAAK;oBAAID,QAAQ;gBAAG;gBAC1F,MAAM,IAAIL,MAAM,CAAC,mBAAmB,EAAEpB,SAAS;YACjD;QACF;QAEA,MAAM+B,SAAS,MAAMvD,wBAAwB,SAASyC;QAEtD5C,OAAO0D,OAAOqB,IAAI,EAAEd,aAAa,CAAC;YAChCe,OAAO;YACPK,QAAQ;YACRJ,UAAU;YACVC,oBAAoB;gBAAC;aAAQ;YAC7BpB,QAAQ;QACV;IACF;IAEA7D,GAAG,yDAAyD;QAC1D,MAAM,EAAE2C,OAAO,EAAE,GAAG5B,QAAQ;YAC1BiC,YAAY/C,GAAGgD,EAAE,CAAC,OAAOvB;gBACvB,IAAIA,YAAY,SAAS,OAAO;oBAAEwB,QAAQ;oBAAmBC,QAAQ;gBAAG;gBACxE,IAAIzB,YAAY,MAAM,OAAO;oBAAEwB,QAAQ;oBAAuCC,QAAQ;gBAAG;gBACzF,IAAIzB,YAAY,UAAU,OAAO;oBAAEwB,QAAQ7B,KAAKC,SAAS,CAAC;wBAAE8B,UAAU;oBAAK;oBAAID,QAAQ;gBAAG;gBAC1F,MAAM,IAAIL,MAAM,CAAC,mBAAmB,EAAEpB,SAAS;YACjD;QACF;QAEA,MAAM+B,SAAS,MAAMvD,wBAAwB,SAASyC;QAEtD5C,OAAO0D,OAAOqB,IAAI,EAAEd,aAAa,CAAC;YAAEe,OAAO;YAAmBlB,QAAQ;QAAO;IAC/E;AACF"}
|
|
1
|
+
{"version":3,"sources":["../../../../src/__tests__/harnesses/readiness/AgentReadiness.test.ts"],"sourcesContent":["import path from \"node:path\";\nimport { describe, expect, it, vi } from \"vitest\";\nimport {\n getAgentReadinessReport,\n getAgentReadinessReports,\n type AgentReadinessOptions,\n type ReadinessAgentType,\n} from \"../../../harnesses/readiness/AgentReadiness.js\";\nimport { HARNESS_RUNTIME_PROFILES } from \"../../../harnesses/runtimeProfiles.js\";\n\ntype Files = Record<string, string>;\n\nconst builtInSkillNames = [\"agent-management\", \"memory\", \"verify\"];\nconst skillRoots: Record<string, string> = {\n claude: \".claude/skills\",\n codex: \".codex/skills\",\n copilot: \".copilot/skills\",\n gemini_cli: \".gemini/skills\",\n grok_cli: \".grok/skills\",\n antigravity_cli: \".gemini/config/skills\",\n opencode: \".config/opencode/skill\",\n pi: \".pi/agent/skills\",\n};\n\nfunction fixture(overrides: Partial<AgentReadinessOptions> = {}) {\n const homeDir = \"/home/test\";\n const assetRoot = \"/assets\";\n const files: Files = {\n [path.join(homeDir, \".codex\", \"hooks\", \"codex-session-mapping.cjs\")]: \"codex-hook\",\n [path.join(assetRoot, \"codex\", \"codex-session-mapping.cjs\")]: \"codex-hook\",\n [path.join(homeDir, \".codex\", \"hooks.json\")]: JSON.stringify({\n hooks: {\n SessionStart: [\n {\n hooks: [{ type: \"command\", command: \"node ~/.codex/hooks/codex-session-mapping.cjs\" }],\n },\n ],\n },\n }),\n [path.join(homeDir, \".codex\", \"ai-devkit\", \"sessions.json\")]: JSON.stringify({\n \"123\": \"/sessions/codex.jsonl\",\n }),\n \"/sessions/codex.jsonl\": \"\",\n [path.join(homeDir, \".claude\", \"hooks\", \"claude-prompt-hook.js\")]: \"claude-hook\",\n [path.join(assetRoot, \"claude\", \"claude-prompt-hook.js\")]: \"claude-hook\",\n [path.join(homeDir, \".claude\", \"settings.json\")]: JSON.stringify({\n hooks: {\n PreToolUse: [\n { hooks: [{ type: \"command\", command: \"node ~/.claude/hooks/claude-prompt-hook.js\" }] },\n ],\n },\n }),\n [path.join(homeDir, \".pi\", \"agent\", \"sessions.json\")]: JSON.stringify({\n \"456\": \"/sessions/missing-pi.jsonl\",\n }),\n [path.join(homeDir, \".pi\", \"agent\", \"auth.json\")]: JSON.stringify({\n anthropic: { access: \"access-secret\", refresh: \"refresh-secret\" },\n litellm: { key: \"litellm-secret\", env: { LITELLM_BASE_URL: \"https://litellm.example.test\" } },\n }),\n };\n for (const directory of [\n \".claude\",\n \".codex\",\n \".copilot\",\n \".gemini\",\n \".grok\",\n \".kiro\",\n \".gemini/antigravity-cli\",\n \".config/opencode\",\n \".config/devin\",\n \".config/muse\",\n \".pi\",\n ]) {\n files[path.join(homeDir, directory)] = \"<dir>\";\n }\n for (const root of Object.values(skillRoots)) {\n for (const skill of builtInSkillNames) {\n files[path.join(homeDir, root, skill, \"SKILL.md\")] = \"# skill\";\n }\n }\n const executablePaths = Object.values(HARNESS_RUNTIME_PROFILES).map((agent) =>\n path.join(\"/bin\", agent.command),\n );\n const options: AgentReadinessOptions = {\n homeDir,\n path: \"/bin\",\n assetRoot,\n builtInSkillNames,\n skillRoots,\n readFile: async (target) => {\n if (!(target in files)) throw new Error(\"missing\");\n return files[target];\n },\n access: async (target) => {\n if (executablePaths.includes(target) || target in files) return;\n throw new Error(\"missing\");\n },\n runCommand: vi.fn(async (command) => {\n if (command === \"pi\") return { stdout: \"npm:@ai-devkit/pi-session-tracker\\n\", stderr: \"\" };\n if (command === \"claude\") return { stdout: JSON.stringify({ loggedIn: true }), stderr: \"\" };\n if (command === \"gh\")\n return { stdout: \"github.com\\n Logged in to github.com account test-user\\n\", stderr: \"\" };\n if (command === \"opencode\")\n return { stdout: \"● litellm api\\n● OpenAI oauth\\n\", stderr: \"\" };\n if (command === \"devin\")\n return { stdout: \"Logged in (via Devin).\\n\\nUser:\\n Name: test-user\\n\", stderr: \"\" };\n throw new Error(`unexpected command ${command}`);\n }),\n codexAuth: async () => true,\n ...overrides,\n };\n return { options, files };\n}\n\ndescribe(\"agent readiness\", () => {\n it(\"reports every readiness-supported adapter with generic checks and only real integrations\", async () => {\n const { options } = fixture();\n\n const reports = await getAgentReadinessReports(options);\n\n expect(Object.keys(reports)).toEqual([\n \"claude\",\n \"codex\",\n \"gemini_cli\",\n \"grok_cli\",\n \"kiro\",\n \"antigravity_cli\",\n \"opencode\",\n \"copilot\",\n \"pi\",\n \"devin\",\n \"muse\",\n ]);\n for (const [agent, report] of Object.entries(reports) as Array<\n [ReadinessAgentType, (typeof reports)[ReadinessAgentType]]\n >) {\n expect(report.type).toBe(agent);\n expect(report.executable.status).toBe(\"pass\");\n expect(report.globalConfig.status).toBe(\"pass\");\n expect(report.builtInSkills.status).toBe(\"info\");\n }\n expect(reports.gemini_cli.builtInSkills).toMatchObject({ present: 3, required: 3 });\n expect(reports.antigravity_cli.builtInSkills).toMatchObject({ present: 3, required: 3 });\n expect(reports.kiro.builtInSkills).toMatchObject({\n path: null,\n present: 0,\n required: 3,\n missing: builtInSkillNames,\n });\n expect(reports.codex.integration).toMatchObject({\n label: \"ai-devkit hook\",\n installed: true,\n status: \"pass\",\n });\n expect(reports.claude.integration).toMatchObject({\n label: \"ai-devkit hook\",\n installed: true,\n status: \"pass\",\n });\n expect(reports.pi.integration).toMatchObject({\n label: \"ai-devkit plugin\",\n installed: true,\n status: \"pass\",\n });\n expect(reports.copilot.integration).toBeUndefined();\n expect(reports.gemini_cli.integration).toBeUndefined();\n expect(reports.grok_cli.integration).toBeUndefined();\n expect(reports.kiro.integration).toBeUndefined();\n expect(reports.antigravity_cli.integration).toBeUndefined();\n expect(reports.opencode.integration).toBeUndefined();\n expect(reports.muse.integration).toBeUndefined();\n });\n\n it(\"resolves agent executables from PATH directories containing spaces\", async () => {\n const spacedBin = \"/Applications/Some App.app/Contents/Resources\";\n const codexPath = path.join(spacedBin, HARNESS_RUNTIME_PROFILES.codex.command);\n const { options } = fixture({\n path: [spacedBin, \"/usr/local/bin\"].join(path.delimiter),\n access: async (target) => {\n if (target === codexPath || target.startsWith(\"/home/test\")) return;\n throw new Error(\"missing\");\n },\n });\n\n const report = await getAgentReadinessReport(\"codex\", options);\n\n expect(report.executable).toMatchObject({ command: \"codex\", path: codexPath, status: \"pass\" });\n });\n\n it(\"parses Pi provider names without returning credential values\", async () => {\n const { options } = fixture();\n\n const report = await getAgentReadinessReport(\"pi\", options);\n const serialized = JSON.stringify(report);\n\n expect(report.auth).toMatchObject({\n state: \"authenticated\",\n provider: null,\n availableProviders: [\"anthropic\", \"litellm\"],\n status: \"pass\",\n });\n expect(serialized).not.toContain(\"access-secret\");\n expect(serialized).not.toContain(\"refresh-secret\");\n expect(serialized).not.toContain(\"litellm-secret\");\n expect(serialized).not.toContain(\"litellm.example.test\");\n });\n\n it(\"accepts Pi session tracker display names from pi list output\", async () => {\n const { options } = fixture({\n runCommand: vi.fn(async (command) => {\n if (command === \"pi\") return { stdout: \"session tracker\\n\", stderr: \"\" };\n if (command === \"claude\") return { stdout: JSON.stringify({ loggedIn: true }), stderr: \"\" };\n throw new Error(`unexpected command ${command}`);\n }),\n });\n\n const report = await getAgentReadinessReport(\"pi\", options);\n\n expect(report.integration).toMatchObject({\n label: \"ai-devkit plugin\",\n installed: true,\n status: \"pass\",\n });\n });\n\n it(\"reports missing built-in skills as informational readiness\", async () => {\n const { options, files } = fixture();\n delete files[path.join(options.homeDir!, skillRoots.codex, \"verify\", \"SKILL.md\")];\n\n const report = await getAgentReadinessReport(\"codex\", options);\n\n expect(report.builtInSkills).toMatchObject({\n status: \"info\",\n present: 2,\n required: 3,\n missing: [\"verify\"],\n });\n expect(report.status).toBe(\"pass\");\n });\n\n it(\"checks OpenCode auth from auth list output\", async () => {\n const { options } = fixture({\n runCommand: vi.fn(async (command) => {\n if (command === \"opencode\") {\n return {\n stdout: \"\\x1B[32m●\\x1B[0m litellm api\\n● OpenAI oauth\\n● GitLab Duo GITLAB_TOKEN\\n\",\n stderr: \"\",\n };\n }\n if (command === \"pi\") return { stdout: \"npm:@ai-devkit/pi-session-tracker\\n\", stderr: \"\" };\n if (command === \"claude\") return { stdout: JSON.stringify({ loggedIn: true }), stderr: \"\" };\n throw new Error(`unexpected command ${command}`);\n }),\n });\n\n const report = await getAgentReadinessReport(\"opencode\", options);\n\n expect(report.auth).toMatchObject({\n state: \"authenticated\",\n source: \"opencode auth list\",\n provider: null,\n availableProviders: [\"GitLab Duo\", \"OpenAI\", \"litellm\"],\n status: \"pass\",\n });\n });\n\n it(\"checks Copilot auth through GitHub CLI authentication\", async () => {\n const { options } = fixture({\n runCommand: vi.fn(async (command) => {\n if (command === \"gh\")\n return {\n stdout: \"github.com\\n Logged in to github.com account test-user\\n\",\n stderr: \"\",\n };\n if (command === \"pi\") return { stdout: \"npm:@ai-devkit/pi-session-tracker\\n\", stderr: \"\" };\n if (command === \"claude\") return { stdout: JSON.stringify({ loggedIn: true }), stderr: \"\" };\n if (command === \"opencode\") return { stdout: \"● litellm api\\n\", stderr: \"\" };\n throw new Error(`unexpected command ${command}`);\n }),\n });\n\n const report = await getAgentReadinessReport(\"copilot\", options);\n\n expect(report.auth).toMatchObject({\n state: \"authenticated\",\n source: \"gh auth status --hostname github.com\",\n provider: \"github\",\n availableProviders: [\"GitHub\"],\n status: \"pass\",\n });\n });\n\n it(\"checks Devin auth from devin auth status output\", async () => {\n const { options } = fixture({\n runCommand: vi.fn(async (command) => {\n if (command === \"devin\")\n return { stdout: \"Logged in (via Devin).\\n\\nUser:\\n Name: test-user\\n\", stderr: \"\" };\n if (command === \"pi\") return { stdout: \"npm:@ai-devkit/pi-session-tracker\\n\", stderr: \"\" };\n if (command === \"claude\") return { stdout: JSON.stringify({ loggedIn: true }), stderr: \"\" };\n throw new Error(`unexpected command ${command}`);\n }),\n });\n\n const report = await getAgentReadinessReport(\"devin\", options);\n\n expect(report.auth).toMatchObject({\n state: \"authenticated\",\n source: \"devin auth status\",\n provider: null,\n availableProviders: [\"devin\"],\n status: \"pass\",\n });\n });\n\n it(\"checks Muse auth from the muse auth.json file\", async () => {\n const authPath = path.join(\"/home/test\", \".config\", \"muse\", \"auth.json\");\n const { options, files } = fixture();\n files[authPath] = JSON.stringify({ meta: { token: \"secret\" } });\n\n const report = await getAgentReadinessReport(\"muse\", options);\n\n expect(report.auth).toMatchObject({ state: \"authenticated\", status: \"pass\" });\n expect(report.globalConfig).toMatchObject({ status: \"pass\" });\n });\n\n it(\"reports Muse as unauthenticated with an empty auth file\", async () => {\n const authPath = path.join(\"/home/test\", \".config\", \"muse\", \"auth.json\");\n const { options, files } = fixture();\n files[authPath] = JSON.stringify({});\n\n const report = await getAgentReadinessReport(\"muse\", options);\n\n expect(report.auth).toMatchObject({\n state: \"unauthenticated\",\n status: \"fail\",\n errors: [\"Muse credential file has no entries\"],\n });\n });\n\n it(\"reports Muse as unauthenticated without an auth file\", async () => {\n const { options } = fixture();\n\n const report = await getAgentReadinessReport(\"muse\", options);\n\n expect(report.auth).toMatchObject({ state: \"unauthenticated\", status: \"fail\" });\n });\n\n it(\"reports Devin as unauthenticated without a login line\", async () => {\n const { options } = fixture({\n runCommand: vi.fn(async (command) => {\n if (command === \"devin\") return { stdout: \"Not logged in\\n\", stderr: \"\" };\n if (command === \"pi\") return { stdout: \"npm:@ai-devkit/pi-session-tracker\\n\", stderr: \"\" };\n if (command === \"claude\") return { stdout: JSON.stringify({ loggedIn: true }), stderr: \"\" };\n throw new Error(`unexpected command ${command}`);\n }),\n });\n\n const report = await getAgentReadinessReport(\"devin\", options);\n\n expect(report.auth).toMatchObject({ state: \"unauthenticated\", status: \"fail\" });\n });\n});\n"],"names":["path","describe","expect","it","vi","getAgentReadinessReport","getAgentReadinessReports","HARNESS_RUNTIME_PROFILES","builtInSkillNames","skillRoots","claude","codex","copilot","gemini_cli","grok_cli","antigravity_cli","opencode","pi","fixture","overrides","homeDir","assetRoot","files","join","JSON","stringify","hooks","SessionStart","type","command","PreToolUse","anthropic","access","refresh","litellm","key","env","LITELLM_BASE_URL","directory","root","Object","values","skill","executablePaths","map","agent","options","readFile","target","Error","includes","runCommand","fn","stdout","stderr","loggedIn","codexAuth","reports","keys","toEqual","report","entries","toBe","executable","status","globalConfig","builtInSkills","toMatchObject","present","required","kiro","missing","integration","label","installed","toBeUndefined","muse","spacedBin","codexPath","delimiter","startsWith","serialized","auth","state","provider","availableProviders","not","toContain","source","authPath","meta","token","errors"],"mappings":"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"}
|
|
@@ -28,6 +28,18 @@ describe("harness runtime profiles", ()=>{
|
|
|
28
28
|
expect(runtimeAgentMatchesHarness("gemini", "gemini_cli")).toBe(true);
|
|
29
29
|
expect(runtimeAgentMatchesHarness("codex", "claude")).toBe(false);
|
|
30
30
|
});
|
|
31
|
+
it("profiles the versioned muse launcher", ()=>{
|
|
32
|
+
expect(HARNESS_RUNTIME_PROFILES.muse).toMatchObject({
|
|
33
|
+
command: "muse",
|
|
34
|
+
runtimeKind: "muse",
|
|
35
|
+
discoveryAliases: [
|
|
36
|
+
"muse"
|
|
37
|
+
]
|
|
38
|
+
});
|
|
39
|
+
expect(HARNESS_RUNTIME_PROFILES.muse.matches("/Users/dev/.local/bin/muse-bin-1.4.4-R5419.1")).toBe(true);
|
|
40
|
+
expect(HARNESS_RUNTIME_PROFILES.muse.matches("/usr/local/bin/muse")).toBe(true);
|
|
41
|
+
expect(runtimeAgentMatchesHarness("muse", "muse")).toBe(true);
|
|
42
|
+
});
|
|
31
43
|
});
|
|
32
44
|
describe("harness matchers with executables under paths containing spaces", ()=>{
|
|
33
45
|
let root;
|