@ai-devkit/agent-manager 0.27.0 → 0.28.1

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 (87) hide show
  1. package/README.md +12 -4
  2. package/dist/__tests__/adapters/CodexAdapter.test.js +199 -0
  3. package/dist/__tests__/adapters/CodexAdapter.test.js.map +1 -1
  4. package/dist/__tests__/database/DurableAgentsDatabase.test.js +100 -0
  5. package/dist/__tests__/database/DurableAgentsDatabase.test.js.map +1 -0
  6. package/dist/__tests__/print/ClaudePrintAgent.integration.test.js +7 -7
  7. package/dist/__tests__/print/ClaudePrintAgent.integration.test.js.map +1 -1
  8. package/dist/__tests__/print/ClaudePrintAgentService.test.js +7 -7
  9. package/dist/__tests__/print/ClaudePrintAgentService.test.js.map +1 -1
  10. package/dist/__tests__/print/ClaudePrintRunner.test.js +1 -1
  11. package/dist/__tests__/print/ClaudePrintRunner.test.js.map +1 -1
  12. package/dist/__tests__/print/DurableAgent.test.js +17 -0
  13. package/dist/__tests__/print/DurableAgent.test.js.map +1 -0
  14. package/dist/__tests__/print/DurableAgentRepository.sqlite.test.js +186 -0
  15. package/dist/__tests__/print/DurableAgentRepository.sqlite.test.js.map +1 -0
  16. package/dist/__tests__/print/{PrintAgentStore.test.js → DurableAgentRepository.test.js} +63 -110
  17. package/dist/__tests__/print/DurableAgentRepository.test.js.map +1 -0
  18. package/dist/adapters/CodexAdapter.d.ts +1 -0
  19. package/dist/adapters/CodexAdapter.d.ts.map +1 -1
  20. package/dist/adapters/CodexAdapter.js +16 -6
  21. package/dist/adapters/CodexAdapter.js.map +1 -1
  22. package/dist/database/connection.d.ts.map +1 -1
  23. package/dist/database/connection.js +17 -9
  24. package/dist/database/connection.js.map +1 -1
  25. package/dist/database/migrations/003_durable_agents.sql +43 -0
  26. package/dist/durable/ClaudeCliProbe.d.ts.map +1 -0
  27. package/dist/{print → durable}/ClaudeCliProbe.js +1 -1
  28. package/dist/durable/ClaudeCliProbe.js.map +1 -0
  29. package/dist/{print → durable}/ClaudePrintAgentService.d.ts +11 -11
  30. package/dist/durable/ClaudePrintAgentService.d.ts.map +1 -0
  31. package/dist/{print → durable}/ClaudePrintAgentService.js +12 -12
  32. package/dist/durable/ClaudePrintAgentService.js.map +1 -0
  33. package/dist/{print → durable}/ClaudePrintRunner.d.ts +3 -3
  34. package/dist/durable/ClaudePrintRunner.d.ts.map +1 -0
  35. package/dist/{print → durable}/ClaudePrintRunner.js +2 -2
  36. package/dist/durable/ClaudePrintRunner.js.map +1 -0
  37. package/dist/durable/DurableAgent.d.ts +61 -0
  38. package/dist/durable/DurableAgent.d.ts.map +1 -0
  39. package/dist/durable/DurableAgent.js +46 -0
  40. package/dist/durable/DurableAgent.js.map +1 -0
  41. package/dist/durable/DurableAgentRepository.d.ts +60 -0
  42. package/dist/durable/DurableAgentRepository.d.ts.map +1 -0
  43. package/dist/durable/DurableAgentRepository.js +325 -0
  44. package/dist/durable/DurableAgentRepository.js.map +1 -0
  45. package/dist/index.d.ts +11 -11
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +6 -6
  48. package/dist/index.js.map +1 -1
  49. package/package.json +1 -1
  50. package/src/__tests__/adapters/CodexAdapter.test.ts +132 -0
  51. package/src/__tests__/database/DurableAgentsDatabase.test.ts +77 -0
  52. package/src/__tests__/print/ClaudePrintAgent.integration.test.ts +6 -6
  53. package/src/__tests__/print/ClaudePrintAgentService.test.ts +7 -7
  54. package/src/__tests__/print/ClaudePrintRunner.test.ts +3 -3
  55. package/src/__tests__/print/{PrintAgent.test.ts → DurableAgent.test.ts} +6 -6
  56. package/src/__tests__/print/DurableAgentRepository.sqlite.test.ts +105 -0
  57. package/src/__tests__/print/DurableAgentRepository.test.ts +163 -0
  58. package/src/adapters/CodexAdapter.ts +14 -11
  59. package/src/database/connection.ts +15 -9
  60. package/src/database/migrations/003_durable_agents.sql +43 -0
  61. package/src/{print → durable}/ClaudeCliProbe.ts +1 -1
  62. package/src/{print → durable}/ClaudePrintAgentService.ts +21 -21
  63. package/src/{print → durable}/ClaudePrintRunner.ts +4 -4
  64. package/src/durable/DurableAgent.ts +91 -0
  65. package/src/durable/DurableAgentRepository.ts +307 -0
  66. package/src/index.ts +26 -25
  67. package/dist/__tests__/print/PrintAgent.test.js +0 -17
  68. package/dist/__tests__/print/PrintAgent.test.js.map +0 -1
  69. package/dist/__tests__/print/PrintAgentStore.test.js.map +0 -1
  70. package/dist/print/ClaudeCliProbe.d.ts.map +0 -1
  71. package/dist/print/ClaudeCliProbe.js.map +0 -1
  72. package/dist/print/ClaudePrintAgentService.d.ts.map +0 -1
  73. package/dist/print/ClaudePrintAgentService.js.map +0 -1
  74. package/dist/print/ClaudePrintRunner.d.ts.map +0 -1
  75. package/dist/print/ClaudePrintRunner.js.map +0 -1
  76. package/dist/print/PrintAgent.d.ts +0 -57
  77. package/dist/print/PrintAgent.d.ts.map +0 -1
  78. package/dist/print/PrintAgent.js +0 -42
  79. package/dist/print/PrintAgent.js.map +0 -1
  80. package/dist/print/PrintAgentStore.d.ts +0 -69
  81. package/dist/print/PrintAgentStore.d.ts.map +0 -1
  82. package/dist/print/PrintAgentStore.js +0 -484
  83. package/dist/print/PrintAgentStore.js.map +0 -1
  84. package/src/__tests__/print/PrintAgentStore.test.ts +0 -192
  85. package/src/print/PrintAgent.ts +0 -86
  86. package/src/print/PrintAgentStore.ts +0 -503
  87. /package/dist/{print → durable}/ClaudeCliProbe.d.ts +0 -0
