@ai-devkit/agent-manager 0.39.0 → 0.41.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.
Files changed (61) hide show
  1. package/dist/AgentManager.d.ts +12 -0
  2. package/dist/AgentManager.d.ts.map +1 -1
  3. package/dist/AgentManager.js +20 -1
  4. package/dist/AgentManager.js.map +1 -1
  5. package/dist/__tests__/AgentManager.test.js +111 -0
  6. package/dist/__tests__/AgentManager.test.js.map +1 -1
  7. package/dist/__tests__/capacity/devin.test.js +239 -0
  8. package/dist/__tests__/capacity/devin.test.js.map +1 -0
  9. package/dist/__tests__/fixtures.test.js +218 -0
  10. package/dist/__tests__/fixtures.test.js.map +1 -0
  11. package/dist/__tests__/readiness-fixtures.test.js +54 -0
  12. package/dist/__tests__/readiness-fixtures.test.js.map +1 -0
  13. package/dist/capacity/index.d.ts +3 -0
  14. package/dist/capacity/index.d.ts.map +1 -1
  15. package/dist/capacity/index.js +8 -0
  16. package/dist/capacity/index.js.map +1 -1
  17. package/dist/capacity/providers/devin.d.ts +13 -0
  18. package/dist/capacity/providers/devin.d.ts.map +1 -0
  19. package/dist/capacity/providers/devin.js +92 -0
  20. package/dist/capacity/providers/devin.js.map +1 -0
  21. package/dist/capacity/sources/devin.d.ts +12 -0
  22. package/dist/capacity/sources/devin.d.ts.map +1 -0
  23. package/dist/capacity/sources/devin.js +29 -0
  24. package/dist/capacity/sources/devin.js.map +1 -0
  25. package/dist/fixtures/bundle.d.ts +67 -0
  26. package/dist/fixtures/bundle.d.ts.map +1 -0
  27. package/dist/fixtures/bundle.js +86 -0
  28. package/dist/fixtures/bundle.js.map +1 -0
  29. package/dist/fixtures/capture.d.ts +11 -0
  30. package/dist/fixtures/capture.d.ts.map +1 -0
  31. package/dist/fixtures/capture.js +317 -0
  32. package/dist/fixtures/capture.js.map +1 -0
  33. package/dist/harnesses/devin/credentials.d.ts +10 -0
  34. package/dist/harnesses/devin/credentials.d.ts.map +1 -0
  35. package/dist/harnesses/devin/credentials.js +66 -0
  36. package/dist/harnesses/devin/credentials.js.map +1 -0
  37. package/dist/harnesses/shared.d.ts.map +1 -1
  38. package/dist/harnesses/shared.js +1 -1
  39. package/dist/harnesses/shared.js.map +1 -1
  40. package/dist/index.d.ts +1 -1
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +1 -1
  43. package/dist/index.js.map +1 -1
  44. package/package.json +1 -1
  45. package/src/AgentManager.ts +37 -1
  46. package/src/__tests__/AgentManager.test.ts +84 -0
  47. package/src/__tests__/capacity/devin.test.ts +210 -0
  48. package/src/__tests__/capacity/fixtures/devin-config.json +6 -0
  49. package/src/__tests__/capacity/fixtures/devin-credentials.toml +4 -0
  50. package/src/__tests__/capacity/fixtures/devin-quota-nonplan.json +5 -0
  51. package/src/__tests__/capacity/fixtures/devin-quota.json +10 -0
  52. package/src/__tests__/fixtures.test.ts +247 -0
  53. package/src/__tests__/readiness-fixtures.test.ts +79 -0
  54. package/src/capacity/index.ts +12 -0
  55. package/src/capacity/providers/devin.ts +121 -0
  56. package/src/capacity/sources/devin.ts +49 -0
  57. package/src/fixtures/bundle.ts +130 -0
  58. package/src/fixtures/capture.ts +349 -0
  59. package/src/harnesses/devin/credentials.ts +88 -0
  60. package/src/harnesses/shared.ts +1 -1
  61. package/src/index.ts +1 -0
