@ai-devkit/agent-manager 0.29.0 → 0.31.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 (202) hide show
  1. package/dist/__tests__/adapters/ClaudeCodeAdapter.test.js +1 -342
  2. package/dist/__tests__/adapters/ClaudeCodeAdapter.test.js.map +1 -1
  3. package/dist/__tests__/database/DurableAgentsDatabase.test.js +31 -3
  4. package/dist/__tests__/database/DurableAgentsDatabase.test.js.map +1 -1
  5. package/dist/__tests__/durable/CodexCliProbe.test.js +129 -0
  6. package/dist/__tests__/durable/CodexCliProbe.test.js.map +1 -0
  7. package/dist/__tests__/durable/CodexDurableAgentRepository.test.js +84 -0
  8. package/dist/__tests__/durable/CodexDurableAgentRepository.test.js.map +1 -0
  9. package/dist/__tests__/durable/CodexPrintAgent.integration.test.js +126 -0
  10. package/dist/__tests__/durable/CodexPrintAgent.integration.test.js.map +1 -0
  11. package/dist/__tests__/durable/CodexPrintAgentService.test.js +271 -0
  12. package/dist/__tests__/durable/CodexPrintAgentService.test.js.map +1 -0
  13. package/dist/__tests__/durable/CodexPrintRunner.test.js +393 -0
  14. package/dist/__tests__/durable/CodexPrintRunner.test.js.map +1 -0
  15. package/dist/__tests__/print/ClaudeCliProbe.test.js +3 -7
  16. package/dist/__tests__/print/ClaudeCliProbe.test.js.map +1 -1
  17. package/dist/__tests__/print/ClaudePrintAgent.integration.test.js +25 -28
  18. package/dist/__tests__/print/ClaudePrintAgent.integration.test.js.map +1 -1
  19. package/dist/__tests__/print/ClaudePrintAgentService.test.js +65 -0
  20. package/dist/__tests__/print/ClaudePrintAgentService.test.js.map +1 -1
  21. package/dist/__tests__/print/ClaudePrintRunner.test.js +4 -10
  22. package/dist/__tests__/print/ClaudePrintRunner.test.js.map +1 -1
  23. package/dist/__tests__/providers/claude/ClaudeAgentMapper.test.js +79 -0
  24. package/dist/__tests__/providers/claude/ClaudeAgentMapper.test.js.map +1 -0
  25. package/dist/__tests__/providers/claude/ClaudeSessionLocator.test.js +436 -0
  26. package/dist/__tests__/providers/claude/ClaudeSessionLocator.test.js.map +1 -0
  27. package/dist/__tests__/providers/pi/durable/PiCliProbe.test.js +76 -0
  28. package/dist/__tests__/providers/pi/durable/PiCliProbe.test.js.map +1 -0
  29. package/dist/__tests__/providers/pi/durable/PiDurableAgentRepository.test.js +43 -0
  30. package/dist/__tests__/providers/pi/durable/PiDurableAgentRepository.test.js.map +1 -0
  31. package/dist/__tests__/providers/pi/durable/PiPrintAgentService.test.js +202 -0
  32. package/dist/__tests__/providers/pi/durable/PiPrintAgentService.test.js.map +1 -0
  33. package/dist/__tests__/providers/pi/durable/PiPrintProtocol.test.js +119 -0
  34. package/dist/__tests__/providers/pi/durable/PiPrintProtocol.test.js.map +1 -0
  35. package/dist/__tests__/providers/pi/durable/PiPrintRunner.test.js +386 -0
  36. package/dist/__tests__/providers/pi/durable/PiPrintRunner.test.js.map +1 -0
  37. package/dist/__tests__/providers/pi/durable/PiStreamParser.test.js +34 -0
  38. package/dist/__tests__/providers/pi/durable/PiStreamParser.test.js.map +1 -0
  39. package/dist/__tests__/utils/ClaudeSessionParser.test.js +2 -2
  40. package/dist/__tests__/utils/ClaudeSessionParser.test.js.map +1 -1
  41. package/dist/adapters/index.d.ts +1 -1
  42. package/dist/adapters/index.d.ts.map +1 -1
  43. package/dist/adapters/index.js +1 -1
  44. package/dist/adapters/index.js.map +1 -1
  45. package/dist/database/connection.d.ts.map +1 -1
  46. package/dist/database/connection.js +5 -1
  47. package/dist/database/connection.js.map +1 -1
  48. package/dist/database/migrations/004_codex_durable_sessions.sql +1 -0
  49. package/dist/durable/DurableAgent.d.ts +23 -3
  50. package/dist/durable/DurableAgent.d.ts.map +1 -1
  51. package/dist/durable/DurableAgent.js +12 -0
  52. package/dist/durable/DurableAgent.js.map +1 -1
  53. package/dist/durable/DurableAgentRepository.d.ts +6 -7
  54. package/dist/durable/DurableAgentRepository.d.ts.map +1 -1
  55. package/dist/durable/DurableAgentRepository.js +73 -42
  56. package/dist/durable/DurableAgentRepository.js.map +1 -1
  57. package/dist/durable/process.d.ts +8 -0
  58. package/dist/durable/process.d.ts.map +1 -0
  59. package/dist/durable/process.js +40 -0
  60. package/dist/durable/process.js.map +1 -0
  61. package/dist/durable/run.d.ts +32 -0
  62. package/dist/durable/run.d.ts.map +1 -0
  63. package/dist/durable/run.js +46 -0
  64. package/dist/durable/run.js.map +1 -0
  65. package/dist/durable/utils.d.ts +19 -0
  66. package/dist/durable/utils.d.ts.map +1 -0
  67. package/dist/durable/utils.js +52 -0
  68. package/dist/durable/utils.js.map +1 -0
  69. package/dist/index.d.ts +17 -13
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +9 -8
  72. package/dist/index.js.map +1 -1
  73. package/dist/providers/claude/ClaudeAgentMapper.d.ts +21 -0
  74. package/dist/providers/claude/ClaudeAgentMapper.d.ts.map +1 -0
  75. package/dist/providers/claude/ClaudeAgentMapper.js +42 -0
  76. package/dist/providers/claude/ClaudeAgentMapper.js.map +1 -0
  77. package/dist/providers/claude/ClaudeCodeAdapter.d.ts +29 -0
  78. package/dist/providers/claude/ClaudeCodeAdapter.d.ts.map +1 -0
  79. package/dist/providers/claude/ClaudeCodeAdapter.js +129 -0
  80. package/dist/providers/claude/ClaudeCodeAdapter.js.map +1 -0
  81. package/dist/{adapters/ClaudeCodeAdapter.d.ts → providers/claude/ClaudeSessionLocator.d.ts} +68 -52
  82. package/dist/providers/claude/ClaudeSessionLocator.d.ts.map +1 -0
  83. package/dist/{adapters/ClaudeCodeAdapter.js → providers/claude/ClaudeSessionLocator.js} +71 -189
  84. package/dist/providers/claude/ClaudeSessionLocator.js.map +1 -0
  85. package/dist/{utils → providers/claude}/ClaudeSessionParser.d.ts +2 -2
  86. package/dist/providers/claude/ClaudeSessionParser.d.ts.map +1 -0
  87. package/dist/{utils → providers/claude}/ClaudeSessionParser.js +1 -1
  88. package/dist/providers/claude/ClaudeSessionParser.js.map +1 -0
  89. package/dist/providers/claude/durable/ClaudeCliProbe.d.ts.map +1 -0
  90. package/dist/{durable → providers/claude/durable}/ClaudeCliProbe.js +4 -9
  91. package/dist/providers/claude/durable/ClaudeCliProbe.js.map +1 -0
  92. package/dist/{durable → providers/claude/durable}/ClaudePrintAgentService.d.ts +4 -9
  93. package/dist/providers/claude/durable/ClaudePrintAgentService.d.ts.map +1 -0
  94. package/dist/providers/claude/durable/ClaudePrintAgentService.js +45 -0
  95. package/dist/providers/claude/durable/ClaudePrintAgentService.js.map +1 -0
  96. package/dist/{durable → providers/claude/durable}/ClaudePrintRunner.d.ts +3 -3
  97. package/dist/providers/claude/durable/ClaudePrintRunner.d.ts.map +1 -0
  98. package/dist/providers/claude/durable/ClaudePrintRunner.js +75 -0
  99. package/dist/providers/claude/durable/ClaudePrintRunner.js.map +1 -0
  100. package/dist/providers/claude/durable/ClaudeStreamParser.d.ts +17 -0
  101. package/dist/providers/claude/durable/ClaudeStreamParser.d.ts.map +1 -0
  102. package/dist/providers/claude/durable/ClaudeStreamParser.js +69 -0
  103. package/dist/providers/claude/durable/ClaudeStreamParser.js.map +1 -0
  104. package/dist/providers/codex/durable/CodexCliProbe.d.ts +20 -0
  105. package/dist/providers/codex/durable/CodexCliProbe.d.ts.map +1 -0
  106. package/dist/providers/codex/durable/CodexCliProbe.js +61 -0
  107. package/dist/providers/codex/durable/CodexCliProbe.js.map +1 -0
  108. package/dist/providers/codex/durable/CodexPrintAgentService.d.ts +40 -0
  109. package/dist/providers/codex/durable/CodexPrintAgentService.d.ts.map +1 -0
  110. package/dist/providers/codex/durable/CodexPrintAgentService.js +50 -0
  111. package/dist/providers/codex/durable/CodexPrintAgentService.js.map +1 -0
  112. package/dist/providers/codex/durable/CodexPrintRunner.d.ts +34 -0
  113. package/dist/providers/codex/durable/CodexPrintRunner.d.ts.map +1 -0
  114. package/dist/providers/codex/durable/CodexPrintRunner.js +78 -0
  115. package/dist/providers/codex/durable/CodexPrintRunner.js.map +1 -0
  116. package/dist/providers/codex/durable/CodexStreamParser.d.ts +21 -0
  117. package/dist/providers/codex/durable/CodexStreamParser.d.ts.map +1 -0
  118. package/dist/providers/codex/durable/CodexStreamParser.js +86 -0
  119. package/dist/providers/codex/durable/CodexStreamParser.js.map +1 -0
  120. package/dist/providers/pi/durable/PiCliProbe.d.ts +20 -0
  121. package/dist/providers/pi/durable/PiCliProbe.d.ts.map +1 -0
  122. package/dist/providers/pi/durable/PiCliProbe.js +51 -0
  123. package/dist/providers/pi/durable/PiCliProbe.js.map +1 -0
  124. package/dist/providers/pi/durable/PiPrintAgentService.d.ts +39 -0
  125. package/dist/providers/pi/durable/PiPrintAgentService.d.ts.map +1 -0
  126. package/dist/providers/pi/durable/PiPrintAgentService.js +47 -0
  127. package/dist/providers/pi/durable/PiPrintAgentService.js.map +1 -0
  128. package/dist/providers/pi/durable/PiPrintProtocol.d.ts +4 -0
  129. package/dist/providers/pi/durable/PiPrintProtocol.d.ts.map +1 -0
  130. package/dist/providers/pi/durable/PiPrintProtocol.js +44 -0
  131. package/dist/providers/pi/durable/PiPrintProtocol.js.map +1 -0
  132. package/dist/providers/pi/durable/PiPrintRunner.d.ts +32 -0
  133. package/dist/providers/pi/durable/PiPrintRunner.d.ts.map +1 -0
  134. package/dist/providers/pi/durable/PiPrintRunner.js +61 -0
  135. package/dist/providers/pi/durable/PiPrintRunner.js.map +1 -0
  136. package/dist/providers/pi/durable/PiStreamParser.d.ts +21 -0
  137. package/dist/providers/pi/durable/PiStreamParser.d.ts.map +1 -0
  138. package/dist/providers/pi/durable/PiStreamParser.js +71 -0
  139. package/dist/providers/pi/durable/PiStreamParser.js.map +1 -0
  140. package/package.json +1 -1
  141. package/src/__tests__/adapters/ClaudeCodeAdapter.test.ts +1 -307
  142. package/src/__tests__/database/DurableAgentsDatabase.test.ts +28 -3
  143. package/src/__tests__/durable/CodexCliProbe.test.ts +63 -0
  144. package/src/__tests__/durable/CodexDurableAgentRepository.test.ts +54 -0
  145. package/src/__tests__/durable/CodexPrintAgent.integration.test.ts +79 -0
  146. package/src/__tests__/durable/CodexPrintAgentService.test.ts +139 -0
  147. package/src/__tests__/durable/CodexPrintRunner.test.ts +189 -0
  148. package/src/__tests__/fixtures/fake-codex.cjs +45 -0
  149. package/src/__tests__/print/ClaudeCliProbe.test.ts +3 -7
  150. package/src/__tests__/print/ClaudePrintAgent.integration.test.ts +11 -21
  151. package/src/__tests__/print/ClaudePrintAgentService.test.ts +33 -1
  152. package/src/__tests__/print/ClaudePrintRunner.test.ts +4 -10
  153. package/src/__tests__/providers/claude/ClaudeAgentMapper.test.ts +84 -0
  154. package/src/__tests__/providers/claude/ClaudeSessionLocator.test.ts +411 -0
  155. package/src/__tests__/providers/pi/durable/PiCliProbe.test.ts +38 -0
  156. package/src/__tests__/providers/pi/durable/PiDurableAgentRepository.test.ts +32 -0
  157. package/src/__tests__/providers/pi/durable/PiPrintAgentService.test.ts +69 -0
  158. package/src/__tests__/providers/pi/durable/PiPrintProtocol.test.ts +33 -0
  159. package/src/__tests__/providers/pi/durable/PiPrintRunner.test.ts +107 -0
  160. package/src/__tests__/providers/pi/durable/PiStreamParser.test.ts +24 -0
  161. package/src/__tests__/utils/ClaudeSessionParser.test.ts +2 -2
  162. package/src/adapters/index.ts +1 -1
  163. package/src/database/connection.ts +3 -1
  164. package/src/database/migrations/004_codex_durable_sessions.sql +1 -0
  165. package/src/durable/DurableAgent.ts +51 -3
  166. package/src/durable/DurableAgentRepository.ts +60 -31
  167. package/src/durable/process.ts +28 -0
  168. package/src/durable/run.ts +74 -0
  169. package/src/durable/utils.ts +61 -0
  170. package/src/index.ts +31 -15
  171. package/src/providers/claude/ClaudeAgentMapper.ts +62 -0
  172. package/src/providers/claude/ClaudeCodeAdapter.ts +160 -0
  173. package/src/{adapters/ClaudeCodeAdapter.ts → providers/claude/ClaudeSessionLocator.ts} +97 -239
  174. package/src/{utils → providers/claude}/ClaudeSessionParser.ts +2 -2
  175. package/src/{durable → providers/claude/durable}/ClaudeCliProbe.ts +4 -10
  176. package/src/providers/claude/durable/ClaudePrintAgentService.ts +66 -0
  177. package/src/providers/claude/durable/ClaudePrintRunner.ts +92 -0
  178. package/src/providers/claude/durable/ClaudeStreamParser.ts +79 -0
  179. package/src/providers/codex/durable/CodexCliProbe.ts +60 -0
  180. package/src/providers/codex/durable/CodexPrintAgentService.ts +65 -0
  181. package/src/providers/codex/durable/CodexPrintRunner.ts +94 -0
  182. package/src/providers/codex/durable/CodexStreamParser.ts +104 -0
  183. package/src/providers/pi/durable/PiCliProbe.ts +48 -0
  184. package/src/providers/pi/durable/PiPrintAgentService.ts +69 -0
  185. package/src/providers/pi/durable/PiPrintProtocol.ts +39 -0
  186. package/src/providers/pi/durable/PiPrintRunner.ts +88 -0
  187. package/src/providers/pi/durable/PiStreamParser.ts +84 -0
  188. package/dist/adapters/ClaudeCodeAdapter.d.ts.map +0 -1
  189. package/dist/adapters/ClaudeCodeAdapter.js.map +0 -1
  190. package/dist/durable/ClaudeCliProbe.d.ts.map +0 -1
  191. package/dist/durable/ClaudeCliProbe.js.map +0 -1
  192. package/dist/durable/ClaudePrintAgentService.d.ts.map +0 -1
  193. package/dist/durable/ClaudePrintAgentService.js +0 -66
  194. package/dist/durable/ClaudePrintAgentService.js.map +0 -1
  195. package/dist/durable/ClaudePrintRunner.d.ts.map +0 -1
  196. package/dist/durable/ClaudePrintRunner.js +0 -128
  197. package/dist/durable/ClaudePrintRunner.js.map +0 -1
  198. package/dist/utils/ClaudeSessionParser.d.ts.map +0 -1
  199. package/dist/utils/ClaudeSessionParser.js.map +0 -1
  200. package/src/durable/ClaudePrintAgentService.ts +0 -94
  201. package/src/durable/ClaudePrintRunner.ts +0 -139
  202. /package/dist/{durable → providers/claude/durable}/ClaudeCliProbe.d.ts +0 -0