package/README.md CHANGED
@@ -24,19 +24,27 @@ ai-devkit agent send "run the tests and report back" --id <agent-name> --wait
24
24
  npm test 2>&1 | ai-devkit agent send --id <agent-name> --stdin
25
25
  ```
26
26
 
27
- Claude Code can also be registered as a durable print-mode agent. Registration
27
+ Claude Code can also be registered as a durable agent. Registration
28
28
  does not launch Claude; each send starts one synchronous process and later sends
29
29
  resume the same Claude session:
30
30
 
31
31
  ```bash
32
- ai-devkit agent start --type claude --mode print --name reviewer --cwd /path/to/project
32
+ ai-devkit agent start --type claude --mode durable --name reviewer --cwd /path/to/project
33
33
  ai-devkit agent send "review the current diff" --id reviewer
34
34
  ```
35
35
 
36
- Print mode inherits Claude Code's settings, permissions, hooks, MCP servers, and
36
+ Durable mode inherits Claude Code's settings, permissions, hooks, MCP servers, and
37
37
  tool side effects for that working directory. AI DevKit adds no permission bypass
38
38
  or automatic retry, and prompts are delivered over stdin rather than command-line
39
- arguments. `--timeout` is not supported for print agents in this first release.
39
+ arguments. `--timeout` is not supported for durable agents in this first release.
40
+
41
+ Durable durable-agent state is stored in `~/.ai-devkit/agents.db`. This feature was
42
+ not released with JSON persistence, so there is no legacy import or dual-write.
43
+
44
+ For direct `DurableAgentRepository` consumers, `dbPath` selects the SQLite database. The
45
+ `lockTimeoutMs`, `incompleteLockGraceMs`, and `mutationLockStaleMs` options are
46
+ deprecated, accepted, and ignored because SQLite transactions replace the
47
+ filesystem lock machinery.
40
48
 
41
49
  Use this package directly only when building custom tooling around AI DevKit's agent detection and control surface.
42
50
 
@@ -884,6 +884,65 @@ describe('CodexAdapter', ()=>{
884
884
  expect(mockedMatchProcessesToSessions).not.toHaveBeenCalled();
885
885
  expect(mockedBatchGetSessionFileBirthtimes).not.toHaveBeenCalled();
886
886
  });
887
+ it('uses the hook session mapping instead of a stale registry entry for the same live pid', async ()=>{
888
+ registerEntry();
889
+ const sessionsDir = path.join(tmpDir, '.codex', 'sessions');
890
+ const dateDir = path.join(sessionsDir, '2026', '06', '26');
891
+ const mappingPath = path.join(tmpDir, '.codex', 'ai-devkit', 'sessions.json');
892
+ const currentSessionFile = path.join(dateDir, 'sess-current.jsonl');
893
+ const recentTs = new Date().toISOString();
894
+ fs.mkdirSync(dateDir, {
895
+ recursive: true
896
+ });
897
+ fs.mkdirSync(path.dirname(mappingPath), {
898
+ recursive: true
899
+ });
900
+ fs.writeFileSync(currentSessionFile, [
901
+ JSON.stringify({
902
+ type: 'session_meta',
903
+ payload: {
904
+ id: 'sess-current',
905
+ timestamp: recentTs,
906
+ cwd: '/repo-current'
907
+ }
908
+ }),
909
+ JSON.stringify({
910
+ type: 'event',
911
+ timestamp: recentTs,
912
+ payload: {
913
+ type: 'token_count',
914
+ message: 'Hello from current mapping'
915
+ }
916
+ })
917
+ ].join('\n'));
918
+ fs.writeFileSync(mappingPath, JSON.stringify({
919
+ 100: currentSessionFile
920
+ }));
921
+ cachedAdapter.codexSessionsDir = sessionsDir;
922
+ cachedAdapter.sessionMappingPath = mappingPath;
923
+ const processes = [
924
+ {
925
+ pid: 100,
926
+ command: 'codex',
927
+ cwd: '/repo-current',
928
+ tty: 'ttys001',
929
+ startTime: new Date()
930
+ }
931
+ ];
932
+ mockedListAgentProcesses.mockReturnValue(processes);
933
+ mockedEnrichProcesses.mockReturnValue(processes);
934
+ const agents = await cachedAdapter.detectAgents();
935
+ expect(agents).toHaveLength(1);
936
+ expect(agents[0]).toMatchObject({
937
+ type: 'codex',
938
+ pid: 100,
939
+ sessionId: 'sess-current',
940
+ sessionFilePath: currentSessionFile,
941
+ summary: 'Hello from current mapping'
942
+ });
943
+ expect(mockedMatchProcessesToSessions).not.toHaveBeenCalled();
944
+ expect(mockedBatchGetSessionFileBirthtimes).not.toHaveBeenCalled();
945
+ });
887
946
  it('falls through when no registry entry exists for the pid', async ()=>{
888
947
  const processes = [
889
948
  {
@@ -2152,6 +2211,146 @@ describe('CodexAdapter', ()=>{
2152
2211
  expect(result).toHaveLength(1);
2153
2212
  expect(result[0].firstUserMessage).toBe('first user');
2154
2213
  });
2214
+ it('captures the first current Codex response_item user message as firstUserMessage', async ()=>{
2215
+ const day = path.join(sessionsDir, '2025', '01', '01');
2216
+ writeCodexSession(day, 'response-item-user', [
2217
+ {
2218
+ type: 'session_meta',
2219
+ payload: {
2220
+ id: 'response-item-user',
2221
+ cwd: '/repo',
2222
+ timestamp: '2025-01-01T00:00:00Z'
2223
+ }
2224
+ },
2225
+ {
2226
+ type: 'response_item',
2227
+ timestamp: '2025-01-01T00:00:00.500Z',
2228
+ payload: {
2229
+ type: 'message',
2230
+ role: 'user',
2231
+ content: [
2232
+ {
2233
+ type: 'input_text',
2234
+ text: '<environment_context>\n <cwd>/repo</cwd>\n</environment_context>'
2235
+ }
2236
+ ]
2237
+ }
2238
+ },
2239
+ {
2240
+ type: 'response_item',
2241
+ timestamp: '2025-01-01T00:00:01Z',
2242
+ payload: {
2243
+ type: 'message',
2244
+ role: 'assistant',
2245
+ content: [
2246
+ {
2247
+ type: 'output_text',
2248
+ text: 'preamble'
2249
+ }
2250
+ ]
2251
+ }
2252
+ },
2253
+ {
2254
+ type: 'response_item',
2255
+ timestamp: '2025-01-01T00:00:02Z',
2256
+ payload: {
2257
+ type: 'message',
2258
+ role: 'user',
2259
+ content: [
2260
+ {
2261
+ type: 'input_text',
2262
+ text: 'first current user'
2263
+ }
2264
+ ]
2265
+ }
2266
+ },
2267
+ {
2268
+ type: 'response_item',
2269
+ timestamp: '2025-01-01T00:00:03Z',
2270
+ payload: {
2271
+ type: 'message',
2272
+ role: 'user',
2273
+ content: [
2274
+ {
2275
+ type: 'input_text',
2276
+ text: 'second current user'
2277
+ }
2278
+ ]
2279
+ }
2280
+ }
2281
+ ]);
2282
+ const result = await adapter.listSessions({
2283
+ cwd: '/repo'
2284
+ });
2285
+ expect(result).toHaveLength(1);
2286
+ expect(result[0].firstUserMessage).toBe('first current user');
2287
+ });
2288
+ it('captures the first current Codex event_msg user message as firstUserMessage', async ()=>{
2289
+ const day = path.join(sessionsDir, '2025', '01', '01');
2290
+ writeCodexSession(day, 'event-msg-user', [
2291
+ {
2292
+ type: 'session_meta',
2293
+ payload: {
2294
+ id: 'event-msg-user',
2295
+ cwd: '/repo',
2296
+ timestamp: '2025-01-01T00:00:00Z'
2297
+ }
2298
+ },
2299
+ {
2300
+ type: 'event_msg',
2301
+ timestamp: '2025-01-01T00:00:01Z',
2302
+ payload: {
2303
+ type: 'item_completed',
2304
+ item: {
2305
+ type: 'AgentMessage',
2306
+ content: [
2307
+ {
2308
+ type: 'Text',
2309
+ text: 'preamble'
2310
+ }
2311
+ ]
2312
+ }
2313
+ }
2314
+ },
2315
+ {
2316
+ type: 'event_msg',
2317
+ timestamp: '2025-01-01T00:00:02Z',
2318
+ payload: {
2319
+ type: 'item_completed',
2320
+ item: {
2321
+ type: 'UserMessage',
2322
+ content: [
2323
+ {
2324
+ type: 'text',
2325
+ text: 'first event user'
2326
+ }
2327
+ ]
2328
+ }
2329
+ }
2330
+ },
2331
+ {
2332
+ type: 'event_msg',
2333
+ timestamp: '2025-01-01T00:00:03Z',
2334
+ payload: {
2335
+ type: 'item_completed',
2336
+ item: {
2337
+ type: 'UserMessage',
2338
+ content: [
2339
+ {
2340
+ type: 'text',
2341
+ text: 'second event user'
2342
+ }
2343
+ ]
2344
+ }
2345
+ }
2346
+ }
2347
+ ]);
2348
+ const result = await adapter.listSessions({
2349
+ cwd: '/repo'
2350
+ });
2351
+ expect(result).toHaveLength(1);
2352
+ expect(result[0].firstUserMessage).toBe('first event user');
2353
+ });
2155
2354
  it('returns empty firstUserMessage when no user_message exists', async ()=>{
2156
2355
  const day = path.join(sessionsDir, '2025', '01', '01');
2157
2356
  writeCodexSession(day, 's', [