@@ -0,0 +1,317 @@
1
+ /**
2
+ * Capture a live fixture bundle for an adapter: snapshot the real process
3
+ * list, run detectAgents with a frozen clock, and record expected output
4
+ * plus the session files the detection touched.
5
+ *
6
+ * Run via `AI_DEVKIT_FIXTURE_CAPTURE=1 npx vitest run fixtures.capture`.
7
+ */ import * as fs from "node:fs";
8
+ import * as os from "node:os";
9
+ import * as path from "node:path";
10
+ import { fileURLToPath } from "node:url";
11
+ import Database from "better-sqlite3";
12
+ import { AgentRegistry } from "../utils/AgentRegistry.js";
13
+ import { captureProcessSnapshot } from "../utils/process.js";
14
+ import { normalizeAgents, sanitize, withFrozenClock } from "./bundle.js";
15
+ const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../../../..");
16
+ export const FIXTURES_ROOT = path.join(repoRoot, "fixtures", "harness");
17
+ /** Session-file trees each harness consults during detection. */ const HARNESS_DIRS = {
18
+ claude: [
19
+ ".claude/sessions",
20
+ ".claude/projects"
21
+ ],
22
+ codex: [
23
+ ".codex/sessions",
24
+ ".codex/archived_sessions",
25
+ ".codex/ai-devkit"
26
+ ],
27
+ pi: [
28
+ ".pi/agent/sessions"
29
+ ],
30
+ // tmp/<shortId>/{.project_root,chats/*} — small tree; index-style capture
31
+ // keeps the ownership markers replay needs for slug-dir attribution.
32
+ gemini_cli: [
33
+ ".gemini/tmp"
34
+ ],
35
+ // session-state/<id>/{inuse.*.lock,events.jsonl,workspace.yaml} — the lock
36
+ // files drive attribution, so the whole tree must materialize on replay.
37
+ copilot: [
38
+ ".copilot/session-state"
39
+ ],
40
+ grok_cli: [
41
+ ".grok/sessions"
42
+ ],
43
+ // session_locks/<slug>.lock — pid-holder files read on every detect.
44
+ devin: [
45
+ ".local/share/devin/cli/session_locks"
46
+ ],
47
+ // sessions/cli/<id>.{lock,json,jsonl} — locks drive attribution; the
48
+ // whole flat dir is lightweight enough for index-style capture.
49
+ kiro: [
50
+ ".kiro/sessions/cli"
51
+ ],
52
+ // antigravity-cli/{cache,brain} — last_conversations.json (small .json
53
+ // aux) plus only the transcripts referenced by matched agents; the
54
+ // brain tree is tens of MB so it stays referenced-only (not index).
55
+ antigravity_cli: [
56
+ ".gemini/antigravity-cli"
57
+ ]
58
+ };
59
+ /**
60
+ * Dirs captured wholesale (lock/index trees where every file matters —
61
+ * copilot lock dirs, kiro/devin locks, the codex mapping dir, the gemini
62
+ * tmp tree). Everything else captures agent-referenced session files
63
+ * plus small .json aux files.
64
+ */ const HARNESS_INDEX_DIRS = new Set([
65
+ ".gemini/tmp",
66
+ ".copilot/session-state",
67
+ ".codex/ai-devkit",
68
+ ".local/share/devin/cli/session_locks",
69
+ ".kiro/sessions/cli"
70
+ ]);
71
+ /** Individual files (not dirs) each harness consults during detection. */ const HARNESS_FILES = {
72
+ pi: [
73
+ ".pi/agent/sessions.json"
74
+ ],
75
+ grok_cli: [
76
+ ".grok/active_sessions.json"
77
+ ]
78
+ };
79
+ /**
80
+ * Copy the harness's session dirs into `home` — only files the detection
81
+ * actually touched (referenced sessionFilePath) plus index/pid files.
82
+ * Transcript trees can be large; we include full files only for sessions
83
+ * that produced an agent, plus every file in lightweight index dirs.
84
+ */ function collectHomeFiles(adapterType, realHome, agents) {
85
+ const home = {};
86
+ const mtimes = {};
87
+ const referenced = new Set(agents.map((a)=>a.sessionFilePath).filter((p)=>Boolean(p)));
88
+ const put = (rel, file)=>{
89
+ home[rel] = sanitize(fs.readFileSync(file, "utf8"), realHome);
90
+ // Real mtimes are captured too — adapters that read them (Grok
91
+ // lastActive, latest-session pick) replay deterministically.
92
+ mtimes[rel] = fs.statSync(file).mtimeMs;
93
+ };
94
+ for (const relDir of HARNESS_DIRS[adapterType] ?? []){
95
+ const dir = path.join(realHome, relDir);
96
+ if (!fs.existsSync(dir)) continue;
97
+ // Session trees can hold months of transcripts; files that produced no
98
+ // agent cannot influence replayed output, so only agent-referenced
99
+ // session files plus small aux/index files are copied.
100
+ const isIndexDir = HARNESS_INDEX_DIRS.has(relDir);
101
+ walk(dir, (file)=>{
102
+ const rel = path.join(relDir, path.relative(dir, file));
103
+ const isAuxIndex = rel.endsWith("sessions.json") || rel.endsWith(".json");
104
+ if (isIndexDir || referenced.has(file) || isAuxIndex && fileSize(file) < 64 * 1024) {
105
+ put(rel, file);
106
+ }
107
+ });
108
+ }
109
+ for (const relFile of HARNESS_FILES[adapterType] ?? []){
110
+ const file = path.join(realHome, relFile);
111
+ if (fs.existsSync(file) && fileSize(file) < 64 * 1024) {
112
+ put(relFile, file);
113
+ }
114
+ }
115
+ return {
116
+ home,
117
+ mtimes
118
+ };
119
+ }
120
+ /**
121
+ * SQLite stores adapters consult (OpenCode's opencode.db). Sessions are
122
+ * reachable only through captured processes' cwds, so the dump keeps the
123
+ * table DDL plus every session row for those directories and their
124
+ * message/part rows — replay answers identical queries against the same
125
+ * view. Rows are serialized as executable SQL (binary dbs can't travel in
126
+ * the JSON `home` map); the key is the canonical `~/.local/share` relpath,
127
+ * which replay pins via XDG_DATA_HOME.
128
+ */ function collectSqliteDumps(adapterType, realHome, processes) {
129
+ const share = process.env.XDG_DATA_HOME || path.join(realHome, ".local", "share");
130
+ if (adapterType === "opencode") {
131
+ return dumpSqliteDb(path.join(share, "opencode", "opencode.db"), ".local/share/opencode/opencode.db", realHome, (dump)=>{
132
+ const dirs = uniqueCwds(processes);
133
+ if (dirs.length === 0) return;
134
+ const sessions = dump.rows("session", `WHERE directory IN ${dump.inList(dirs)} ORDER BY rowid`, dirs);
135
+ const sids = sessions.map((s)=>s.id);
136
+ dump.insertRows("session", sessions);
137
+ if (sids.length === 0) return;
138
+ dump.insertRows("message", dump.rows("message", `WHERE session_id IN ${dump.inList(sids)} ORDER BY rowid`, sids));
139
+ dump.insertRows("part", dump.rows("part", `WHERE session_id IN ${dump.inList(sids)} ORDER BY rowid`, sids));
140
+ }, [
141
+ "session",
142
+ "message",
143
+ "part"
144
+ ]);
145
+ }
146
+ if (adapterType === "devin") {
147
+ const dbPath = path.join(share, "devin", "cli", "sessions.db");
148
+ // Locks are captured separately as text files; slugs here widen the
149
+ // session row set so lock-slug lookups resolve identically on replay.
150
+ const lockSids = fs.existsSync(path.join(path.dirname(dbPath), "session_locks")) ? fs.readdirSync(path.join(path.dirname(dbPath), "session_locks")).filter((f)=>f.endsWith(".lock")).map((f)=>f.slice(0, -".lock".length)).filter(Boolean) : [];
151
+ return dumpSqliteDb(dbPath, ".local/share/devin/cli/sessions.db", realHome, (dump)=>{
152
+ const dirs = uniqueCwds(processes);
153
+ const clauses = [];
154
+ const args = [];
155
+ if (dirs.length) {
156
+ clauses.push(`working_directory IN ${dump.inList(dirs)}`);
157
+ args.push(...dirs);
158
+ }
159
+ if (lockSids.length) {
160
+ clauses.push(`id IN ${dump.inList(lockSids)}`);
161
+ args.push(...lockSids);
162
+ }
163
+ const sessions = clauses.length ? dump.rows("sessions", `WHERE ${clauses.join(" OR ")} ORDER BY rowid`, args) : [];
164
+ const sids = sessions.map((s)=>s.id);
165
+ dump.insertRows("sessions", sessions);
166
+ if (sids.length === 0) return;
167
+ // Only the rows getSessionStats can reach: the frontier node, the
168
+ // MAX(created_at) node, the 8 newest role='user' nodes, and the
169
+ // newest non-shell non-slash prompt — identical query results on
170
+ // replay without hauling full transcripts into the bundle.
171
+ for (const sid of sids){
172
+ const reachable = [
173
+ ...dump.rows("message_nodes", "WHERE session_id = ? ORDER BY node_id DESC LIMIT 1", [
174
+ sid
175
+ ]),
176
+ // The newest-created_at row — `MAX(created_at)` reads only the
177
+ // value, and `= MAX(...)` unbounded explodes when batch-written
178
+ // nodes share a timestamp.
179
+ ...dump.rows("message_nodes", "WHERE session_id = ? ORDER BY created_at DESC, node_id DESC LIMIT 1", [
180
+ sid
181
+ ]),
182
+ ...dump.rows("message_nodes", "WHERE session_id = ? AND json_extract(chat_message, '$.role') = 'user' ORDER BY node_id DESC LIMIT 8", [
183
+ sid
184
+ ])
185
+ ];
186
+ const seen = new Set();
187
+ dump.insertRows("message_nodes", reachable.filter((r)=>seen.has(r.node_id) ? false : (seen.add(r.node_id), true)));
188
+ dump.insertRows("prompt_history", dump.rows("prompt_history", "WHERE session_id = ? AND is_shell = 0 AND content NOT LIKE '/%' ORDER BY timestamp DESC LIMIT 1", [
189
+ sid
190
+ ]));
191
+ }
192
+ }, [
193
+ "sessions",
194
+ "message_nodes",
195
+ "prompt_history"
196
+ ]);
197
+ }
198
+ return {};
199
+ }
200
+ function uniqueCwds(processes) {
201
+ return [
202
+ ...new Set(processes.map((p)=>p.cwd).filter((c)=>Boolean(c)))
203
+ ];
204
+ }
205
+ /**
206
+ * Dump `dbPath`'s tables (DDL + caller-selected rows) as executable SQL,
207
+ * keyed under `relKey`. `fill` receives helpers that append INSERTs for
208
+ * the rows the adapter's detection can reach.
209
+ */ function dumpSqliteDb(dbPath, relKey, realHome, fill, tables) {
210
+ if (!fs.existsSync(dbPath)) return {};
211
+ const db = new Database(dbPath, {
212
+ readonly: true
213
+ });
214
+ try {
215
+ const stmts = db.prepare(`SELECT sql FROM sqlite_master WHERE type='table' AND name IN (${tables.map(()=>"?").join(",")})`).all(...tables).map((r)=>`${r.sql};`);
216
+ const dump = {
217
+ inList: (xs)=>`(${xs.map(()=>"?").join(",")})`,
218
+ rows: (table, clause, args)=>db.prepare(`SELECT * FROM "${table}" ${clause}`).all(...args),
219
+ insertRows: (table, rs)=>{
220
+ for (const r of rs){
221
+ stmts.push(`INSERT INTO "${table}" VALUES (${Object.values(r).map(sqlLit).join(",")});`);
222
+ }
223
+ }
224
+ };
225
+ fill(dump);
226
+ return {
227
+ [relKey]: stmts.map((s)=>sanitize(s, realHome))
228
+ };
229
+ } finally{
230
+ db.close();
231
+ }
232
+ }
233
+ function sqlLit(v) {
234
+ if (v === null || v === undefined) return "NULL";
235
+ if (typeof v === "number" || typeof v === "bigint") return String(v);
236
+ if (v instanceof Uint8Array) return `X'${Buffer.from(v).toString("hex")}'`;
237
+ return `'${String(v).split("'").join("''")}'`;
238
+ }
239
+ /**
240
+ * Registry rows keyed to captured pids — adapters' registry caches consult
241
+ * them during detection, so replay must see the same view (otherwise
242
+ * ordering/attribution diverges: cache hits precede locator matches).
243
+ */ function collectRegistryEntries(realHome, processes) {
244
+ const pids = new Set(processes.map((p)=>p.pid));
245
+ try {
246
+ return AgentRegistry.default().list().filter((e)=>pids.has(e.pid)).map((e)=>sanitize({
247
+ ...e
248
+ }, realHome));
249
+ } catch {
250
+ return [];
251
+ }
252
+ }
253
+ function fileSize(file) {
254
+ try {
255
+ return fs.statSync(file).size;
256
+ } catch {
257
+ return Infinity;
258
+ }
259
+ }
260
+ function walk(dir, visit, depth = 0) {
261
+ if (depth > 4) return;
262
+ for (const entry of fs.readdirSync(dir, {
263
+ withFileTypes: true
264
+ })){
265
+ const p = path.join(dir, entry.name);
266
+ if (entry.isDirectory()) walk(p, visit, depth + 1);
267
+ else if (entry.isFile()) visit(p);
268
+ }
269
+ }
270
+ export async function captureLive(adapter, caseName = "live") {
271
+ const realHome = os.homedir();
272
+ const frozenNow = Date.now();
273
+ const names = adapter.processNames;
274
+ if (!names?.length) {
275
+ throw new Error(`adapter ${adapter.type} has no processNames — can't snapshot`);
276
+ }
277
+ const { processes, agents, registry } = await withFrozenClock(frozenNow, async ()=>{
278
+ const snapshot = await captureProcessSnapshot([
279
+ ...names
280
+ ], {
281
+ isCandidate: (p)=>adapter.canHandle(p)
282
+ });
283
+ // Registry read must precede detection — detectAgents persists its own
284
+ // results, which would poison the captured view.
285
+ const registryBefore = collectRegistryEntries(realHome, snapshot);
286
+ const detected = await adapter.detectAgents({
287
+ processes: snapshot
288
+ });
289
+ return {
290
+ processes: snapshot,
291
+ agents: detected,
292
+ registry: registryBefore
293
+ };
294
+ });
295
+ const { home, mtimes } = collectHomeFiles(adapter.type, realHome, agents);
296
+ const sqlite = collectSqliteDumps(adapter.type, realHome, processes);
297
+ const bundle = {
298
+ adapter: adapter.type,
299
+ capturedAt: new Date(frozenNow).toISOString(),
300
+ frozenNow,
301
+ processes: sanitize(processes, realHome),
302
+ home,
303
+ mtimes,
304
+ registry,
305
+ sqlite: Object.keys(sqlite).length > 0 ? sqlite : undefined,
306
+ expected: normalizeAgents(agents, realHome)
307
+ };
308
+ const dir = path.join(FIXTURES_ROOT, adapter.type);
309
+ fs.mkdirSync(dir, {
310
+ recursive: true
311
+ });
312
+ const out = path.join(dir, `${caseName}.json`);
313
+ fs.writeFileSync(out, JSON.stringify(bundle, null, 2));
314
+ return out;
315
+ }
316
+
317
+ //# sourceMappingURL=capture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/fixtures/capture.ts"],"sourcesContent":["/**\n * Capture a live fixture bundle for an adapter: snapshot the real process\n * list, run detectAgents with a frozen clock, and record expected output\n * plus the session files the detection touched.\n *\n * Run via `AI_DEVKIT_FIXTURE_CAPTURE=1 npx vitest run fixtures.capture`.\n */\n\nimport * as fs from \"node:fs\";\nimport * as os from \"node:os\";\nimport * as path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport Database from \"better-sqlite3\";\nimport type { AgentAdapter, AgentInfo } from \"../adapters/AgentAdapter.js\";\nimport { AgentRegistry } from \"../utils/AgentRegistry.js\";\nimport { captureProcessSnapshot } from \"../utils/process.js\";\nimport { FixtureBundle, normalizeAgents, sanitize, withFrozenClock } from \"./bundle.js\";\n\nconst repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), \"../../../..\");\nexport const FIXTURES_ROOT = path.join(repoRoot, \"fixtures\", \"harness\");\n\n/** Session-file trees each harness consults during detection. */\nconst HARNESS_DIRS: Record<string, string[]> = {\n claude: [\".claude/sessions\", \".claude/projects\"],\n codex: [\".codex/sessions\", \".codex/archived_sessions\", \".codex/ai-devkit\"],\n pi: [\".pi/agent/sessions\"],\n // tmp/<shortId>/{.project_root,chats/*} — small tree; index-style capture\n // keeps the ownership markers replay needs for slug-dir attribution.\n gemini_cli: [\".gemini/tmp\"],\n // session-state/<id>/{inuse.*.lock,events.jsonl,workspace.yaml} — the lock\n // files drive attribution, so the whole tree must materialize on replay.\n copilot: [\".copilot/session-state\"],\n grok_cli: [\".grok/sessions\"],\n // session_locks/<slug>.lock — pid-holder files read on every detect.\n devin: [\".local/share/devin/cli/session_locks\"],\n // sessions/cli/<id>.{lock,json,jsonl} — locks drive attribution; the\n // whole flat dir is lightweight enough for index-style capture.\n kiro: [\".kiro/sessions/cli\"],\n // antigravity-cli/{cache,brain} — last_conversations.json (small .json\n // aux) plus only the transcripts referenced by matched agents; the\n // brain tree is tens of MB so it stays referenced-only (not index).\n antigravity_cli: [\".gemini/antigravity-cli\"],\n};\n\n/**\n * Dirs captured wholesale (lock/index trees where every file matters —\n * copilot lock dirs, kiro/devin locks, the codex mapping dir, the gemini\n * tmp tree). Everything else captures agent-referenced session files\n * plus small .json aux files.\n */\nconst HARNESS_INDEX_DIRS = new Set([\n \".gemini/tmp\",\n \".copilot/session-state\",\n \".codex/ai-devkit\",\n \".local/share/devin/cli/session_locks\",\n \".kiro/sessions/cli\",\n]);\n\n/** Individual files (not dirs) each harness consults during detection. */\nconst HARNESS_FILES: Record<string, string[]> = {\n pi: [\".pi/agent/sessions.json\"],\n grok_cli: [\".grok/active_sessions.json\"],\n};\n\n/**\n * Copy the harness's session dirs into `home` — only files the detection\n * actually touched (referenced sessionFilePath) plus index/pid files.\n * Transcript trees can be large; we include full files only for sessions\n * that produced an agent, plus every file in lightweight index dirs.\n */\nfunction collectHomeFiles(\n adapterType: string,\n realHome: string,\n agents: AgentInfo[],\n): { home: Record<string, string>; mtimes: Record<string, number> } {\n const home: Record<string, string> = {};\n const mtimes: Record<string, number> = {};\n const referenced = new Set(\n agents.map((a) => a.sessionFilePath).filter((p): p is string => Boolean(p)),\n );\n\n const put = (rel: string, file: string) => {\n home[rel] = sanitize(fs.readFileSync(file, \"utf8\"), realHome);\n // Real mtimes are captured too — adapters that read them (Grok\n // lastActive, latest-session pick) replay deterministically.\n mtimes[rel] = fs.statSync(file).mtimeMs;\n };\n\n for (const relDir of HARNESS_DIRS[adapterType] ?? []) {\n const dir = path.join(realHome, relDir);\n if (!fs.existsSync(dir)) continue;\n // Session trees can hold months of transcripts; files that produced no\n // agent cannot influence replayed output, so only agent-referenced\n // session files plus small aux/index files are copied.\n const isIndexDir = HARNESS_INDEX_DIRS.has(relDir);\n walk(dir, (file) => {\n const rel = path.join(relDir, path.relative(dir, file));\n const isAuxIndex = rel.endsWith(\"sessions.json\") || rel.endsWith(\".json\");\n if (isIndexDir || referenced.has(file) || (isAuxIndex && fileSize(file) < 64 * 1024)) {\n put(rel, file);\n }\n });\n }\n for (const relFile of HARNESS_FILES[adapterType] ?? []) {\n const file = path.join(realHome, relFile);\n if (fs.existsSync(file) && fileSize(file) < 64 * 1024) {\n put(relFile, file);\n }\n }\n return { home, mtimes };\n}\n\n/**\n * SQLite stores adapters consult (OpenCode's opencode.db). Sessions are\n * reachable only through captured processes' cwds, so the dump keeps the\n * table DDL plus every session row for those directories and their\n * message/part rows — replay answers identical queries against the same\n * view. Rows are serialized as executable SQL (binary dbs can't travel in\n * the JSON `home` map); the key is the canonical `~/.local/share` relpath,\n * which replay pins via XDG_DATA_HOME.\n */\nfunction collectSqliteDumps(\n adapterType: string,\n realHome: string,\n processes: { cwd?: string | null }[],\n): Record<string, string[]> {\n const share = process.env.XDG_DATA_HOME || path.join(realHome, \".local\", \"share\");\n if (adapterType === \"opencode\") {\n return dumpSqliteDb(\n path.join(share, \"opencode\", \"opencode.db\"),\n \".local/share/opencode/opencode.db\",\n realHome,\n (dump) => {\n const dirs = uniqueCwds(processes);\n if (dirs.length === 0) return;\n const sessions = dump.rows(\"session\", `WHERE directory IN ${dump.inList(dirs)} ORDER BY rowid`, dirs);\n const sids = sessions.map((s) => s.id);\n dump.insertRows(\"session\", sessions);\n if (sids.length === 0) return;\n dump.insertRows(\"message\", dump.rows(\"message\", `WHERE session_id IN ${dump.inList(sids)} ORDER BY rowid`, sids));\n dump.insertRows(\"part\", dump.rows(\"part\", `WHERE session_id IN ${dump.inList(sids)} ORDER BY rowid`, sids));\n },\n [\"session\", \"message\", \"part\"],\n );\n }\n if (adapterType === \"devin\") {\n const dbPath = path.join(share, \"devin\", \"cli\", \"sessions.db\");\n // Locks are captured separately as text files; slugs here widen the\n // session row set so lock-slug lookups resolve identically on replay.\n const lockSids = fs.existsSync(path.join(path.dirname(dbPath), \"session_locks\"))\n ? fs\n .readdirSync(path.join(path.dirname(dbPath), \"session_locks\"))\n .filter((f) => f.endsWith(\".lock\"))\n .map((f) => f.slice(0, -\".lock\".length))\n .filter(Boolean)\n : [];\n return dumpSqliteDb(\n dbPath,\n \".local/share/devin/cli/sessions.db\",\n realHome,\n (dump) => {\n const dirs = uniqueCwds(processes);\n const clauses: string[] = [];\n const args: unknown[] = [];\n if (dirs.length) {\n clauses.push(`working_directory IN ${dump.inList(dirs)}`);\n args.push(...dirs);\n }\n if (lockSids.length) {\n clauses.push(`id IN ${dump.inList(lockSids)}`);\n args.push(...lockSids);\n }\n const sessions = clauses.length ? dump.rows(\"sessions\", `WHERE ${clauses.join(\" OR \")} ORDER BY rowid`, args) : [];\n const sids = sessions.map((s) => s.id);\n dump.insertRows(\"sessions\", sessions);\n if (sids.length === 0) return;\n // Only the rows getSessionStats can reach: the frontier node, the\n // MAX(created_at) node, the 8 newest role='user' nodes, and the\n // newest non-shell non-slash prompt — identical query results on\n // replay without hauling full transcripts into the bundle.\n for (const sid of sids) {\n const reachable = [\n ...dump.rows(\"message_nodes\", \"WHERE session_id = ? ORDER BY node_id DESC LIMIT 1\", [sid]),\n // The newest-created_at row — `MAX(created_at)` reads only the\n // value, and `= MAX(...)` unbounded explodes when batch-written\n // nodes share a timestamp.\n ...dump.rows(\"message_nodes\", \"WHERE session_id = ? ORDER BY created_at DESC, node_id DESC LIMIT 1\", [sid]),\n ...dump.rows(\n \"message_nodes\",\n \"WHERE session_id = ? AND json_extract(chat_message, '$.role') = 'user' ORDER BY node_id DESC LIMIT 8\",\n [sid],\n ),\n ];\n const seen = new Set<unknown>();\n dump.insertRows(\n \"message_nodes\",\n reachable.filter((r) => (seen.has(r.node_id) ? false : (seen.add(r.node_id), true))),\n );\n dump.insertRows(\n \"prompt_history\",\n dump.rows(\n \"prompt_history\",\n \"WHERE session_id = ? AND is_shell = 0 AND content NOT LIKE '/%' ORDER BY timestamp DESC LIMIT 1\",\n [sid],\n ),\n );\n }\n },\n [\"sessions\", \"message_nodes\", \"prompt_history\"],\n );\n }\n return {};\n}\n\nfunction uniqueCwds(processes: { cwd?: string | null }[]): string[] {\n return [...new Set(processes.map((p) => p.cwd).filter((c): c is string => Boolean(c)))];\n}\n\n/**\n * Dump `dbPath`'s tables (DDL + caller-selected rows) as executable SQL,\n * keyed under `relKey`. `fill` receives helpers that append INSERTs for\n * the rows the adapter's detection can reach.\n */\nfunction dumpSqliteDb(\n dbPath: string,\n relKey: string,\n realHome: string,\n fill: (dump: {\n inList: (xs: unknown[]) => string;\n rows: (table: string, where: string, args: unknown[]) => Record<string, unknown>[];\n insertRows: (table: string, rs: Record<string, unknown>[]) => void;\n }) => void,\n tables: string[],\n): Record<string, string[]> {\n if (!fs.existsSync(dbPath)) return {};\n const db = new Database(dbPath, { readonly: true });\n try {\n const stmts = (\n db\n .prepare(\n `SELECT sql FROM sqlite_master WHERE type='table' AND name IN (${tables.map(() => \"?\").join(\",\")})`,\n )\n .all(...tables) as { sql: string }[]\n ).map((r) => `${r.sql};`);\n const dump = {\n inList: (xs: unknown[]) => `(${xs.map(() => \"?\").join(\",\")})`,\n rows: (table: string, clause: string, args: unknown[]) =>\n db.prepare(`SELECT * FROM \"${table}\" ${clause}`).all(...args) as Record<\n string,\n unknown\n >[],\n insertRows: (table: string, rs: Record<string, unknown>[]) => {\n for (const r of rs) {\n stmts.push(\n `INSERT INTO \"${table}\" VALUES (${Object.values(r).map(sqlLit).join(\",\")});`,\n );\n }\n },\n };\n fill(dump);\n return { [relKey]: stmts.map((s) => sanitize(s, realHome)) };\n } finally {\n db.close();\n }\n}\n\nfunction sqlLit(v: unknown): string {\n if (v === null || v === undefined) return \"NULL\";\n if (typeof v === \"number\" || typeof v === \"bigint\") return String(v);\n if (v instanceof Uint8Array) return `X'${Buffer.from(v).toString(\"hex\")}'`;\n return `'${String(v).split(\"'\").join(\"''\")}'`;\n}\n\n/**\n * Registry rows keyed to captured pids — adapters' registry caches consult\n * them during detection, so replay must see the same view (otherwise\n * ordering/attribution diverges: cache hits precede locator matches).\n */\nfunction collectRegistryEntries(\n realHome: string,\n processes: { pid: number }[],\n): Record<string, unknown>[] {\n const pids = new Set(processes.map((p) => p.pid));\n try {\n return AgentRegistry.default()\n .list()\n .filter((e) => pids.has(e.pid))\n .map((e) => sanitize({ ...e }, realHome) as Record<string, unknown>);\n } catch {\n return [];\n }\n}\n\nfunction fileSize(file: string): number {\n try {\n return fs.statSync(file).size;\n } catch {\n return Infinity;\n }\n}\n\nfunction walk(dir: string, visit: (file: string) => void, depth = 0) {\n if (depth > 4) return;\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const p = path.join(dir, entry.name);\n if (entry.isDirectory()) walk(p, visit, depth + 1);\n else if (entry.isFile()) visit(p);\n }\n}\n\nexport async function captureLive(adapter: AgentAdapter, caseName = \"live\"): Promise<string> {\n const realHome = os.homedir();\n const frozenNow = Date.now();\n const names = adapter.processNames;\n if (!names?.length) {\n throw new Error(`adapter ${adapter.type} has no processNames — can't snapshot`);\n }\n\n const { processes, agents, registry } = await withFrozenClock(frozenNow, async () => {\n const snapshot = await captureProcessSnapshot([...names], {\n isCandidate: (p) => adapter.canHandle(p),\n });\n // Registry read must precede detection — detectAgents persists its own\n // results, which would poison the captured view.\n const registryBefore = collectRegistryEntries(realHome, snapshot);\n const detected = await adapter.detectAgents({ processes: snapshot });\n return { processes: snapshot, agents: detected, registry: registryBefore };\n });\n\n const { home, mtimes } = collectHomeFiles(adapter.type, realHome, agents);\n const sqlite = collectSqliteDumps(adapter.type, realHome, processes);\n const bundle: FixtureBundle = {\n adapter: adapter.type,\n capturedAt: new Date(frozenNow).toISOString(),\n frozenNow,\n processes: sanitize(processes, realHome),\n home,\n mtimes,\n registry,\n sqlite: Object.keys(sqlite).length > 0 ? sqlite : undefined,\n expected: normalizeAgents(agents, realHome),\n };\n\n const dir = path.join(FIXTURES_ROOT, adapter.type);\n fs.mkdirSync(dir, { recursive: true });\n const out = path.join(dir, `${caseName}.json`);\n fs.writeFileSync(out, JSON.stringify(bundle, null, 2));\n return out;\n}\n"],"names":["fs","os","path","fileURLToPath","Database","AgentRegistry","captureProcessSnapshot","normalizeAgents","sanitize","withFrozenClock","repoRoot","resolve","dirname","url","FIXTURES_ROOT","join","HARNESS_DIRS","claude","codex","pi","gemini_cli","copilot","grok_cli","devin","kiro","antigravity_cli","HARNESS_INDEX_DIRS","Set","HARNESS_FILES","collectHomeFiles","adapterType","realHome","agents","home","mtimes","referenced","map","a","sessionFilePath","filter","p","Boolean","put","rel","file","readFileSync","statSync","mtimeMs","relDir","dir","existsSync","isIndexDir","has","walk","relative","isAuxIndex","endsWith","fileSize","relFile","collectSqliteDumps","processes","share","process","env","XDG_DATA_HOME","dumpSqliteDb","dump","dirs","uniqueCwds","length","sessions","rows","inList","sids","s","id","insertRows","dbPath","lockSids","readdirSync","f","slice","clauses","args","push","sid","reachable","seen","r","node_id","add","cwd","c","relKey","fill","tables","db","readonly","stmts","prepare","all","sql","xs","table","clause","rs","Object","values","sqlLit","close","v","undefined","String","Uint8Array","Buffer","from","toString","split","collectRegistryEntries","pids","pid","default","list","e","size","Infinity","visit","depth","entry","withFileTypes","name","isDirectory","isFile","captureLive","adapter","caseName","homedir","frozenNow","Date","now","names","processNames","Error","type","registry","snapshot","isCandidate","canHandle","registryBefore","detected","detectAgents","sqlite","bundle","capturedAt","toISOString","keys","expected","mkdirSync","recursive","out","writeFileSync","JSON","stringify"],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export type DevinCredential = {
2
+ key: string;
3
+ orgId: string;
4
+ };
5
+ export type DevinCredentialOptions = {
6
+ env?: NodeJS.ProcessEnv;
7
+ readFile?: (path: string, encoding: BufferEncoding) => Promise<string>;
8
+ };
9
+ export declare function resolveDevinCredential(options?: DevinCredentialOptions): Promise<DevinCredential>;
10
+ //# sourceMappingURL=credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../../../src/harnesses/devin/credentials.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACxE,CAAC;AAkEF,wBAAsB,sBAAsB,CAC1C,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,eAAe,CAAC,CAK1B"}
@@ -0,0 +1,66 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ const DEVIN_KEY_MISSING = "Devin API key not found; set DEVIN_API_KEY or log in with the Devin CLI";
5
+ const DEVIN_ORG_MISSING = "Devin organization id not found; set DEVIN_ORG_ID or log in with the Devin CLI";
6
+ function record(value) {
7
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : null;
8
+ }
9
+ function nonEmptyText(value) {
10
+ return typeof value === "string" && value.trim() ? value.trim() : null;
11
+ }
12
+ async function readText(path, options) {
13
+ try {
14
+ return await (options.readFile ?? readFile)(path, "utf8");
15
+ } catch (error) {
16
+ if (record(error)?.code === "ENOENT") return null;
17
+ throw error;
18
+ }
19
+ }
20
+ function parseFlatToml(contents) {
21
+ const entries = {};
22
+ for (const line of contents.split("\n")){
23
+ const trimmed = line.trim();
24
+ if (!trimmed || trimmed.startsWith("#") || trimmed.startsWith("[")) continue;
25
+ const match = /^([A-Za-z0-9_]+)\s*=\s*"(.*)"\s*$/.exec(trimmed);
26
+ if (!match) return null;
27
+ entries[match[1]] = match[2];
28
+ }
29
+ return entries;
30
+ }
31
+ async function resolveKey(home, options) {
32
+ const envKey = nonEmptyText(options.env?.DEVIN_API_KEY ?? process.env.DEVIN_API_KEY);
33
+ if (envKey) return envKey;
34
+ const contents = await readText(join(home, ".local", "share", "devin", "credentials.toml"), options);
35
+ if (contents === null) throw new Error(DEVIN_KEY_MISSING);
36
+ const parsed = parseFlatToml(contents);
37
+ if (parsed === null) throw new Error("Devin credentials file is malformed");
38
+ const key = nonEmptyText(parsed.windsurf_api_key);
39
+ if (!key) throw new Error(DEVIN_KEY_MISSING);
40
+ return key;
41
+ }
42
+ async function resolveOrgId(home, options) {
43
+ const envOrg = nonEmptyText(options.env?.DEVIN_ORG_ID ?? process.env.DEVIN_ORG_ID);
44
+ if (envOrg) return envOrg;
45
+ const contents = await readText(join(home, ".config", "devin", "config.json"), options);
46
+ if (contents === null) throw new Error(DEVIN_ORG_MISSING);
47
+ const parsed = record(JSON.parse(contents));
48
+ if (!parsed) throw new Error("Devin config file is malformed");
49
+ const orgId = nonEmptyText(record(parsed.devin)?.org_id);
50
+ if (!orgId) throw new Error(DEVIN_ORG_MISSING);
51
+ return orgId;
52
+ }
53
+ export async function resolveDevinCredential(options = {}) {
54
+ const env = options.env ?? process.env;
55
+ const home = env.HOME || homedir();
56
+ const [key, orgId] = await Promise.all([
57
+ resolveKey(home, options),
58
+ resolveOrgId(home, options)
59
+ ]);
60
+ return {
61
+ key,
62
+ orgId
63
+ };
64
+ }
65
+
66
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/harnesses/devin/credentials.ts"],"sourcesContent":["import { readFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\ntype UnknownRecord = Record<string, unknown>;\n\nexport type DevinCredential = {\n key: string;\n orgId: string;\n};\n\nexport type DevinCredentialOptions = {\n env?: NodeJS.ProcessEnv;\n readFile?: (path: string, encoding: BufferEncoding) => Promise<string>;\n};\n\nconst DEVIN_KEY_MISSING = \"Devin API key not found; set DEVIN_API_KEY or log in with the Devin CLI\";\nconst DEVIN_ORG_MISSING =\n \"Devin organization id not found; set DEVIN_ORG_ID or log in with the Devin CLI\";\n\nfunction record(value: unknown): UnknownRecord | null {\n return value !== null && typeof value === \"object\" && !Array.isArray(value)\n ? (value as UnknownRecord)\n : null;\n}\n\nfunction nonEmptyText(value: unknown): string | null {\n return typeof value === \"string\" && value.trim() ? value.trim() : null;\n}\n\nasync function readText(path: string, options: DevinCredentialOptions): Promise<string | null> {\n try {\n return await (options.readFile ?? readFile)(path, \"utf8\");\n } catch (error) {\n if (record(error)?.code === \"ENOENT\") return null;\n throw error;\n }\n}\n\nfunction parseFlatToml(contents: string): UnknownRecord | null {\n const entries: UnknownRecord = {};\n for (const line of contents.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith(\"#\") || trimmed.startsWith(\"[\")) continue;\n const match = /^([A-Za-z0-9_]+)\\s*=\\s*\"(.*)\"\\s*$/.exec(trimmed);\n if (!match) return null;\n entries[match[1]] = match[2];\n }\n return entries;\n}\n\nasync function resolveKey(home: string, options: DevinCredentialOptions): Promise<string> {\n const envKey = nonEmptyText(options.env?.DEVIN_API_KEY ?? process.env.DEVIN_API_KEY);\n if (envKey) return envKey;\n\n const contents = await readText(\n join(home, \".local\", \"share\", \"devin\", \"credentials.toml\"),\n options,\n );\n if (contents === null) throw new Error(DEVIN_KEY_MISSING);\n const parsed = parseFlatToml(contents);\n if (parsed === null) throw new Error(\"Devin credentials file is malformed\");\n const key = nonEmptyText(parsed.windsurf_api_key);\n if (!key) throw new Error(DEVIN_KEY_MISSING);\n return key;\n}\n\nasync function resolveOrgId(home: string, options: DevinCredentialOptions): Promise<string> {\n const envOrg = nonEmptyText(options.env?.DEVIN_ORG_ID ?? process.env.DEVIN_ORG_ID);\n if (envOrg) return envOrg;\n\n const contents = await readText(join(home, \".config\", \"devin\", \"config.json\"), options);\n if (contents === null) throw new Error(DEVIN_ORG_MISSING);\n const parsed = record(JSON.parse(contents));\n if (!parsed) throw new Error(\"Devin config file is malformed\");\n const orgId = nonEmptyText(record(parsed.devin)?.org_id);\n if (!orgId) throw new Error(DEVIN_ORG_MISSING);\n return orgId;\n}\n\nexport async function resolveDevinCredential(\n options: DevinCredentialOptions = {},\n): Promise<DevinCredential> {\n const env = options.env ?? process.env;\n const home = env.HOME || homedir();\n const [key, orgId] = await Promise.all([resolveKey(home, options), resolveOrgId(home, options)]);\n return { key, orgId };\n}\n"],"names":["readFile","homedir","join","DEVIN_KEY_MISSING","DEVIN_ORG_MISSING","record","value","Array","isArray","nonEmptyText","trim","readText","path","options","error","code","parseFlatToml","contents","entries","line","split","trimmed","startsWith","match","exec","resolveKey","home","envKey","env","DEVIN_API_KEY","process","Error","parsed","key","windsurf_api_key","resolveOrgId","envOrg","DEVIN_ORG_ID","JSON","parse","orgId","devin","org_id","resolveDevinCredential","HOME","Promise","all"],"mappings":"AAAA,SAASA,QAAQ,QAAQ,mBAAmB;AAC5C,SAASC,OAAO,QAAQ,UAAU;AAClC,SAASC,IAAI,QAAQ,YAAY;AAcjC,MAAMC,oBAAoB;AAC1B,MAAMC,oBACJ;AAEF,SAASC,OAAOC,KAAc;IAC5B,OAAOA,UAAU,QAAQ,OAAOA,UAAU,YAAY,CAACC,MAAMC,OAAO,CAACF,SAChEA,QACD;AACN;AAEA,SAASG,aAAaH,KAAc;IAClC,OAAO,OAAOA,UAAU,YAAYA,MAAMI,IAAI,KAAKJ,MAAMI,IAAI,KAAK;AACpE;AAEA,eAAeC,SAASC,IAAY,EAAEC,OAA+B;IACnE,IAAI;QACF,OAAO,MAAM,AAACA,CAAAA,QAAQb,QAAQ,IAAIA,QAAO,EAAGY,MAAM;IACpD,EAAE,OAAOE,OAAO;QACd,IAAIT,OAAOS,QAAQC,SAAS,UAAU,OAAO;QAC7C,MAAMD;IACR;AACF;AAEA,SAASE,cAAcC,QAAgB;IACrC,MAAMC,UAAyB,CAAC;IAChC,KAAK,MAAMC,QAAQF,SAASG,KAAK,CAAC,MAAO;QACvC,MAAMC,UAAUF,KAAKT,IAAI;QACzB,IAAI,CAACW,WAAWA,QAAQC,UAAU,CAAC,QAAQD,QAAQC,UAAU,CAAC,MAAM;QACpE,MAAMC,QAAQ,oCAAoCC,IAAI,CAACH;QACvD,IAAI,CAACE,OAAO,OAAO;QACnBL,OAAO,CAACK,KAAK,CAAC,EAAE,CAAC,GAAGA,KAAK,CAAC,EAAE;IAC9B;IACA,OAAOL;AACT;AAEA,eAAeO,WAAWC,IAAY,EAAEb,OAA+B;IACrE,MAAMc,SAASlB,aAAaI,QAAQe,GAAG,EAAEC,iBAAiBC,QAAQF,GAAG,CAACC,aAAa;IACnF,IAAIF,QAAQ,OAAOA;IAEnB,MAAMV,WAAW,MAAMN,SACrBT,KAAKwB,MAAM,UAAU,SAAS,SAAS,qBACvCb;IAEF,IAAII,aAAa,MAAM,MAAM,IAAIc,MAAM5B;IACvC,MAAM6B,SAAShB,cAAcC;IAC7B,IAAIe,WAAW,MAAM,MAAM,IAAID,MAAM;IACrC,MAAME,MAAMxB,aAAauB,OAAOE,gBAAgB;IAChD,IAAI,CAACD,KAAK,MAAM,IAAIF,MAAM5B;IAC1B,OAAO8B;AACT;AAEA,eAAeE,aAAaT,IAAY,EAAEb,OAA+B;IACvE,MAAMuB,SAAS3B,aAAaI,QAAQe,GAAG,EAAES,gBAAgBP,QAAQF,GAAG,CAACS,YAAY;IACjF,IAAID,QAAQ,OAAOA;IAEnB,MAAMnB,WAAW,MAAMN,SAAST,KAAKwB,MAAM,WAAW,SAAS,gBAAgBb;IAC/E,IAAII,aAAa,MAAM,MAAM,IAAIc,MAAM3B;IACvC,MAAM4B,SAAS3B,OAAOiC,KAAKC,KAAK,CAACtB;IACjC,IAAI,CAACe,QAAQ,MAAM,IAAID,MAAM;IAC7B,MAAMS,QAAQ/B,aAAaJ,OAAO2B,OAAOS,KAAK,GAAGC;IACjD,IAAI,CAACF,OAAO,MAAM,IAAIT,MAAM3B;IAC5B,OAAOoC;AACT;AAEA,OAAO,eAAeG,uBACpB9B,UAAkC,CAAC,CAAC;IAEpC,MAAMe,MAAMf,QAAQe,GAAG,IAAIE,QAAQF,GAAG;IACtC,MAAMF,OAAOE,IAAIgB,IAAI,IAAI3C;IACzB,MAAM,CAACgC,KAAKO,MAAM,GAAG,MAAMK,QAAQC,GAAG,CAAC;QAACrB,WAAWC,MAAMb;QAAUsB,aAAaT,MAAMb;KAAS;IAC/F,OAAO;QAAEoB;QAAKO;IAAM;AACtB"}
@@ -1 +1 @@
1
- {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/harnesses/shared.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,qBAAqB,EACrB,SAAS,EACT,SAAS,EACT,WAAW,EACZ,MAAM,6BAA6B,CAAC;AASrC,wDAAwD;AACxD,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC,MAAM,WAAW,gBAAgB;IAC/B,6EAA6E;IAC7E,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,wEAAwE;IACxE,SAAS,EAAE,WAAW,EAAE,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,GAAG;IAAE,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,EACvF,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,gBAAgB,CAAC,CAa3B;AAED,0DAA0D;AAC1D,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAGxE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,SAAS,EACf,WAAW,EAAE,WAAW,EACxB,EAAE,OAAO,EAAE,GAAqB,EAAE,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACpE,SAAS,CAWX;AAED,wBAAgB,MAAM,CAAC,UAAU,EAAE,IAAI,EAAE,GAAG,SAAa,GAAG,OAAO,CAElE;AAED,wBAAgB,OAAO,IAAI,MAAM,CAEhC;AAED,mFAAmF;AACnF,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,GAAG,IAAI,CAQ1D;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAGjE;AAED,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAU1D"}
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/harnesses/shared.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,qBAAqB,EACrB,SAAS,EACT,SAAS,EACT,WAAW,EACZ,MAAM,6BAA6B,CAAC;AASrC,wDAAwD;AACxD,eAAO,MAAM,sBAAsB,IAAI,CAAC;AAExC,sEAAsE;AACtE,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC,MAAM,WAAW,gBAAgB;IAC/B,6EAA6E;IAC7E,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,wEAAwE;IACxE,SAAS,EAAE,WAAW,EAAE,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,IAAI,CAAC,YAAY,EAAE,WAAW,CAAC,GAAG;IAAE,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,EACvF,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,gBAAgB,CAAC,CAa3B;AAED,0DAA0D;AAC1D,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAGxE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,SAAS,EACf,WAAW,EAAE,WAAW,EACxB,EAAE,OAAO,EAAE,GAA2B,EAAE,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1E,SAAS,CAWX;AAED,wBAAgB,MAAM,CAAC,UAAU,EAAE,IAAI,EAAE,GAAG,SAAa,GAAG,OAAO,CAElE;AAED,wBAAgB,OAAO,IAAI,MAAM,CAEhC;AAED,mFAAmF;AACnF,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,GAAG,IAAI,CAQ1D;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAGjE;AAED,yEAAyE;AACzE,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAU1D"}
@@ -31,7 +31,7 @@ import { captureProcessSnapshot, executableBasename, filterByProcessNames } from
31
31
  /**
32
32
  * The placeholder agent for a running process with no matched session:
33
33
  * RUNNING, with a `pid-<pid>` session id.
34
- */ export function processOnlyAgent(type, processInfo, { summary, cwd = processInfo.cwd }) {
34
+ */ export function processOnlyAgent(type, processInfo, { summary, cwd = processInfo.cwd ?? "" }) {
35
35
  return {
36
36
  name: generateAgentName(cwd, processInfo.pid),
37
37
  type,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/harnesses/shared.ts"],"sourcesContent":["/**\n * Helpers shared by the harness adapters. Each harness keeps its own session\n * matching and parsing; these cover the parts that are the same everywhere.\n */\n\nimport type {\n AgentAdapter,\n AgentDetectionContext,\n AgentInfo,\n AgentType,\n ProcessInfo,\n} from \"../adapters/AgentAdapter.js\";\nimport { AgentStatus } from \"../adapters/AgentAdapter.js\";\nimport { generateAgentName } from \"../utils/matching.js\";\nimport {\n captureProcessSnapshot,\n executableBasename,\n filterByProcessNames,\n} from \"../utils/process.js\";\n\n/** A session untouched for longer than this is IDLE. */\nexport const IDLE_THRESHOLD_MINUTES = 5;\n\n/** Maximum length of a session summary derived from a user prompt. */\nexport const SUMMARY_MAX_LENGTH = 120;\n\nexport interface HarnessProcesses {\n /** Every process whose executable is one of the adapter's `processNames`. */\n relevant: ProcessInfo[];\n /** The subset `canHandle` accepts (deduplicated by PID): the agents. */\n processes: ProcessInfo[];\n}\n\n/**\n * The adapter's processes from the manager's shared snapshot, or from its own\n * capture when called standalone. `relevant` also keeps helper processes\n * (e.g. a harness's child runtime) that detection may need to walk through.\n */\nexport async function findHarnessProcesses(\n adapter: Pick<AgentAdapter, \"canHandle\"> & { readonly processNames: readonly string[] },\n context?: AgentDetectionContext,\n): Promise<HarnessProcesses> {\n const snapshot =\n context?.processes ??\n (await captureProcessSnapshot(adapter.processNames, {\n isCandidate: (process) => adapter.canHandle(process),\n }));\n const relevant = filterByProcessNames(snapshot, adapter.processNames);\n\n const byPid = new Map<number, ProcessInfo>();\n for (const process of relevant) {\n if (!byPid.has(process.pid) && adapter.canHandle(process)) byPid.set(process.pid, process);\n }\n return { relevant, processes: Array.from(byPid.values()) };\n}\n\n/** Whether argv[0]'s basename is `name` or `name.exe`. */\nexport function matchesExecutable(command: string, name: string): boolean {\n const base = executableBasename(command);\n return base === name || base === `${name}.exe`;\n}\n\n/**\n * The placeholder agent for a running process with no matched session:\n * RUNNING, with a `pid-<pid>` session id.\n */\nexport function processOnlyAgent(\n type: AgentType,\n processInfo: ProcessInfo,\n { summary, cwd = processInfo.cwd }: { summary: string; cwd?: string },\n): AgentInfo {\n return {\n name: generateAgentName(cwd, processInfo.pid),\n type,\n status: AgentStatus.RUNNING,\n summary,\n pid: processInfo.pid,\n projectPath: cwd,\n sessionId: `pid-${processInfo.pid}`,\n lastActive: new Date(),\n };\n}\n\nexport function isIdle(lastActive: Date, now = Date.now()): boolean {\n return (now - lastActive.getTime()) / 60000 > IDLE_THRESHOLD_MINUTES;\n}\n\nexport function homeDir(): string {\n return process.env.HOME || process.env.USERPROFILE || \"\";\n}\n\n/** ISO strings, or epoch numbers in seconds or milliseconds; null when invalid. */\nexport function parseTimestamp(value: unknown): Date | null {\n if (typeof value === \"number\") {\n const date = new Date(value < 1_000_000_000_000 ? value * 1000 : value);\n return Number.isNaN(date.getTime()) ? null : date;\n }\n if (typeof value !== \"string\" || !value) return null;\n const date = new Date(value);\n return Number.isNaN(date.getTime()) ? null : date;\n}\n\nexport function truncate(value: string, maxLength: number): string {\n if (value.length <= maxLength) return value;\n return `${value.slice(0, maxLength - 3)}...`;\n}\n\n/** Flatten a string, or an array of `{ text }` blocks, to plain text. */\nexport function flattenTextBlocks(content: unknown): string {\n if (typeof content === \"string\") return content;\n if (!Array.isArray(content)) return \"\";\n return content\n .map((block) =>\n block && typeof block === \"object\" && typeof (block as { text?: unknown }).text === \"string\"\n ? (block as { text: string }).text\n : \"\",\n )\n .join(\"\");\n}\n"],"names":["AgentStatus","generateAgentName","captureProcessSnapshot","executableBasename","filterByProcessNames","IDLE_THRESHOLD_MINUTES","SUMMARY_MAX_LENGTH","findHarnessProcesses","adapter","context","snapshot","processes","processNames","isCandidate","process","canHandle","relevant","byPid","Map","has","pid","set","Array","from","values","matchesExecutable","command","name","base","processOnlyAgent","type","processInfo","summary","cwd","status","RUNNING","projectPath","sessionId","lastActive","Date","isIdle","now","getTime","homeDir","env","HOME","USERPROFILE","parseTimestamp","value","date","Number","isNaN","truncate","maxLength","length","slice","flattenTextBlocks","content","isArray","map","block","text","join"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/harnesses/shared.ts"],"sourcesContent":["/**\n * Helpers shared by the harness adapters. Each harness keeps its own session\n * matching and parsing; these cover the parts that are the same everywhere.\n */\n\nimport type {\n AgentAdapter,\n AgentDetectionContext,\n AgentInfo,\n AgentType,\n ProcessInfo,\n} from \"../adapters/AgentAdapter.js\";\nimport { AgentStatus } from \"../adapters/AgentAdapter.js\";\nimport { generateAgentName } from \"../utils/matching.js\";\nimport {\n captureProcessSnapshot,\n executableBasename,\n filterByProcessNames,\n} from \"../utils/process.js\";\n\n/** A session untouched for longer than this is IDLE. */\nexport const IDLE_THRESHOLD_MINUTES = 5;\n\n/** Maximum length of a session summary derived from a user prompt. */\nexport const SUMMARY_MAX_LENGTH = 120;\n\nexport interface HarnessProcesses {\n /** Every process whose executable is one of the adapter's `processNames`. */\n relevant: ProcessInfo[];\n /** The subset `canHandle` accepts (deduplicated by PID): the agents. */\n processes: ProcessInfo[];\n}\n\n/**\n * The adapter's processes from the manager's shared snapshot, or from its own\n * capture when called standalone. `relevant` also keeps helper processes\n * (e.g. a harness's child runtime) that detection may need to walk through.\n */\nexport async function findHarnessProcesses(\n adapter: Pick<AgentAdapter, \"canHandle\"> & { readonly processNames: readonly string[] },\n context?: AgentDetectionContext,\n): Promise<HarnessProcesses> {\n const snapshot =\n context?.processes ??\n (await captureProcessSnapshot(adapter.processNames, {\n isCandidate: (process) => adapter.canHandle(process),\n }));\n const relevant = filterByProcessNames(snapshot, adapter.processNames);\n\n const byPid = new Map<number, ProcessInfo>();\n for (const process of relevant) {\n if (!byPid.has(process.pid) && adapter.canHandle(process)) byPid.set(process.pid, process);\n }\n return { relevant, processes: Array.from(byPid.values()) };\n}\n\n/** Whether argv[0]'s basename is `name` or `name.exe`. */\nexport function matchesExecutable(command: string, name: string): boolean {\n const base = executableBasename(command);\n return base === name || base === `${name}.exe`;\n}\n\n/**\n * The placeholder agent for a running process with no matched session:\n * RUNNING, with a `pid-<pid>` session id.\n */\nexport function processOnlyAgent(\n type: AgentType,\n processInfo: ProcessInfo,\n { summary, cwd = processInfo.cwd ?? \"\" }: { summary: string; cwd?: string },\n): AgentInfo {\n return {\n name: generateAgentName(cwd, processInfo.pid),\n type,\n status: AgentStatus.RUNNING,\n summary,\n pid: processInfo.pid,\n projectPath: cwd,\n sessionId: `pid-${processInfo.pid}`,\n lastActive: new Date(),\n };\n}\n\nexport function isIdle(lastActive: Date, now = Date.now()): boolean {\n return (now - lastActive.getTime()) / 60000 > IDLE_THRESHOLD_MINUTES;\n}\n\nexport function homeDir(): string {\n return process.env.HOME || process.env.USERPROFILE || \"\";\n}\n\n/** ISO strings, or epoch numbers in seconds or milliseconds; null when invalid. */\nexport function parseTimestamp(value: unknown): Date | null {\n if (typeof value === \"number\") {\n const date = new Date(value < 1_000_000_000_000 ? value * 1000 : value);\n return Number.isNaN(date.getTime()) ? null : date;\n }\n if (typeof value !== \"string\" || !value) return null;\n const date = new Date(value);\n return Number.isNaN(date.getTime()) ? null : date;\n}\n\nexport function truncate(value: string, maxLength: number): string {\n if (value.length <= maxLength) return value;\n return `${value.slice(0, maxLength - 3)}...`;\n}\n\n/** Flatten a string, or an array of `{ text }` blocks, to plain text. */\nexport function flattenTextBlocks(content: unknown): string {\n if (typeof content === \"string\") return content;\n if (!Array.isArray(content)) return \"\";\n return content\n .map((block) =>\n block && typeof block === \"object\" && typeof (block as { text?: unknown }).text === \"string\"\n ? (block as { text: string }).text\n : \"\",\n )\n .join(\"\");\n}\n"],"names":["AgentStatus","generateAgentName","captureProcessSnapshot","executableBasename","filterByProcessNames","IDLE_THRESHOLD_MINUTES","SUMMARY_MAX_LENGTH","findHarnessProcesses","adapter","context","snapshot","processes","processNames","isCandidate","process","canHandle","relevant","byPid","Map","has","pid","set","Array","from","values","matchesExecutable","command","name","base","processOnlyAgent","type","processInfo","summary","cwd","status","RUNNING","projectPath","sessionId","lastActive","Date","isIdle","now","getTime","homeDir","env","HOME","USERPROFILE","parseTimestamp","value","date","Number","isNaN","truncate","maxLength","length","slice","flattenTextBlocks","content","isArray","map","block","text","join"],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { AgentManager } from "./AgentManager.js";
2
- export { getAnthropicCapacityReport, getClaudeCapacityReport, getCodexCapacityReport, getOpenAiCapacityReport, getZaiCapacityReport, } from "./capacity/index.js";
2
+ export { getAnthropicCapacityReport, getClaudeCapacityReport, getCodexCapacityReport, getDevinCapacityReport, getOpenAiCapacityReport, getZaiCapacityReport, } from "./capacity/index.js";
3
3
  export type { CapacityReport, CapacityWindow } from "./capacity/index.js";
4
4
  export { getAgentReadinessReports, worstReadinessStatus, } from "./harnesses/readiness/AgentReadiness.js";
5
5
  export type { AgentReadinessOptions, AgentReadinessReport, ReadinessAgentType, ReadinessStatus, } from "./harnesses/readiness/AgentReadiness.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,EACvB,sBAAsB,EACtB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1E,OAAO,EACL,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,yCAAyC,CAAC;AACjD,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,GAChB,MAAM,yCAAyC,CAAC;AAEjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,wBAAwB,EAAE,KAAK,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACnG,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACtE,YAAY,EACV,YAAY,EACZ,SAAS,EACT,SAAS,EACT,mBAAmB,EACnB,cAAc,EACd,mBAAmB,GACpB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,YAAY,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,mBAAmB,EACnB,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AACrE,YAAY,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,EACL,UAAU,EACV,SAAS,EACT,UAAU,EACV,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,wBAAwB,EACxB,4BAA4B,EAC5B,0BAA0B,EAC1B,2BAA2B,GAC5B,MAAM,kCAAkC,CAAC;AAE1C,YAAY,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAEtF,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,YAAY,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,EAAE,uBAAuB,EAAE,MAAM,uDAAuD,CAAC;AAChG,OAAO,EAAE,sBAAsB,EAAE,MAAM,qDAAqD,CAAC;AAC7F,OAAO,EAAE,mBAAmB,EAAE,MAAM,+CAA+C,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EACL,0BAA0B,EAC1B,uBAAuB,EACvB,sBAAsB,EACtB,sBAAsB,EACtB,uBAAuB,EACvB,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAE1E,OAAO,EACL,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,yCAAyC,CAAC;AACjD,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,GAChB,MAAM,yCAAyC,CAAC;AAEjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,wBAAwB,EAAE,KAAK,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACnG,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACtE,YAAY,EACV,YAAY,EACZ,SAAS,EACT,SAAS,EACT,mBAAmB,EACnB,cAAc,EACd,mBAAmB,GACpB,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,YAAY,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAEpD,OAAO,EACL,aAAa,EACb,mBAAmB,EACnB,mBAAmB,EACnB,uBAAuB,EACvB,mBAAmB,GACpB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AACrE,YAAY,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,EACL,UAAU,EACV,SAAS,EACT,UAAU,EACV,eAAe,EACf,oBAAoB,EACpB,mBAAmB,EACnB,wBAAwB,EACxB,4BAA4B,EAC5B,0BAA0B,EAC1B,2BAA2B,GAC5B,MAAM,kCAAkC,CAAC;AAE1C,YAAY,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAEtF,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACxD,YAAY,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,qCAAqC,CAAC;AAC7E,OAAO,EAAE,uBAAuB,EAAE,MAAM,uDAAuD,CAAC;AAChG,OAAO,EAAE,sBAAsB,EAAE,MAAM,qDAAqD,CAAC;AAC7F,OAAO,EAAE,mBAAmB,EAAE,MAAM,+CAA+C,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { AgentManager } from "./AgentManager.js";
2
- export { getAnthropicCapacityReport, getClaudeCapacityReport, getCodexCapacityReport, getOpenAiCapacityReport, getZaiCapacityReport } from "./capacity/index.js";
2
+ export { getAnthropicCapacityReport, getClaudeCapacityReport, getCodexCapacityReport, getDevinCapacityReport, getOpenAiCapacityReport, getZaiCapacityReport } from "./capacity/index.js";
3
3
  export { getAgentReadinessReports, worstReadinessStatus } from "./harnesses/readiness/AgentReadiness.js";
4
4
  export { createBuiltinAdapters } from "./harnesses/index.js";
5
5
  export { HARNESS_RUNTIME_PROFILES } from "./harnesses/runtimeProfiles.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export { AgentManager } from \"./AgentManager.js\";\nexport {\n getAnthropicCapacityReport,\n getClaudeCapacityReport,\n getCodexCapacityReport,\n getOpenAiCapacityReport,\n getZaiCapacityReport,\n} from \"./capacity/index.js\";\nexport type { CapacityReport, CapacityWindow } from \"./capacity/index.js\";\n\nexport {\n getAgentReadinessReports,\n worstReadinessStatus,\n} from \"./harnesses/readiness/AgentReadiness.js\";\nexport type {\n AgentReadinessOptions,\n AgentReadinessReport,\n ReadinessAgentType,\n ReadinessStatus,\n} from \"./harnesses/readiness/AgentReadiness.js\";\n\nexport { createBuiltinAdapters } from \"./harnesses/index.js\";\nexport { HARNESS_RUNTIME_PROFILES, type StartableAgentType } from \"./harnesses/runtimeProfiles.js\";\nexport { AGENT_TYPES, AgentStatus } from \"./adapters/AgentAdapter.js\";\nexport type {\n AgentAdapter,\n AgentType,\n AgentInfo,\n ConversationMessage,\n SessionSummary,\n ListSessionsOptions,\n} from \"./adapters/AgentAdapter.js\";\n\nexport { TerminalFocusManager } from \"./terminal/TerminalFocusManager.js\";\nexport { TerminalType } from \"./terminal/types.js\";\nexport type { TerminalLocation } from \"./terminal/types.js\";\nexport { TtyWriter } from \"./terminal/TtyWriter.js\";\n\nexport {\n AgentRegistry,\n RenameNotFoundError,\n RenameConflictError,\n AGENT_RUNTIME_PROVIDERS,\n parseTmuxRuntimeRef,\n} from \"./utils/AgentRegistry.js\";\nexport type { AgentRuntimeProvider } from \"./utils/AgentRegistry.js\";\nexport type { InteractiveAgentRuntime } from \"./runtime/types.js\";\nexport {\n startAgent,\n stopAgent,\n focusAgent,\n sendAgentPrompt,\n TmuxUnavailableError,\n AgentNameInUseError,\n AgentPidPollTimeoutError,\n AgentRuntimeUnavailableError,\n AgentTerminalNotFoundError,\n DEFAULT_PID_POLL_TIMEOUT_MS,\n} from \"./runtime/ManagedAgentRuntime.js\";\n\nexport type { AgentRequest } from \"./utils/agent-requests.js\";\nexport { readLatestAgentRequest, writeAgentRequest } from \"./utils/agent-requests.js\";\n\nexport { AGENT_MODES } from \"./durable/DurableAgent.js\";\nexport type { DurableProvider } from \"./durable/DurableAgent.js\";\nexport { DurableAgentRepository } from \"./durable/DurableAgentRepository.js\";\nexport { ClaudePrintAgentService } from \"./harnesses/claude/durable/ClaudePrintAgentService.js\";\nexport { CodexPrintAgentService } from \"./harnesses/codex/durable/CodexPrintAgentService.js\";\nexport { PiPrintAgentService } from \"./harnesses/pi/durable/PiPrintAgentService.js\";\n"],"names":["AgentManager","getAnthropicCapacityReport","getClaudeCapacityReport","getCodexCapacityReport","getOpenAiCapacityReport","getZaiCapacityReport","getAgentReadinessReports","worstReadinessStatus","createBuiltinAdapters","HARNESS_RUNTIME_PROFILES","AGENT_TYPES","AgentStatus","TerminalFocusManager","TerminalType","TtyWriter","AgentRegistry","RenameNotFoundError","RenameConflictError","AGENT_RUNTIME_PROVIDERS","parseTmuxRuntimeRef","startAgent","stopAgent","focusAgent","sendAgentPrompt","TmuxUnavailableError","AgentNameInUseError","AgentPidPollTimeoutError","AgentRuntimeUnavailableError","AgentTerminalNotFoundError","DEFAULT_PID_POLL_TIMEOUT_MS","readLatestAgentRequest","writeAgentRequest","AGENT_MODES","DurableAgentRepository","ClaudePrintAgentService","CodexPrintAgentService","PiPrintAgentService"],"mappings":"AAAA,SAASA,YAAY,QAAQ,oBAAoB;AACjD,SACEC,0BAA0B,EAC1BC,uBAAuB,EACvBC,sBAAsB,EACtBC,uBAAuB,EACvBC,oBAAoB,QACf,sBAAsB;AAG7B,SACEC,wBAAwB,EACxBC,oBAAoB,QACf,0CAA0C;AAQjD,SAASC,qBAAqB,QAAQ,uBAAuB;AAC7D,SAASC,wBAAwB,QAAiC,iCAAiC;AACnG,SAASC,WAAW,EAAEC,WAAW,QAAQ,6BAA6B;AAUtE,SAASC,oBAAoB,QAAQ,qCAAqC;AAC1E,SAASC,YAAY,QAAQ,sBAAsB;AAEnD,SAASC,SAAS,QAAQ,0BAA0B;AAEpD,SACEC,aAAa,EACbC,mBAAmB,EACnBC,mBAAmB,EACnBC,uBAAuB,EACvBC,mBAAmB,QACd,2BAA2B;AAGlC,SACEC,UAAU,EACVC,SAAS,EACTC,UAAU,EACVC,eAAe,EACfC,oBAAoB,EACpBC,mBAAmB,EACnBC,wBAAwB,EACxBC,4BAA4B,EAC5BC,0BAA0B,EAC1BC,2BAA2B,QACtB,mCAAmC;AAG1C,SAASC,sBAAsB,EAAEC,iBAAiB,QAAQ,4BAA4B;AAEtF,SAASC,WAAW,QAAQ,4BAA4B;AAExD,SAASC,sBAAsB,QAAQ,sCAAsC;AAC7E,SAASC,uBAAuB,QAAQ,wDAAwD;AAChG,SAASC,sBAAsB,QAAQ,sDAAsD;AAC7F,SAASC,mBAAmB,QAAQ,gDAAgD"}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export { AgentManager } from \"./AgentManager.js\";\nexport {\n getAnthropicCapacityReport,\n getClaudeCapacityReport,\n getCodexCapacityReport,\n getDevinCapacityReport,\n getOpenAiCapacityReport,\n getZaiCapacityReport,\n} from \"./capacity/index.js\";\nexport type { CapacityReport, CapacityWindow } from \"./capacity/index.js\";\n\nexport {\n getAgentReadinessReports,\n worstReadinessStatus,\n} from \"./harnesses/readiness/AgentReadiness.js\";\nexport type {\n AgentReadinessOptions,\n AgentReadinessReport,\n ReadinessAgentType,\n ReadinessStatus,\n} from \"./harnesses/readiness/AgentReadiness.js\";\n\nexport { createBuiltinAdapters } from \"./harnesses/index.js\";\nexport { HARNESS_RUNTIME_PROFILES, type StartableAgentType } from \"./harnesses/runtimeProfiles.js\";\nexport { AGENT_TYPES, AgentStatus } from \"./adapters/AgentAdapter.js\";\nexport type {\n AgentAdapter,\n AgentType,\n AgentInfo,\n ConversationMessage,\n SessionSummary,\n ListSessionsOptions,\n} from \"./adapters/AgentAdapter.js\";\n\nexport { TerminalFocusManager } from \"./terminal/TerminalFocusManager.js\";\nexport { TerminalType } from \"./terminal/types.js\";\nexport type { TerminalLocation } from \"./terminal/types.js\";\nexport { TtyWriter } from \"./terminal/TtyWriter.js\";\n\nexport {\n AgentRegistry,\n RenameNotFoundError,\n RenameConflictError,\n AGENT_RUNTIME_PROVIDERS,\n parseTmuxRuntimeRef,\n} from \"./utils/AgentRegistry.js\";\nexport type { AgentRuntimeProvider } from \"./utils/AgentRegistry.js\";\nexport type { InteractiveAgentRuntime } from \"./runtime/types.js\";\nexport {\n startAgent,\n stopAgent,\n focusAgent,\n sendAgentPrompt,\n TmuxUnavailableError,\n AgentNameInUseError,\n AgentPidPollTimeoutError,\n AgentRuntimeUnavailableError,\n AgentTerminalNotFoundError,\n DEFAULT_PID_POLL_TIMEOUT_MS,\n} from \"./runtime/ManagedAgentRuntime.js\";\n\nexport type { AgentRequest } from \"./utils/agent-requests.js\";\nexport { readLatestAgentRequest, writeAgentRequest } from \"./utils/agent-requests.js\";\n\nexport { AGENT_MODES } from \"./durable/DurableAgent.js\";\nexport type { DurableProvider } from \"./durable/DurableAgent.js\";\nexport { DurableAgentRepository } from \"./durable/DurableAgentRepository.js\";\nexport { ClaudePrintAgentService } from \"./harnesses/claude/durable/ClaudePrintAgentService.js\";\nexport { CodexPrintAgentService } from \"./harnesses/codex/durable/CodexPrintAgentService.js\";\nexport { PiPrintAgentService } from \"./harnesses/pi/durable/PiPrintAgentService.js\";\n"],"names":["AgentManager","getAnthropicCapacityReport","getClaudeCapacityReport","getCodexCapacityReport","getDevinCapacityReport","getOpenAiCapacityReport","getZaiCapacityReport","getAgentReadinessReports","worstReadinessStatus","createBuiltinAdapters","HARNESS_RUNTIME_PROFILES","AGENT_TYPES","AgentStatus","TerminalFocusManager","TerminalType","TtyWriter","AgentRegistry","RenameNotFoundError","RenameConflictError","AGENT_RUNTIME_PROVIDERS","parseTmuxRuntimeRef","startAgent","stopAgent","focusAgent","sendAgentPrompt","TmuxUnavailableError","AgentNameInUseError","AgentPidPollTimeoutError","AgentRuntimeUnavailableError","AgentTerminalNotFoundError","DEFAULT_PID_POLL_TIMEOUT_MS","readLatestAgentRequest","writeAgentRequest","AGENT_MODES","DurableAgentRepository","ClaudePrintAgentService","CodexPrintAgentService","PiPrintAgentService"],"mappings":"AAAA,SAASA,YAAY,QAAQ,oBAAoB;AACjD,SACEC,0BAA0B,EAC1BC,uBAAuB,EACvBC,sBAAsB,EACtBC,sBAAsB,EACtBC,uBAAuB,EACvBC,oBAAoB,QACf,sBAAsB;AAG7B,SACEC,wBAAwB,EACxBC,oBAAoB,QACf,0CAA0C;AAQjD,SAASC,qBAAqB,QAAQ,uBAAuB;AAC7D,SAASC,wBAAwB,QAAiC,iCAAiC;AACnG,SAASC,WAAW,EAAEC,WAAW,QAAQ,6BAA6B;AAUtE,SAASC,oBAAoB,QAAQ,qCAAqC;AAC1E,SAASC,YAAY,QAAQ,sBAAsB;AAEnD,SAASC,SAAS,QAAQ,0BAA0B;AAEpD,SACEC,aAAa,EACbC,mBAAmB,EACnBC,mBAAmB,EACnBC,uBAAuB,EACvBC,mBAAmB,QACd,2BAA2B;AAGlC,SACEC,UAAU,EACVC,SAAS,EACTC,UAAU,EACVC,eAAe,EACfC,oBAAoB,EACpBC,mBAAmB,EACnBC,wBAAwB,EACxBC,4BAA4B,EAC5BC,0BAA0B,EAC1BC,2BAA2B,QACtB,mCAAmC;AAG1C,SAASC,sBAAsB,EAAEC,iBAAiB,QAAQ,4BAA4B;AAEtF,SAASC,WAAW,QAAQ,4BAA4B;AAExD,SAASC,sBAAsB,QAAQ,sCAAsC;AAC7E,SAASC,uBAAuB,QAAQ,wDAAwD;AAChG,SAASC,sBAAsB,QAAQ,sDAAsD;AAC7F,SAASC,mBAAmB,QAAQ,gDAAgD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-devkit/agent-manager",
3
- "version": "0.39.0",
3
+ "version": "0.41.0",
4
4
  "description": "Standalone agent detection and management utilities for AI DevKit",
5
5
  "keywords": [
6
6
  "agent",