@@ -18,11 +18,11 @@ function dbPath() {
18
18
  return path.join(root, 'state', 'agents.db');
19
19
  }
20
20
  describe('durable agents schema', ()=>{
21
- it('migrates to version 3 with durable constraints and indexes', ()=>{
21
+ it('migrates to version 4 with durable constraints and indexes', ()=>{
22
22
  const connection = new DatabaseConnection({
23
23
  dbPath: dbPath()
24
24
  });
25
- expect(getSchemaVersion(connection)).toBe(3);
25
+ expect(getSchemaVersion(connection)).toBe(4);
26
26
  const table = connection.queryOne("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'durable_agents'");
27
27
  expect(table?.sql).toContain("DEFAULT 'durable'");
28
28
  expect(table?.sql).toContain("state IN ('ready','running','degraded')");
@@ -32,6 +32,34 @@ describe('durable agents schema', ()=>{
32
32
  ]));
33
33
  connection.close();
34
34
  });
35
+ it('upgrades a version 3 database to nullable, unique provider sessions', ()=>{
36
+ const file = dbPath();
37
+ fs.mkdirSync(path.dirname(file), {
38
+ recursive: true
39
+ });
40
+ const raw = new Database(file);
41
+ raw.exec(`
42
+ CREATE TABLE durable_agents (
43
+ id TEXT PRIMARY KEY,
44
+ provider_session_id TEXT NOT NULL UNIQUE
45
+ );
46
+ INSERT INTO durable_agents (id, provider_session_id) VALUES ('existing', 'existing-session');
47
+ PRAGMA user_version = 3;
48
+ `);
49
+ raw.close();
50
+ const connection = new DatabaseConnection({
51
+ dbPath: file
52
+ });
53
+ expect(getSchemaVersion(connection)).toBe(4);
54
+ expect(()=>connection.execute('INSERT INTO durable_agents (id, provider_session_id) VALUES (?, NULL)', [
55
+ 'codex'
56
+ ])).not.toThrow();
57
+ expect(()=>connection.execute('INSERT INTO durable_agents (id, provider_session_id) VALUES (?, ?)', [
58
+ 'duplicate',
59
+ 'existing-session'
60
+ ])).toThrow(/UNIQUE/i);
61
+ connection.close();
62
+ });
35
63
  it('enforces case-insensitive names and active-run consistency but permits new providers', ()=>{
36
64
  const connection = new DatabaseConnection({
37
65
  dbPath: dbPath()
@@ -68,7 +96,7 @@ describe('readonly DatabaseConnection', ()=>{
68
96
  dbPath: file,
69
97
  readonly: true
70
98
  });
71
- expect(readonly.queryOne('PRAGMA user_version')?.user_version).toBe(3);
99
+ expect(readonly.queryOne('PRAGMA user_version')?.user_version).toBe(4);
72
100
  readonly.close();
73
101
  expect(fs.statSync(file).mtimeMs).toBe(before);
74
102
  });
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/__tests__/database/DurableAgentsDatabase.test.ts"],"sourcesContent":["import fs from 'fs';\nimport os from 'os';\nimport path from 'path';\nimport Database from 'better-sqlite3';\nimport { afterEach, describe, expect, it } from 'vitest';\nimport { DatabaseConnection } from '../../database/connection.js';\nimport { getSchemaVersion } from '../../database/schema.js';\n\nconst roots: string[] = [];\n\nafterEach(() => {\n for (const root of roots.splice(0)) fs.rmSync(root, { recursive: true, force: true });\n});\n\nfunction dbPath(): string {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), 'durable-agent-db-'));\n roots.push(root);\n return path.join(root, 'state', 'agents.db');\n}\n\ndescribe('durable agents schema', () => {\n it('migrates to version 3 with durable constraints and indexes', () => {\n const connection = new DatabaseConnection({ dbPath: dbPath() });\n expect(getSchemaVersion(connection)).toBe(3);\n const table = connection.queryOne<{ sql: string }>(\n \"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'durable_agents'\",\n );\n expect(table?.sql).toContain(\"DEFAULT 'durable'\");\n expect(table?.sql).toContain(\"state IN ('ready','running','degraded')\");\n expect(connection.query<{ name: string }>(\n \"SELECT name FROM sqlite_master WHERE type = 'index' AND tbl_name = 'durable_agents'\",\n ).map(({ name }) => name)).toEqual(expect.arrayContaining([\n 'idx_durable_agents_state', 'idx_durable_agents_list',\n ]));\n connection.close();\n });\n\n it('enforces case-insensitive names and active-run consistency but permits new providers', () => {\n const connection = new DatabaseConnection({ dbPath: dbPath() });\n const insert = (name: string, provider: string, state = 'ready') => connection.execute(`\n INSERT INTO durable_agents (\n id, name, provider, mode, cwd, provider_session_id, state, session_health,\n created_at, updated_at\n ) VALUES (?, ?, ?, 'durable', '/tmp', ?, ?, 'uninitialized', ?, ?)\n `, [crypto.randomUUID(), name, provider, crypto.randomUUID(), state, new Date().toISOString(), new Date().toISOString()]);\n expect(() => insert('Alpha', 'future-provider')).not.toThrow();\n expect(() => insert('alpha', 'claude')).toThrow(/UNIQUE/i);\n expect(() => insert('Broken', 'claude', 'running')).toThrow(/CHECK/i);\n connection.close();\n });\n});\n\ndescribe('readonly DatabaseConnection', () => {\n it('opens an already migrated database without changing it', () => {\n const file = dbPath();\n const writable = new DatabaseConnection({ dbPath: file });\n writable.close();\n const before = fs.statSync(file).mtimeMs;\n const readonly = new DatabaseConnection({ dbPath: file, readonly: true });\n expect(readonly.queryOne<{ user_version: number }>('PRAGMA user_version')?.user_version).toBe(3);\n readonly.close();\n expect(fs.statSync(file).mtimeMs).toBe(before);\n });\n\n it('does not create a missing database or migrate an old one', () => {\n const missing = dbPath();\n expect(() => new DatabaseConnection({ dbPath: missing, readonly: true })).toThrow(/readonly.*exist/i);\n expect(fs.existsSync(missing)).toBe(false);\n\n fs.mkdirSync(path.dirname(missing), { recursive: true });\n const raw = new Database(missing);\n raw.pragma('user_version = 2');\n raw.close();\n expect(() => new DatabaseConnection({ dbPath: missing, readonly: true })).toThrow(/schema version 3/i);\n expect(new Database(missing, { readonly: true }).pragma('user_version', { simple: true })).toBe(2);\n });\n});\n"],"names":["fs","os","path","Database","afterEach","describe","expect","it","DatabaseConnection","getSchemaVersion","roots","root","splice","rmSync","recursive","force","dbPath","mkdtempSync","join","tmpdir","push","connection","toBe","table","queryOne","sql","toContain","query","map","name","toEqual","arrayContaining","close","insert","provider","state","execute","crypto","randomUUID","Date","toISOString","not","toThrow","file","writable","before","statSync","mtimeMs","readonly","user_version","missing","existsSync","mkdirSync","dirname","raw","pragma","simple"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/__tests__/database/DurableAgentsDatabase.test.ts"],"sourcesContent":["import fs from 'fs';\nimport os from 'os';\nimport path from 'path';\nimport Database from 'better-sqlite3';\nimport { afterEach, describe, expect, it } from 'vitest';\nimport { DatabaseConnection } from '../../database/connection.js';\nimport { getSchemaVersion } from '../../database/schema.js';\n\nconst roots: string[] = [];\n\nafterEach(() => {\n for (const root of roots.splice(0)) fs.rmSync(root, { recursive: true, force: true });\n});\n\nfunction dbPath(): string {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), 'durable-agent-db-'));\n roots.push(root);\n return path.join(root, 'state', 'agents.db');\n}\n\ndescribe('durable agents schema', () => {\n it('migrates to version 4 with durable constraints and indexes', () => {\n const connection = new DatabaseConnection({ dbPath: dbPath() });\n expect(getSchemaVersion(connection)).toBe(4);\n const table = connection.queryOne<{ sql: string }>(\n \"SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'durable_agents'\",\n );\n expect(table?.sql).toContain(\"DEFAULT 'durable'\");\n expect(table?.sql).toContain(\"state IN ('ready','running','degraded')\");\n expect(connection.query<{ name: string }>(\n \"SELECT name FROM sqlite_master WHERE type = 'index' AND tbl_name = 'durable_agents'\",\n ).map(({ name }) => name)).toEqual(expect.arrayContaining([\n 'idx_durable_agents_state', 'idx_durable_agents_list',\n ]));\n connection.close();\n });\n\n it('upgrades a version 3 database to nullable, unique provider sessions', () => {\n const file = dbPath();\n fs.mkdirSync(path.dirname(file), { recursive: true });\n const raw = new Database(file);\n raw.exec(`\n CREATE TABLE durable_agents (\n id TEXT PRIMARY KEY,\n provider_session_id TEXT NOT NULL UNIQUE\n );\n INSERT INTO durable_agents (id, provider_session_id) VALUES ('existing', 'existing-session');\n PRAGMA user_version = 3;\n `);\n raw.close();\n\n const connection = new DatabaseConnection({ dbPath: file });\n expect(getSchemaVersion(connection)).toBe(4);\n expect(() => connection.execute(\n 'INSERT INTO durable_agents (id, provider_session_id) VALUES (?, NULL)', ['codex'],\n )).not.toThrow();\n expect(() => connection.execute(\n 'INSERT INTO durable_agents (id, provider_session_id) VALUES (?, ?)', ['duplicate', 'existing-session'],\n )).toThrow(/UNIQUE/i);\n connection.close();\n });\n\n it('enforces case-insensitive names and active-run consistency but permits new providers', () => {\n const connection = new DatabaseConnection({ dbPath: dbPath() });\n const insert = (name: string, provider: string, state = 'ready') => connection.execute(`\n INSERT INTO durable_agents (\n id, name, provider, mode, cwd, provider_session_id, state, session_health,\n created_at, updated_at\n ) VALUES (?, ?, ?, 'durable', '/tmp', ?, ?, 'uninitialized', ?, ?)\n `, [crypto.randomUUID(), name, provider, crypto.randomUUID(), state, new Date().toISOString(), new Date().toISOString()]);\n expect(() => insert('Alpha', 'future-provider')).not.toThrow();\n expect(() => insert('alpha', 'claude')).toThrow(/UNIQUE/i);\n expect(() => insert('Broken', 'claude', 'running')).toThrow(/CHECK/i);\n connection.close();\n });\n});\n\ndescribe('readonly DatabaseConnection', () => {\n it('opens an already migrated database without changing it', () => {\n const file = dbPath();\n const writable = new DatabaseConnection({ dbPath: file });\n writable.close();\n const before = fs.statSync(file).mtimeMs;\n const readonly = new DatabaseConnection({ dbPath: file, readonly: true });\n expect(readonly.queryOne<{ user_version: number }>('PRAGMA user_version')?.user_version).toBe(4);\n readonly.close();\n expect(fs.statSync(file).mtimeMs).toBe(before);\n });\n\n it('does not create a missing database or migrate an old one', () => {\n const missing = dbPath();\n expect(() => new DatabaseConnection({ dbPath: missing, readonly: true })).toThrow(/readonly.*exist/i);\n expect(fs.existsSync(missing)).toBe(false);\n\n fs.mkdirSync(path.dirname(missing), { recursive: true });\n const raw = new Database(missing);\n raw.pragma('user_version = 2');\n raw.close();\n expect(() => new DatabaseConnection({ dbPath: missing, readonly: true })).toThrow(/schema version 3/i);\n expect(new Database(missing, { readonly: true }).pragma('user_version', { simple: true })).toBe(2);\n });\n});\n"],"names":["fs","os","path","Database","afterEach","describe","expect","it","DatabaseConnection","getSchemaVersion","roots","root","splice","rmSync","recursive","force","dbPath","mkdtempSync","join","tmpdir","push","connection","toBe","table","queryOne","sql","toContain","query","map","name","toEqual","arrayContaining","close","file","mkdirSync","dirname","raw","exec","execute","not","toThrow","insert","provider","state","crypto","randomUUID","Date","toISOString","writable","before","statSync","mtimeMs","readonly","user_version","missing","existsSync","pragma","simple"],"mappings":"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"}
@@ -0,0 +1,129 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+ describe('CodexCliProbe', ()=>{
3
+ it('validates version, exec JSON/stdin, and resume capabilities without a model call', async ()=>{
4
+ const api = await import('../../index.js');
5
+ expect(api).toHaveProperty('CodexCliProbe');
6
+ const exec = vi.fn().mockResolvedValueOnce({
7
+ stdout: 'codex-cli 0.147.0',
8
+ stderr: ''
9
+ }).mockResolvedValueOnce({
10
+ stdout: 'Usage: codex exec [PROMPT]\n--json\n- read from stdin\nresume',
11
+ stderr: ''
12
+ }).mockResolvedValueOnce({
13
+ stdout: 'Usage: codex exec resume [SESSION_ID] [PROMPT]\n--json\n- stdin',
14
+ stderr: ''
15
+ });
16
+ const Probe = api.CodexCliProbe;
17
+ await expect(new Probe({
18
+ executable: 'fake-codex',
19
+ exec
20
+ }).validate()).resolves.toEqual({
21
+ executable: 'fake-codex',
22
+ version: 'codex-cli 0.147.0'
23
+ });
24
+ expect(exec.mock.calls).toEqual([
25
+ [
26
+ 'fake-codex',
27
+ [
28
+ '--version'
29
+ ]
30
+ ],
31
+ [
32
+ 'fake-codex',
33
+ [
34
+ 'exec',
35
+ '--help'
36
+ ]
37
+ ],
38
+ [
39
+ 'fake-codex',
40
+ [
41
+ 'exec',
42
+ 'resume',
43
+ '--help'
44
+ ]
45
+ ]
46
+ ]);
47
+ });
48
+ it('rejects unsupported and unavailable CLIs with bounded sanitized errors', async ()=>{
49
+ const api = await import('../../index.js');
50
+ const Probe = api.CodexCliProbe;
51
+ const unsupported = new Probe({
52
+ exec: vi.fn().mockResolvedValueOnce({
53
+ stdout: 'version',
54
+ stderr: ''
55
+ }).mockResolvedValueOnce({
56
+ stdout: 'exec',
57
+ stderr: ''
58
+ }).mockResolvedValueOnce({
59
+ stdout: 'resume',
60
+ stderr: ''
61
+ })
62
+ });
63
+ await expect(unsupported.validate()).rejects.toMatchObject({
64
+ code: 'CODEX_CLI_UNSUPPORTED'
65
+ });
66
+ const missingCommands = new Probe({
67
+ exec: vi.fn().mockResolvedValueOnce({
68
+ stdout: 'version',
69
+ stderr: ''
70
+ }).mockResolvedValueOnce({
71
+ stdout: '',
72
+ stderr: ''
73
+ }).mockResolvedValueOnce({
74
+ stdout: '',
75
+ stderr: ''
76
+ })
77
+ });
78
+ await expect(missingCommands.validate()).rejects.toMatchObject({
79
+ code: 'CODEX_CLI_UNSUPPORTED'
80
+ });
81
+ const unavailable = new Probe({
82
+ exec: vi.fn().mockRejectedValue(new Error(`bad\0${'x'.repeat(1000)}`))
83
+ });
84
+ const error = await unavailable.validate().catch((value)=>value);
85
+ expect(error.code).toBe('CODEX_CLI_UNAVAILABLE');
86
+ expect(error.message).not.toContain('\0');
87
+ expect(error.message.length).toBeLessThan(600);
88
+ });
89
+ it('reports an empty version response as unknown', async ()=>{
90
+ const api = await import('../../index.js');
91
+ const Probe = api.CodexCliProbe;
92
+ const exec = vi.fn().mockResolvedValueOnce({
93
+ stdout: ' \n',
94
+ stderr: ''
95
+ }).mockResolvedValueOnce({
96
+ stdout: 'exec --json -',
97
+ stderr: ''
98
+ }).mockResolvedValueOnce({
99
+ stdout: 'resume --json -',
100
+ stderr: ''
101
+ });
102
+ await expect(new Probe({
103
+ exec
104
+ }).validate()).resolves.toMatchObject({
105
+ version: 'unknown'
106
+ });
107
+ });
108
+ it('requires a standalone stdin dash rather than accepting flag hyphens', async ()=>{
109
+ const api = await import('../../index.js');
110
+ const Probe = api.CodexCliProbe;
111
+ const exec = vi.fn().mockResolvedValueOnce({
112
+ stdout: 'version',
113
+ stderr: ''
114
+ }).mockResolvedValueOnce({
115
+ stdout: 'exec --json',
116
+ stderr: ''
117
+ }).mockResolvedValueOnce({
118
+ stdout: 'resume --json',
119
+ stderr: ''
120
+ });
121
+ await expect(new Probe({
122
+ exec
123
+ }).validate()).rejects.toMatchObject({
124
+ code: 'CODEX_CLI_UNSUPPORTED'
125
+ });
126
+ });
127
+ });
128
+
129
+ //# sourceMappingURL=CodexCliProbe.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/__tests__/durable/CodexCliProbe.test.ts"],"sourcesContent":["import { describe, expect, it, vi } from 'vitest';\n\ndescribe('CodexCliProbe', () => {\n it('validates version, exec JSON/stdin, and resume capabilities without a model call', async () => {\n const api = await import('../../index.js') as Record<string, unknown>;\n expect(api).toHaveProperty('CodexCliProbe');\n const exec = vi.fn()\n .mockResolvedValueOnce({ stdout: 'codex-cli 0.147.0', stderr: '' })\n .mockResolvedValueOnce({ stdout: 'Usage: codex exec [PROMPT]\\n--json\\n- read from stdin\\nresume', stderr: '' })\n .mockResolvedValueOnce({ stdout: 'Usage: codex exec resume [SESSION_ID] [PROMPT]\\n--json\\n- stdin', stderr: '' });\n const Probe = api.CodexCliProbe as new (options: unknown) => any;\n\n await expect(new Probe({ executable: 'fake-codex', exec }).validate()).resolves.toEqual({\n executable: 'fake-codex', version: 'codex-cli 0.147.0',\n });\n expect(exec.mock.calls).toEqual([\n ['fake-codex', ['--version']],\n ['fake-codex', ['exec', '--help']],\n ['fake-codex', ['exec', 'resume', '--help']],\n ]);\n });\n\n it('rejects unsupported and unavailable CLIs with bounded sanitized errors', async () => {\n const api = await import('../../index.js') as Record<string, unknown>;\n const Probe = api.CodexCliProbe as new (options: unknown) => any;\n const unsupported = new Probe({ exec: vi.fn()\n .mockResolvedValueOnce({ stdout: 'version', stderr: '' })\n .mockResolvedValueOnce({ stdout: 'exec', stderr: '' })\n .mockResolvedValueOnce({ stdout: 'resume', stderr: '' }) });\n await expect(unsupported.validate()).rejects.toMatchObject({ code: 'CODEX_CLI_UNSUPPORTED' });\n const missingCommands = new Probe({ exec: vi.fn()\n .mockResolvedValueOnce({ stdout: 'version', stderr: '' })\n .mockResolvedValueOnce({ stdout: '', stderr: '' })\n .mockResolvedValueOnce({ stdout: '', stderr: '' }) });\n await expect(missingCommands.validate()).rejects.toMatchObject({ code: 'CODEX_CLI_UNSUPPORTED' });\n\n const unavailable = new Probe({ exec: vi.fn().mockRejectedValue(new Error(`bad\\0${'x'.repeat(1000)}`)) });\n const error = await unavailable.validate().catch((value: Error & { code: string }) => value);\n expect(error.code).toBe('CODEX_CLI_UNAVAILABLE');\n expect(error.message).not.toContain('\\0');\n expect(error.message.length).toBeLessThan(600);\n });\n\n it('reports an empty version response as unknown', async () => {\n const api = await import('../../index.js') as Record<string, unknown>;\n const Probe = api.CodexCliProbe as new (options: unknown) => any;\n const exec = vi.fn()\n .mockResolvedValueOnce({ stdout: ' \\n', stderr: '' })\n .mockResolvedValueOnce({ stdout: 'exec --json -', stderr: '' })\n .mockResolvedValueOnce({ stdout: 'resume --json -', stderr: '' });\n await expect(new Probe({ exec }).validate()).resolves.toMatchObject({ version: 'unknown' });\n });\n\n it('requires a standalone stdin dash rather than accepting flag hyphens', async () => {\n const api = await import('../../index.js') as Record<string, unknown>;\n const Probe = api.CodexCliProbe as new (options: unknown) => any;\n const exec = vi.fn()\n .mockResolvedValueOnce({ stdout: 'version', stderr: '' })\n .mockResolvedValueOnce({ stdout: 'exec --json', stderr: '' })\n .mockResolvedValueOnce({ stdout: 'resume --json', stderr: '' });\n await expect(new Probe({ exec }).validate()).rejects.toMatchObject({ code: 'CODEX_CLI_UNSUPPORTED' });\n });\n});\n"],"names":["describe","expect","it","vi","api","toHaveProperty","exec","fn","mockResolvedValueOnce","stdout","stderr","Probe","CodexCliProbe","executable","validate","resolves","toEqual","version","mock","calls","unsupported","rejects","toMatchObject","code","missingCommands","unavailable","mockRejectedValue","Error","repeat","error","catch","value","toBe","message","not","toContain","length","toBeLessThan"],"mappings":"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"}
@@ -0,0 +1,84 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { afterEach, describe, expect, it } from 'vitest';
5
+ import { DurableAgentRepository } from '../../durable/DurableAgentRepository.js';
6
+ const SESSION = '22222222-2222-4222-8222-222222222222';
7
+ const OTHER_SESSION = '33333333-3333-4333-8333-333333333333';
8
+ const roots = [];
9
+ afterEach(()=>{
10
+ for (const root of roots.splice(0))fs.rmSync(root, {
11
+ recursive: true,
12
+ force: true
13
+ });
14
+ });
15
+ function fixture() {
16
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'codex-durable-repository-'));
17
+ roots.push(root);
18
+ const cwd = path.join(root, 'project');
19
+ fs.mkdirSync(cwd);
20
+ const processInspector = {
21
+ getIdentity: (pid)=>({
22
+ pid,
23
+ startedAt: 'owner-start'
24
+ })
25
+ };
26
+ return {
27
+ cwd,
28
+ repository: new DurableAgentRepository({
29
+ dbPath: path.join(root, 'agents.db'),
30
+ processInspector
31
+ })
32
+ };
33
+ }
34
+ describe('Codex durable-agent repository binding', ()=>{
35
+ it('creates Codex agents unbound and binds during the owned run', async ()=>{
36
+ const { cwd, repository } = fixture();
37
+ const agent = await repository.create({
38
+ name: 'reviewer',
39
+ cwd,
40
+ provider: 'codex'
41
+ });
42
+ expect(agent).toMatchObject({
43
+ provider: 'codex',
44
+ mode: 'durable',
45
+ providerSessionId: null
46
+ });
47
+ const run = await repository.acquireRun(agent.id);
48
+ const bound = await repository.bindProviderSession(agent.id, run.token, SESSION);
49
+ expect(bound.providerSessionId).toBe(SESSION);
50
+ await expect(repository.bindProviderSession(agent.id, run.token, SESSION)).resolves.toMatchObject({
51
+ providerSessionId: SESSION
52
+ });
53
+ });
54
+ it('rejects invalid, stale, replacement, and duplicate bindings', async ()=>{
55
+ const { cwd, repository } = fixture();
56
+ const first = await repository.create({
57
+ name: 'first',
58
+ cwd,
59
+ provider: 'codex'
60
+ });
61
+ const second = await repository.create({
62
+ name: 'second',
63
+ cwd,
64
+ provider: 'codex'
65
+ });
66
+ const firstRun = await repository.acquireRun(first.id);
67
+ const secondRun = await repository.acquireRun(second.id);
68
+ await expect(repository.bindProviderSession(first.id, 'stale', SESSION)).rejects.toMatchObject({
69
+ code: 'DURABLE_AGENT_REPOSITORY'
70
+ });
71
+ await expect(repository.bindProviderSession(first.id, firstRun.token, 'invalid')).rejects.toMatchObject({
72
+ code: 'DURABLE_AGENT_REPOSITORY'
73
+ });
74
+ await repository.bindProviderSession(first.id, firstRun.token, SESSION);
75
+ await expect(repository.bindProviderSession(first.id, firstRun.token, OTHER_SESSION)).rejects.toMatchObject({
76
+ code: 'CODEX_SESSION_MISMATCH'
77
+ });
78
+ await expect(repository.bindProviderSession(second.id, secondRun.token, SESSION)).rejects.toMatchObject({
79
+ code: 'CODEX_SESSION_MISMATCH'
80
+ });
81
+ });
82
+ });
83
+
84
+ //# sourceMappingURL=CodexDurableAgentRepository.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/__tests__/durable/CodexDurableAgentRepository.test.ts"],"sourcesContent":["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { afterEach, describe, expect, it } from 'vitest';\nimport { DurableAgentRepository } from '../../durable/DurableAgentRepository.js';\n\nconst SESSION = '22222222-2222-4222-8222-222222222222';\nconst OTHER_SESSION = '33333333-3333-4333-8333-333333333333';\nconst roots: string[] = [];\n\nafterEach(() => {\n for (const root of roots.splice(0)) fs.rmSync(root, { recursive: true, force: true });\n});\n\nfunction fixture() {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), 'codex-durable-repository-'));\n roots.push(root);\n const cwd = path.join(root, 'project');\n fs.mkdirSync(cwd);\n const processInspector = { getIdentity: (pid: number) => ({ pid, startedAt: 'owner-start' }) };\n return { cwd, repository: new DurableAgentRepository({ dbPath: path.join(root, 'agents.db'), processInspector }) };\n}\n\ndescribe('Codex durable-agent repository binding', () => {\n it('creates Codex agents unbound and binds during the owned run', async () => {\n const { cwd, repository } = fixture();\n const agent = await repository.create({ name: 'reviewer', cwd, provider: 'codex' });\n expect(agent).toMatchObject({ provider: 'codex', mode: 'durable', providerSessionId: null });\n\n const run = await repository.acquireRun(agent.id);\n const bound = await repository.bindProviderSession(agent.id, run.token, SESSION);\n expect(bound.providerSessionId).toBe(SESSION);\n await expect(repository.bindProviderSession(agent.id, run.token, SESSION))\n .resolves.toMatchObject({ providerSessionId: SESSION });\n });\n\n it('rejects invalid, stale, replacement, and duplicate bindings', async () => {\n const { cwd, repository } = fixture();\n const first = await repository.create({ name: 'first', cwd, provider: 'codex' });\n const second = await repository.create({ name: 'second', cwd, provider: 'codex' });\n const firstRun = await repository.acquireRun(first.id);\n const secondRun = await repository.acquireRun(second.id);\n\n await expect(repository.bindProviderSession(first.id, 'stale', SESSION))\n .rejects.toMatchObject({ code: 'DURABLE_AGENT_REPOSITORY' });\n await expect(repository.bindProviderSession(first.id, firstRun.token, 'invalid'))\n .rejects.toMatchObject({ code: 'DURABLE_AGENT_REPOSITORY' });\n await repository.bindProviderSession(first.id, firstRun.token, SESSION);\n await expect(repository.bindProviderSession(first.id, firstRun.token, OTHER_SESSION))\n .rejects.toMatchObject({ code: 'CODEX_SESSION_MISMATCH' });\n await expect(repository.bindProviderSession(second.id, secondRun.token, SESSION))\n .rejects.toMatchObject({ code: 'CODEX_SESSION_MISMATCH' });\n });\n});\n"],"names":["fs","os","path","afterEach","describe","expect","it","DurableAgentRepository","SESSION","OTHER_SESSION","roots","root","splice","rmSync","recursive","force","fixture","mkdtempSync","join","tmpdir","push","cwd","mkdirSync","processInspector","getIdentity","pid","startedAt","repository","dbPath","agent","create","name","provider","toMatchObject","mode","providerSessionId","run","acquireRun","id","bound","bindProviderSession","token","toBe","resolves","first","second","firstRun","secondRun","rejects","code"],"mappings":"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"}
@@ -0,0 +1,126 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { afterEach, describe, expect, it, vi } from 'vitest';
5
+ import { CodexPrintError, CodexPrintAgentService, DurableAgentRepository } from '../../index.js';
6
+ const SESSION = '22222222-2222-4222-8222-222222222222';
7
+ const roots = [];
8
+ afterEach(()=>{
9
+ for (const root of roots.splice(0))fs.rmSync(root, {
10
+ recursive: true,
11
+ force: true
12
+ });
13
+ });
14
+ describe('Codex print-agent fake-provider journey', ()=>{
15
+ it('creates unbound, then binds and explicitly resumes the provider-minted session', async ()=>{
16
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'codex-print-integration-'));
17
+ roots.push(root);
18
+ const cwd = path.join(root, 'project');
19
+ fs.mkdirSync(cwd);
20
+ const processInspector = {
21
+ getIdentity: (pid)=>({
22
+ pid,
23
+ startedAt: `process-${pid}`
24
+ })
25
+ };
26
+ const repository = new DurableAgentRepository({
27
+ dbPath: path.join(root, 'agents.db'),
28
+ processInspector
29
+ });
30
+ const runner = {
31
+ run: vi.fn().mockImplementation(async (request)=>{
32
+ await request.onSpawn({
33
+ pid: 42,
34
+ startedAt: 'process-42'
35
+ });
36
+ await request.onSession(SESSION);
37
+ return {
38
+ sessionId: SESSION,
39
+ result: `answer:${request.prompt}`,
40
+ messages: [
41
+ `answer:${request.prompt}`
42
+ ],
43
+ exitCode: 0
44
+ };
45
+ })
46
+ };
47
+ const service = new CodexPrintAgentService({
48
+ repository,
49
+ probe: {
50
+ validate: vi.fn()
51
+ },
52
+ runner
53
+ });
54
+ const created = await service.create({
55
+ name: 'reviewer',
56
+ cwd
57
+ });
58
+ expect(created).toMatchObject({
59
+ provider: 'codex',
60
+ providerSessionId: null,
61
+ sessionHealth: 'uninitialized'
62
+ });
63
+ const first = await service.send(created.id, 'first secret');
64
+ expect(first).toMatchObject({
65
+ result: 'answer:first secret'
66
+ });
67
+ const bound = await repository.getById(created.id);
68
+ expect(bound?.providerSessionId).toBe(first.sessionId);
69
+ await expect(service.send(created.id, 'follow up')).resolves.toMatchObject({
70
+ result: 'answer:follow up'
71
+ });
72
+ expect(runner.run.mock.calls[0][0].agent).toMatchObject({
73
+ providerSessionId: null
74
+ });
75
+ expect(runner.run.mock.calls[1][0].agent).toMatchObject({
76
+ providerSessionId: SESSION
77
+ });
78
+ });
79
+ it('retains a first-run binding when the provider fails after thread start', async ()=>{
80
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'codex-print-bind-failure-'));
81
+ roots.push(root);
82
+ const cwd = path.join(root, 'project');
83
+ fs.mkdirSync(cwd);
84
+ const processInspector = {
85
+ getIdentity: (pid)=>({
86
+ pid,
87
+ startedAt: `process-${pid}`
88
+ })
89
+ };
90
+ const repository = new DurableAgentRepository({
91
+ dbPath: path.join(root, 'agents.db'),
92
+ processInspector
93
+ });
94
+ const runner = {
95
+ run: vi.fn().mockImplementation(async (request)=>{
96
+ await request.onSpawn({
97
+ pid: 42,
98
+ startedAt: 'process-42'
99
+ });
100
+ await request.onSession(SESSION);
101
+ throw new CodexPrintError('Codex print run failed.', 'CODEX_PROCESS');
102
+ })
103
+ };
104
+ const service = new CodexPrintAgentService({
105
+ repository,
106
+ probe: {
107
+ validate: vi.fn()
108
+ },
109
+ runner
110
+ });
111
+ const created = await service.create({
112
+ name: 'reviewer',
113
+ cwd
114
+ });
115
+ await expect(service.send(created.id, 'secret')).rejects.toMatchObject({
116
+ code: 'CODEX_PROCESS'
117
+ });
118
+ expect(await repository.getById(created.id)).toMatchObject({
119
+ providerSessionId: SESSION,
120
+ state: 'degraded',
121
+ sessionHealth: 'unknown'
122
+ });
123
+ });
124
+ });
125
+
126
+ //# sourceMappingURL=CodexPrintAgent.integration.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/__tests__/durable/CodexPrintAgent.integration.test.ts"],"sourcesContent":["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { afterEach, describe, expect, it, vi } from 'vitest';\nimport {\n CodexPrintError,\n CodexPrintAgentService,\n DurableAgentRepository,\n type ProcessInspector,\n} from '../../index.js';\n\nconst SESSION = '22222222-2222-4222-8222-222222222222';\nconst roots: string[] = [];\n\nafterEach(() => {\n for (const root of roots.splice(0)) fs.rmSync(root, { recursive: true, force: true });\n});\n\ndescribe('Codex print-agent fake-provider journey', () => {\n it('creates unbound, then binds and explicitly resumes the provider-minted session', async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), 'codex-print-integration-'));\n roots.push(root);\n const cwd = path.join(root, 'project');\n fs.mkdirSync(cwd);\n const processInspector: ProcessInspector = {\n getIdentity: (pid) => ({ pid, startedAt: `process-${pid}` }),\n };\n const repository = new DurableAgentRepository({ dbPath: path.join(root, 'agents.db'), processInspector });\n const runner = {\n run: vi.fn().mockImplementation(async (request) => {\n await request.onSpawn({ pid: 42, startedAt: 'process-42' });\n await request.onSession(SESSION);\n return { sessionId: SESSION, result: `answer:${request.prompt}`, messages: [`answer:${request.prompt}`], exitCode: 0 };\n }),\n };\n const service = new CodexPrintAgentService({\n repository, probe: { validate: vi.fn() }, runner,\n });\n\n const created = await service.create({ name: 'reviewer', cwd });\n expect(created).toMatchObject({ provider: 'codex', providerSessionId: null, sessionHealth: 'uninitialized' });\n\n const first = await service.send(created.id, 'first secret');\n expect(first).toMatchObject({ result: 'answer:first secret' });\n const bound = await repository.getById(created.id);\n expect(bound?.providerSessionId).toBe(first.sessionId);\n await expect(service.send(created.id, 'follow up')).resolves.toMatchObject({ result: 'answer:follow up' });\n\n expect(runner.run.mock.calls[0][0].agent).toMatchObject({ providerSessionId: null });\n expect(runner.run.mock.calls[1][0].agent).toMatchObject({ providerSessionId: SESSION });\n });\n\n it('retains a first-run binding when the provider fails after thread start', async () => {\n const root = fs.mkdtempSync(path.join(os.tmpdir(), 'codex-print-bind-failure-'));\n roots.push(root);\n const cwd = path.join(root, 'project');\n fs.mkdirSync(cwd);\n const processInspector: ProcessInspector = {\n getIdentity: (pid) => ({ pid, startedAt: `process-${pid}` }),\n };\n const repository = new DurableAgentRepository({ dbPath: path.join(root, 'agents.db'), processInspector });\n const runner = {\n run: vi.fn().mockImplementation(async (request) => {\n await request.onSpawn({ pid: 42, startedAt: 'process-42' });\n await request.onSession(SESSION);\n throw new CodexPrintError('Codex print run failed.', 'CODEX_PROCESS');\n }),\n };\n const service = new CodexPrintAgentService({\n repository, probe: { validate: vi.fn() }, runner,\n });\n const created = await service.create({ name: 'reviewer', cwd });\n\n await expect(service.send(created.id, 'secret')).rejects.toMatchObject({ code: 'CODEX_PROCESS' });\n expect((await repository.getById(created.id))).toMatchObject({\n providerSessionId: SESSION, state: 'degraded', sessionHealth: 'unknown',\n });\n });\n});\n"],"names":["fs","os","path","afterEach","describe","expect","it","vi","CodexPrintError","CodexPrintAgentService","DurableAgentRepository","SESSION","roots","root","splice","rmSync","recursive","force","mkdtempSync","join","tmpdir","push","cwd","mkdirSync","processInspector","getIdentity","pid","startedAt","repository","dbPath","runner","run","fn","mockImplementation","request","onSpawn","onSession","sessionId","result","prompt","messages","exitCode","service","probe","validate","created","create","name","toMatchObject","provider","providerSessionId","sessionHealth","first","send","id","bound","getById","toBe","resolves","mock","calls","agent","rejects","code","state"],"mappings":"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"}