@sapiom/harness 0.0.0 → 0.1.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 (246) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +44 -43
  3. package/dist/cli/auth.d.ts +26 -0
  4. package/dist/cli/auth.d.ts.map +1 -0
  5. package/dist/cli/auth.js +39 -0
  6. package/dist/cli/auth.js.map +1 -0
  7. package/dist/cli/bin.d.ts +3 -0
  8. package/dist/cli/bin.d.ts.map +1 -0
  9. package/dist/cli/bin.js +163 -0
  10. package/dist/cli/bin.js.map +1 -0
  11. package/dist/cli/consent.d.ts +15 -0
  12. package/dist/cli/consent.d.ts.map +1 -0
  13. package/dist/cli/consent.js +81 -0
  14. package/dist/cli/consent.js.map +1 -0
  15. package/dist/cli/doctor.d.ts +27 -0
  16. package/dist/cli/doctor.d.ts.map +1 -0
  17. package/dist/cli/doctor.js +74 -0
  18. package/dist/cli/doctor.js.map +1 -0
  19. package/dist/cli/machine-id.d.ts +19 -0
  20. package/dist/cli/machine-id.d.ts.map +1 -0
  21. package/dist/cli/machine-id.js +75 -0
  22. package/dist/cli/machine-id.js.map +1 -0
  23. package/dist/cli/paths.d.ts +3 -0
  24. package/dist/cli/paths.d.ts.map +1 -0
  25. package/dist/cli/paths.js +11 -0
  26. package/dist/cli/paths.js.map +1 -0
  27. package/dist/cli/settings.d.ts +26 -0
  28. package/dist/cli/settings.d.ts.map +1 -0
  29. package/dist/cli/settings.js +123 -0
  30. package/dist/cli/settings.js.map +1 -0
  31. package/dist/core/adapters/claude-code.d.ts +41 -0
  32. package/dist/core/adapters/claude-code.d.ts.map +1 -0
  33. package/dist/core/adapters/claude-code.js +238 -0
  34. package/dist/core/adapters/claude-code.js.map +1 -0
  35. package/dist/core/adapters/codex.d.ts +59 -0
  36. package/dist/core/adapters/codex.d.ts.map +1 -0
  37. package/dist/core/adapters/codex.js +319 -0
  38. package/dist/core/adapters/codex.js.map +1 -0
  39. package/dist/core/canvas-body.d.ts +45 -0
  40. package/dist/core/canvas-body.d.ts.map +1 -0
  41. package/dist/core/canvas-body.js +94 -0
  42. package/dist/core/canvas-body.js.map +1 -0
  43. package/dist/core/canvas-cache.d.ts +23 -0
  44. package/dist/core/canvas-cache.d.ts.map +1 -0
  45. package/dist/core/canvas-cache.js +64 -0
  46. package/dist/core/canvas-cache.js.map +1 -0
  47. package/dist/core/canvas-enrich.d.ts +106 -0
  48. package/dist/core/canvas-enrich.d.ts.map +1 -0
  49. package/dist/core/canvas-enrich.js +178 -0
  50. package/dist/core/canvas-enrich.js.map +1 -0
  51. package/dist/core/canvas-enrichment.d.ts +144 -0
  52. package/dist/core/canvas-enrichment.d.ts.map +1 -0
  53. package/dist/core/canvas-enrichment.js +192 -0
  54. package/dist/core/canvas-enrichment.js.map +1 -0
  55. package/dist/core/canvas-graph.d.ts +71 -0
  56. package/dist/core/canvas-graph.d.ts.map +1 -0
  57. package/dist/core/canvas-graph.js +111 -0
  58. package/dist/core/canvas-graph.js.map +1 -0
  59. package/dist/core/canvas-index-classify.d.ts +41 -0
  60. package/dist/core/canvas-index-classify.d.ts.map +1 -0
  61. package/dist/core/canvas-index-classify.js +63 -0
  62. package/dist/core/canvas-index-classify.js.map +1 -0
  63. package/dist/core/canvas-interconnections.d.ts +24 -0
  64. package/dist/core/canvas-interconnections.d.ts.map +1 -0
  65. package/dist/core/canvas-interconnections.js +96 -0
  66. package/dist/core/canvas-interconnections.js.map +1 -0
  67. package/dist/core/canvas-manifest-check.d.ts +15 -0
  68. package/dist/core/canvas-manifest-check.d.ts.map +1 -0
  69. package/dist/core/canvas-manifest-check.js +90 -0
  70. package/dist/core/canvas-manifest-check.js.map +1 -0
  71. package/dist/core/canvas-render.d.ts +73 -0
  72. package/dist/core/canvas-render.d.ts.map +1 -0
  73. package/dist/core/canvas-render.js +145 -0
  74. package/dist/core/canvas-render.js.map +1 -0
  75. package/dist/core/canvas-svg.d.ts +50 -0
  76. package/dist/core/canvas-svg.d.ts.map +1 -0
  77. package/dist/core/canvas-svg.js +240 -0
  78. package/dist/core/canvas-svg.js.map +1 -0
  79. package/dist/core/canvas-template.d.ts +43 -0
  80. package/dist/core/canvas-template.d.ts.map +1 -0
  81. package/dist/core/canvas-template.js +365 -0
  82. package/dist/core/canvas-template.js.map +1 -0
  83. package/dist/core/canvas-watcher.d.ts +25 -0
  84. package/dist/core/canvas-watcher.d.ts.map +1 -0
  85. package/dist/core/canvas-watcher.js +144 -0
  86. package/dist/core/canvas-watcher.js.map +1 -0
  87. package/dist/core/collector/batcher.d.ts +57 -0
  88. package/dist/core/collector/batcher.d.ts.map +1 -0
  89. package/dist/core/collector/batcher.js +143 -0
  90. package/dist/core/collector/batcher.js.map +1 -0
  91. package/dist/core/collector/codex-tailer.d.ts +85 -0
  92. package/dist/core/collector/codex-tailer.d.ts.map +1 -0
  93. package/dist/core/collector/codex-tailer.js +270 -0
  94. package/dist/core/collector/codex-tailer.js.map +1 -0
  95. package/dist/core/collector/normalizer.d.ts +33 -0
  96. package/dist/core/collector/normalizer.d.ts.map +1 -0
  97. package/dist/core/collector/normalizer.js +94 -0
  98. package/dist/core/collector/normalizer.js.map +1 -0
  99. package/dist/core/collector/seq.d.ts +14 -0
  100. package/dist/core/collector/seq.d.ts.map +1 -0
  101. package/dist/core/collector/seq.js +20 -0
  102. package/dist/core/collector/seq.js.map +1 -0
  103. package/dist/core/collector/store.d.ts +16 -0
  104. package/dist/core/collector/store.d.ts.map +1 -0
  105. package/dist/core/collector/store.js +30 -0
  106. package/dist/core/collector/store.js.map +1 -0
  107. package/dist/core/collector/transcript.d.ts +38 -0
  108. package/dist/core/collector/transcript.d.ts.map +1 -0
  109. package/dist/core/collector/transcript.js +110 -0
  110. package/dist/core/collector/transcript.js.map +1 -0
  111. package/dist/core/event-bus.d.ts +9 -0
  112. package/dist/core/event-bus.d.ts.map +1 -0
  113. package/dist/core/event-bus.js +25 -0
  114. package/dist/core/event-bus.js.map +1 -0
  115. package/dist/core/example-seed.d.ts +34 -0
  116. package/dist/core/example-seed.d.ts.map +1 -0
  117. package/dist/core/example-seed.js +277 -0
  118. package/dist/core/example-seed.js.map +1 -0
  119. package/dist/core/inject/claude-settings.d.ts +34 -0
  120. package/dist/core/inject/claude-settings.d.ts.map +1 -0
  121. package/dist/core/inject/claude-settings.js +152 -0
  122. package/dist/core/inject/claude-settings.js.map +1 -0
  123. package/dist/core/inject/mcp-config.d.ts +26 -0
  124. package/dist/core/inject/mcp-config.d.ts.map +1 -0
  125. package/dist/core/inject/mcp-config.js +39 -0
  126. package/dist/core/inject/mcp-config.js.map +1 -0
  127. package/dist/core/inject/retention.d.ts +66 -0
  128. package/dist/core/inject/retention.d.ts.map +1 -0
  129. package/dist/core/inject/retention.js +119 -0
  130. package/dist/core/inject/retention.js.map +1 -0
  131. package/dist/core/inject/system-prompt.d.ts +11 -0
  132. package/dist/core/inject/system-prompt.d.ts.map +1 -0
  133. package/dist/core/inject/system-prompt.js +22 -0
  134. package/dist/core/inject/system-prompt.js.map +1 -0
  135. package/dist/core/macro-runner.d.ts +36 -0
  136. package/dist/core/macro-runner.d.ts.map +1 -0
  137. package/dist/core/macro-runner.js +66 -0
  138. package/dist/core/macro-runner.js.map +1 -0
  139. package/dist/core/macros.d.ts +10 -0
  140. package/dist/core/macros.d.ts.map +1 -0
  141. package/dist/core/macros.js +59 -0
  142. package/dist/core/macros.js.map +1 -0
  143. package/dist/core/path-safety.d.ts +33 -0
  144. package/dist/core/path-safety.d.ts.map +1 -0
  145. package/dist/core/path-safety.js +63 -0
  146. package/dist/core/path-safety.js.map +1 -0
  147. package/dist/core/paths.d.ts +27 -0
  148. package/dist/core/paths.d.ts.map +1 -0
  149. package/dist/core/paths.js +43 -0
  150. package/dist/core/paths.js.map +1 -0
  151. package/dist/core/port-detector.d.ts +69 -0
  152. package/dist/core/port-detector.d.ts.map +1 -0
  153. package/dist/core/port-detector.js +121 -0
  154. package/dist/core/port-detector.js.map +1 -0
  155. package/dist/core/session-manager.d.ts +275 -0
  156. package/dist/core/session-manager.d.ts.map +1 -0
  157. package/dist/core/session-manager.js +729 -0
  158. package/dist/core/session-manager.js.map +1 -0
  159. package/dist/core/task-manager.d.ts +138 -0
  160. package/dist/core/task-manager.d.ts.map +1 -0
  161. package/dist/core/task-manager.js +286 -0
  162. package/dist/core/task-manager.js.map +1 -0
  163. package/dist/core/task-stream.d.ts +32 -0
  164. package/dist/core/task-stream.d.ts.map +1 -0
  165. package/dist/core/task-stream.js +87 -0
  166. package/dist/core/task-stream.js.map +1 -0
  167. package/dist/core/workflow-registry.d.ts +36 -0
  168. package/dist/core/workflow-registry.d.ts.map +1 -0
  169. package/dist/core/workflow-registry.js +220 -0
  170. package/dist/core/workflow-registry.js.map +1 -0
  171. package/dist/core/workspace-context.d.ts +43 -0
  172. package/dist/core/workspace-context.d.ts.map +1 -0
  173. package/dist/core/workspace-context.js +108 -0
  174. package/dist/core/workspace-context.js.map +1 -0
  175. package/dist/core/workspace-watcher.d.ts +26 -0
  176. package/dist/core/workspace-watcher.d.ts.map +1 -0
  177. package/dist/core/workspace-watcher.js +174 -0
  178. package/dist/core/workspace-watcher.js.map +1 -0
  179. package/dist/index.d.ts +6 -0
  180. package/dist/index.d.ts.map +1 -0
  181. package/dist/index.js +6 -0
  182. package/dist/index.js.map +1 -0
  183. package/dist/profiles/canvas-guidelines.d.ts +20 -0
  184. package/dist/profiles/canvas-guidelines.d.ts.map +1 -0
  185. package/dist/profiles/canvas-guidelines.js +48 -0
  186. package/dist/profiles/canvas-guidelines.js.map +1 -0
  187. package/dist/profiles/default.d.ts +10 -0
  188. package/dist/profiles/default.d.ts.map +1 -0
  189. package/dist/profiles/default.js +70 -0
  190. package/dist/profiles/default.js.map +1 -0
  191. package/dist/server/auth.d.ts +9 -0
  192. package/dist/server/auth.d.ts.map +1 -0
  193. package/dist/server/auth.js +28 -0
  194. package/dist/server/auth.js.map +1 -0
  195. package/dist/server/canvas-render.d.ts +18 -0
  196. package/dist/server/canvas-render.d.ts.map +1 -0
  197. package/dist/server/canvas-render.js +24 -0
  198. package/dist/server/canvas-render.js.map +1 -0
  199. package/dist/server/canvas.d.ts +12 -0
  200. package/dist/server/canvas.d.ts.map +1 -0
  201. package/dist/server/canvas.js +174 -0
  202. package/dist/server/canvas.js.map +1 -0
  203. package/dist/server/events-ws.d.ts +11 -0
  204. package/dist/server/events-ws.d.ts.map +1 -0
  205. package/dist/server/events-ws.js +24 -0
  206. package/dist/server/events-ws.js.map +1 -0
  207. package/dist/server/fs.d.ts +4 -0
  208. package/dist/server/fs.d.ts.map +1 -0
  209. package/dist/server/fs.js +88 -0
  210. package/dist/server/fs.js.map +1 -0
  211. package/dist/server/index.d.ts +83 -0
  212. package/dist/server/index.d.ts.map +1 -0
  213. package/dist/server/index.js +0 -0
  214. package/dist/server/index.js.map +1 -0
  215. package/dist/server/ingest.d.ts +74 -0
  216. package/dist/server/ingest.d.ts.map +1 -0
  217. package/dist/server/ingest.js +74 -0
  218. package/dist/server/ingest.js.map +1 -0
  219. package/dist/server/macros.d.ts +32 -0
  220. package/dist/server/macros.d.ts.map +1 -0
  221. package/dist/server/macros.js +76 -0
  222. package/dist/server/macros.js.map +1 -0
  223. package/dist/server/rest.d.ts +75 -0
  224. package/dist/server/rest.d.ts.map +1 -0
  225. package/dist/server/rest.js +235 -0
  226. package/dist/server/rest.js.map +1 -0
  227. package/dist/server/static.d.ts +8 -0
  228. package/dist/server/static.d.ts.map +1 -0
  229. package/dist/server/static.js +36 -0
  230. package/dist/server/static.js.map +1 -0
  231. package/dist/server/terminal-ws.d.ts +9 -0
  232. package/dist/server/terminal-ws.d.ts.map +1 -0
  233. package/dist/server/terminal-ws.js +59 -0
  234. package/dist/server/terminal-ws.js.map +1 -0
  235. package/dist/server/ws-router.d.ts +16 -0
  236. package/dist/server/ws-router.d.ts.map +1 -0
  237. package/dist/server/ws-router.js +26 -0
  238. package/dist/server/ws-router.js.map +1 -0
  239. package/dist/shared/types.d.ts +485 -0
  240. package/dist/shared/types.d.ts.map +1 -0
  241. package/dist/shared/types.js +80 -0
  242. package/dist/shared/types.js.map +1 -0
  243. package/dist/web/assets/index-B9R4TzH7.css +32 -0
  244. package/dist/web/assets/index-Dh97QAgq.js +192 -0
  245. package/dist/web/index.html +27 -0
  246. package/package.json +84 -7
@@ -0,0 +1,75 @@
1
+ import * as fs from "node:fs/promises";
2
+ import * as path from "node:path";
3
+ import * as crypto from "node:crypto";
4
+ import { HARNESS_PATHS } from "../shared/types.js";
5
+ import { expandHome } from "./paths.js";
6
+ const UUID_SHAPE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
7
+ /**
8
+ * Stable anonymous install id, used to tag every analytics event
9
+ * (`AnalyticsEvent.machineId`) regardless of which Sapiom user is signed in.
10
+ * Created once on first run and reused forever after.
11
+ *
12
+ * Hardened per `@sapiom/analytics-core`'s `identity.ts` pattern: private
13
+ * file permissions (mode 0600, on both create and a pre-existing file),
14
+ * silent regeneration when the stored content is missing/corrupt, and
15
+ * graceful degradation — an unreadable/unwritable HOME must never crash
16
+ * server boot over an analytics nicety. Kept as our own plain-text
17
+ * `~/.sapiom/harness/machine-id` file for now; converging onto her
18
+ * `~/.sapiom/analytics.json` identity is a separate, later conversation.
19
+ *
20
+ * @param filePath Where the id file lives. Defaults to the real
21
+ * `HARNESS_PATHS.machineId`; tests and scripted checks pass a path under
22
+ * their scratch state root so a boot never writes to the real home dir.
23
+ */
24
+ export async function getOrCreateMachineId(filePath = expandHome(HARNESS_PATHS.machineId)) {
25
+ try {
26
+ const existing = await readMachineId(filePath);
27
+ if (existing) {
28
+ // Re-assert privacy even when we're not writing: a file created by a
29
+ // pre-hardening version of the harness (default `fs.writeFile` perms,
30
+ // typically 0644) would otherwise stay loosely-permissioned forever,
31
+ // since its content never needs to change again once it exists.
32
+ await hardenPermissions(filePath);
33
+ return existing;
34
+ }
35
+ const id = crypto.randomUUID();
36
+ await writeMachineId(filePath, id);
37
+ return id;
38
+ }
39
+ catch {
40
+ // Persistence is impossible (unwritable/unreadable HOME, permissions,
41
+ // whatever) — hand back a fresh id for this process rather than taking
42
+ // the server down over it. It won't be stable across runs, but every
43
+ // event within this run is at least internally consistent.
44
+ return crypto.randomUUID();
45
+ }
46
+ }
47
+ /** Reads and validates the persisted id. Missing, empty, or malformed
48
+ * content returns `undefined` so the caller regenerates — same recovery
49
+ * posture as `identity.ts`'s corrupt/wrong-shape handling. */
50
+ async function readMachineId(filePath) {
51
+ try {
52
+ const raw = (await fs.readFile(filePath, "utf-8")).trim();
53
+ return UUID_SHAPE.test(raw) ? raw : undefined;
54
+ }
55
+ catch {
56
+ return undefined;
57
+ }
58
+ }
59
+ async function writeMachineId(filePath, id) {
60
+ await fs.mkdir(path.dirname(filePath), { recursive: true, mode: 0o700 });
61
+ // `mode` on writeFile only applies when creating the file — the follow-up
62
+ // hardenPermissions() covers the (unlikely but possible) case of writing
63
+ // over a pre-existing file at this path with looser permissions.
64
+ await fs.writeFile(filePath, id + "\n", { mode: 0o600 });
65
+ await hardenPermissions(filePath);
66
+ }
67
+ async function hardenPermissions(filePath) {
68
+ try {
69
+ await fs.chmod(filePath, 0o600);
70
+ }
71
+ catch {
72
+ // Best effort.
73
+ }
74
+ }
75
+ //# sourceMappingURL=machine-id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"machine-id.js","sourceRoot":"","sources":["../../src/cli/machine-id.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,MAAM,UAAU,GAAG,iEAAiE,CAAC;AAErF;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,WAAmB,UAAU,CAAC,aAAa,CAAC,SAAS,CAAC;IAEtD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,QAAQ,CAAC,CAAC;QAC/C,IAAI,QAAQ,EAAE,CAAC;YACb,qEAAqE;YACrE,sEAAsE;YACtE,qEAAqE;YACrE,gEAAgE;YAChE,MAAM,iBAAiB,CAAC,QAAQ,CAAC,CAAC;YAClC,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,MAAM,EAAE,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;QAC/B,MAAM,cAAc,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QACnC,OAAO,EAAE,CAAC;IACZ,CAAC;IAAC,MAAM,CAAC;QACP,sEAAsE;QACtE,uEAAuE;QACvE,qEAAqE;QACrE,2DAA2D;QAC3D,OAAO,MAAM,CAAC,UAAU,EAAE,CAAC;IAC7B,CAAC;AACH,CAAC;AAED;;+DAE+D;AAC/D,KAAK,UAAU,aAAa,CAAC,QAAgB;IAC3C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC1D,OAAO,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,KAAK,UAAU,cAAc,CAAC,QAAgB,EAAE,EAAU;IACxD,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACzE,0EAA0E;IAC1E,yEAAyE;IACzE,iEAAiE;IACjE,MAAM,EAAE,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,GAAG,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACzD,MAAM,iBAAiB,CAAC,QAAQ,CAAC,CAAC;AACpC,CAAC;AAED,KAAK,UAAU,iBAAiB,CAAC,QAAgB;IAC/C,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,eAAe;IACjB,CAAC;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ /** Expand a leading `~` (as used by HARNESS_PATHS) to the user's home directory. */
2
+ export declare function expandHome(p: string): string;
3
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/cli/paths.ts"],"names":[],"mappings":"AAGA,oFAAoF;AACpF,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAI5C"}
@@ -0,0 +1,11 @@
1
+ import * as os from "node:os";
2
+ import * as path from "node:path";
3
+ /** Expand a leading `~` (as used by HARNESS_PATHS) to the user's home directory. */
4
+ export function expandHome(p) {
5
+ if (p === "~")
6
+ return os.homedir();
7
+ if (p.startsWith("~/"))
8
+ return path.join(os.homedir(), p.slice(2));
9
+ return p;
10
+ }
11
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/cli/paths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC,oFAAoF;AACpF,MAAM,UAAU,UAAU,CAAC,CAAS;IAClC,IAAI,CAAC,KAAK,GAAG;QAAE,OAAO,EAAE,CAAC,OAAO,EAAE,CAAC;IACnC,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACnE,OAAO,CAAC,CAAC;AACX,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { type HarnessSettings } from "../shared/types.js";
2
+ /**
3
+ * Validates, normalizes, case-sensitively dedupes (first occurrence wins —
4
+ * callers order newest-first), and caps a candidate recent-dirs list.
5
+ * Applied on every write (via saveSettings) and on every read (via
6
+ * loadSettings), so junk already on disk self-heals the next time it's
7
+ * loaded instead of requiring a migration.
8
+ */
9
+ export declare function sanitizeRecentDirs(candidates: string[]): Promise<string[]>;
10
+ /** Whether settings have ever been persisted — used to detect first run. */
11
+ export declare function hasStoredSettings(settingsPath?: string): Promise<boolean>;
12
+ export declare function loadSettings(settingsPath?: string): Promise<HarnessSettings>;
13
+ export declare function saveSettings(settings: HarnessSettings, settingsPath?: string): Promise<void>;
14
+ /** Record `cwd` as the most-recently-used project directory (validated, deduped, capped). */
15
+ export declare function recordRecentDir(cwd: string, settingsPath?: string): Promise<void>;
16
+ /**
17
+ * Boot-time hygiene: persist the removal of recent-dir entries whose path no
18
+ * longer exists on disk. sanitizeRecentDirs() already drops them from every
19
+ * in-memory read, but a read alone never rewrites the file — so a dead entry
20
+ * (deleted project, a crashed check's temp dir) would otherwise sit in
21
+ * settings.json indefinitely. Returns the entries that were dropped; leaves
22
+ * the file completely untouched (not even created) when there is nothing to
23
+ * prune, so this can never break first-run detection (hasStoredSettings).
24
+ */
25
+ export declare function pruneDeadRecentDirs(settingsPath?: string): Promise<string[]>;
26
+ //# sourceMappingURL=settings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/cli/settings.ts"],"names":[],"mappings":"AAEA,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAqCzE;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAWhF;AAED,4EAA4E;AAC5E,wBAAsB,iBAAiB,CAAC,YAAY,GAAE,MAAkC,GAAG,OAAO,CAAC,OAAO,CAAC,CAO1G;AAED,wBAAsB,YAAY,CAAC,YAAY,GAAE,MAAkC,GAAG,OAAO,CAAC,eAAe,CAAC,CAQ7G;AAED,wBAAsB,YAAY,CAChC,QAAQ,EAAE,eAAe,EACzB,YAAY,GAAE,MAAkC,GAC/C,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,6FAA6F;AAC7F,wBAAsB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,GAAE,MAAkC,GAAG,OAAO,CAAC,IAAI,CAAC,CAGlH;AAED;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,CAAC,YAAY,GAAE,MAAkC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAkB7G"}
@@ -0,0 +1,123 @@
1
+ import * as fs from "node:fs/promises";
2
+ import * as path from "node:path";
3
+ import { HARNESS_PATHS } from "../shared/types.js";
4
+ import { expandHome } from "./paths.js";
5
+ const DEFAULT_SETTINGS = {
6
+ telemetryOptIn: false,
7
+ recentDirs: [],
8
+ };
9
+ const MAX_RECENT_DIRS = 8;
10
+ /** The real settings file. Every function below accepts an explicit path so
11
+ * tests and scripted checks can point at a scratch state root instead —
12
+ * omitted, they operate on the real file, unchanged for CLI users. */
13
+ function defaultSettingsFilePath() {
14
+ return expandHome(HARNESS_PATHS.settings);
15
+ }
16
+ /**
17
+ * Resolves a candidate recent-dir entry to an absolute, existing directory
18
+ * path, or null if it doesn't qualify. Rejects anything that isn't a real
19
+ * directory on disk right now — relative paths, stray free text (e.g. typed
20
+ * into the directory field by mistake), and directories that have since been
21
+ * deleted all fall out here rather than accumulating in the list.
22
+ */
23
+ async function normalizeRecentDir(candidate) {
24
+ const expanded = expandHome(candidate.trim());
25
+ if (!path.isAbsolute(expanded))
26
+ return null;
27
+ const resolved = path.resolve(expanded);
28
+ try {
29
+ const stat = await fs.stat(resolved);
30
+ if (!stat.isDirectory())
31
+ return null;
32
+ }
33
+ catch {
34
+ return null;
35
+ }
36
+ return resolved;
37
+ }
38
+ /**
39
+ * Validates, normalizes, case-sensitively dedupes (first occurrence wins —
40
+ * callers order newest-first), and caps a candidate recent-dirs list.
41
+ * Applied on every write (via saveSettings) and on every read (via
42
+ * loadSettings), so junk already on disk self-heals the next time it's
43
+ * loaded instead of requiring a migration.
44
+ */
45
+ export async function sanitizeRecentDirs(candidates) {
46
+ const seen = new Set();
47
+ const result = [];
48
+ for (const candidate of candidates) {
49
+ if (result.length >= MAX_RECENT_DIRS)
50
+ break;
51
+ const normalized = await normalizeRecentDir(candidate);
52
+ if (normalized === null || seen.has(normalized))
53
+ continue;
54
+ seen.add(normalized);
55
+ result.push(normalized);
56
+ }
57
+ return result;
58
+ }
59
+ /** Whether settings have ever been persisted — used to detect first run. */
60
+ export async function hasStoredSettings(settingsPath = defaultSettingsFilePath()) {
61
+ try {
62
+ await fs.access(settingsPath);
63
+ return true;
64
+ }
65
+ catch {
66
+ return false;
67
+ }
68
+ }
69
+ export async function loadSettings(settingsPath = defaultSettingsFilePath()) {
70
+ try {
71
+ const raw = await fs.readFile(settingsPath, "utf-8");
72
+ const parsed = { ...DEFAULT_SETTINGS, ...JSON.parse(raw) };
73
+ return { ...parsed, recentDirs: await sanitizeRecentDirs(parsed.recentDirs) };
74
+ }
75
+ catch {
76
+ return { ...DEFAULT_SETTINGS };
77
+ }
78
+ }
79
+ export async function saveSettings(settings, settingsPath = defaultSettingsFilePath()) {
80
+ const sanitized = {
81
+ ...settings,
82
+ recentDirs: await sanitizeRecentDirs(settings.recentDirs),
83
+ };
84
+ await fs.mkdir(path.dirname(settingsPath), { recursive: true });
85
+ await fs.writeFile(settingsPath, JSON.stringify(sanitized, null, 2) + "\n");
86
+ }
87
+ /** Record `cwd` as the most-recently-used project directory (validated, deduped, capped). */
88
+ export async function recordRecentDir(cwd, settingsPath = defaultSettingsFilePath()) {
89
+ const settings = await loadSettings(settingsPath);
90
+ await saveSettings({ ...settings, recentDirs: [cwd, ...settings.recentDirs] }, settingsPath);
91
+ }
92
+ /**
93
+ * Boot-time hygiene: persist the removal of recent-dir entries whose path no
94
+ * longer exists on disk. sanitizeRecentDirs() already drops them from every
95
+ * in-memory read, but a read alone never rewrites the file — so a dead entry
96
+ * (deleted project, a crashed check's temp dir) would otherwise sit in
97
+ * settings.json indefinitely. Returns the entries that were dropped; leaves
98
+ * the file completely untouched (not even created) when there is nothing to
99
+ * prune, so this can never break first-run detection (hasStoredSettings).
100
+ */
101
+ export async function pruneDeadRecentDirs(settingsPath = defaultSettingsFilePath()) {
102
+ let stored;
103
+ try {
104
+ stored = JSON.parse(await fs.readFile(settingsPath, "utf-8"));
105
+ }
106
+ catch {
107
+ return []; // no settings file yet (or unreadable) — nothing to prune
108
+ }
109
+ const recentDirs = Array.isArray(stored.recentDirs)
110
+ ? stored.recentDirs.filter((entry) => typeof entry === "string")
111
+ : [];
112
+ const dead = [];
113
+ for (const entry of recentDirs) {
114
+ if ((await normalizeRecentDir(entry)) === null)
115
+ dead.push(entry);
116
+ }
117
+ if (dead.length === 0)
118
+ return [];
119
+ // saveSettings' own sanitize pass is what actually drops the dead entries.
120
+ await saveSettings({ ...DEFAULT_SETTINGS, ...stored, recentDirs }, settingsPath);
121
+ return dead;
122
+ }
123
+ //# sourceMappingURL=settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/cli/settings.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * claude-code adapter — launches/resumes the `claude` CLI and scans its
3
+ * transcript store for resumable history. All Sapiom-ness is injected via
4
+ * flags (settings/mcp-config/system-prompt); nothing here mutates the user's
5
+ * own `~/.claude` config.
6
+ */
7
+ import type { DoctorCheck, HarnessAdapter, LaunchOpts, SessionSummary, SpawnSpec } from "../../shared/types.js";
8
+ export interface ClaudeCodeAdapterOptions {
9
+ /** Overridable for tests (e.g. spawn `bash` instead of a real, auth-gated `claude`). */
10
+ binary?: string;
11
+ /** Overridable for tests. Defaults to the real home directory. */
12
+ homeDir?: string;
13
+ }
14
+ export declare class ClaudeCodeAdapter implements HarnessAdapter {
15
+ readonly id: "claude-code";
16
+ readonly eventSource: "hooks";
17
+ private readonly binary;
18
+ private readonly homeDir;
19
+ constructor(options?: ClaudeCodeAdapterOptions);
20
+ doctor(): Promise<DoctorCheck[]>;
21
+ launch(opts: LaunchOpts): SpawnSpec;
22
+ resume(agentSessionId: string, opts: LaunchOpts): SpawnSpec;
23
+ /**
24
+ * Headless one-shot run for TaskManager (see HarnessAdapter.launchTask).
25
+ * Verified against a real `claude` binary: `-p` carries the exact same
26
+ * --settings/--mcp-config/--append-system-prompt injection as launch()
27
+ * (all six hooks fire), skips the trust dialog entirely, and exits on its
28
+ * own when the turn completes. The extra flags:
29
+ * - --permission-mode acceptEdits: a headless task has no human to click
30
+ * through a permission prompt — without it a tool call hangs forever.
31
+ * - --output-format stream-json --verbose: line-oriented JSON progress on
32
+ * stdout (parsed by core/task-stream.ts) instead of a bare final answer.
33
+ * - --model / --max-turns: only when the caller sets them — a bounded task
34
+ * (canvas enrichment) pins a cheaper model and a hard turn cap instead of
35
+ * inheriting the user's interactive defaults.
36
+ */
37
+ launchTask(opts: LaunchOpts): SpawnSpec;
38
+ listPastSessions(cwd: string): Promise<SessionSummary[]>;
39
+ }
40
+ export declare function createClaudeCodeAdapter(options?: ClaudeCodeAdapterOptions): HarnessAdapter;
41
+ //# sourceMappingURL=claude-code.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code.d.ts","sourceRoot":"","sources":["../../../src/core/adapters/claude-code.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,OAAO,KAAK,EACV,WAAW,EACX,cAAc,EACd,UAAU,EACV,cAAc,EACd,SAAS,EACV,MAAM,uBAAuB,CAAC;AAQ/B,MAAM,WAAW,wBAAwB;IACvC,wFAAwF;IACxF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAuGD,qBAAa,iBAAkB,YAAW,cAAc;IACtD,QAAQ,CAAC,EAAE,EAAG,aAAa,CAAU;IACrC,QAAQ,CAAC,WAAW,EAAG,OAAO,CAAU;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,OAAO,GAAE,wBAA6B;IAK5C,MAAM,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAetC,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS;IAgBnC,MAAM,CAAC,cAAc,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,SAAS;IAa3D;;;;;;;;;;;;;OAaG;IACH,UAAU,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS;IAmBjC,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;CA8B/D;AAcD,wBAAgB,uBAAuB,CAAC,OAAO,CAAC,EAAE,wBAAwB,GAAG,cAAc,CAE1F"}
@@ -0,0 +1,238 @@
1
+ /**
2
+ * claude-code adapter — launches/resumes the `claude` CLI and scans its
3
+ * transcript store for resumable history. All Sapiom-ness is injected via
4
+ * flags (settings/mcp-config/system-prompt); nothing here mutates the user's
5
+ * own `~/.claude` config.
6
+ */
7
+ import { execFile } from "node:child_process";
8
+ import { readFileSync } from "node:fs";
9
+ import { readFile, readdir, stat, open } from "node:fs/promises";
10
+ import { homedir } from "node:os";
11
+ import { basename, join } from "node:path";
12
+ import { promisify } from "node:util";
13
+ const execFileAsync = promisify(execFile);
14
+ /** Bytes read from the tail of a transcript file. Files can be 100MB+; the
15
+ * summary/title we need is always near the end. */
16
+ const TRANSCRIPT_TAIL_BYTES = 65536;
17
+ /**
18
+ * Claude Code stores transcripts at `~/.claude/projects/<encoded-cwd>/*.jsonl`.
19
+ * The encoding strips the leading `/` and replaces `/` and `.` with `-`.
20
+ * If Claude Code changes this scheme, discovery silently returns no history
21
+ * (listPastSessions degrades to an empty list, not an error).
22
+ */
23
+ function encodeProjectPath(cwd) {
24
+ const normalized = cwd.replace(/\\/g, "/");
25
+ return normalized.replace(/:/g, "").replace(/[/.]/g, "-");
26
+ }
27
+ function extractTextFromContent(content) {
28
+ if (typeof content === "string")
29
+ return content;
30
+ if (Array.isArray(content)) {
31
+ const texts = content
32
+ .map((block) => typeof block === "object" && block !== null && "text" in block
33
+ ? block.text
34
+ : undefined)
35
+ .filter((text) => typeof text === "string");
36
+ return texts.length > 0 ? texts.join(" ") : undefined;
37
+ }
38
+ return undefined;
39
+ }
40
+ /** Read only the tail of a (possibly huge) JSONL transcript file. */
41
+ async function readTranscriptTail(filePath, maxBytes = TRANSCRIPT_TAIL_BYTES) {
42
+ let content;
43
+ let startedMidFile = false;
44
+ try {
45
+ const { size } = await stat(filePath);
46
+ if (size <= maxBytes) {
47
+ content = await readFile(filePath, "utf8");
48
+ }
49
+ else {
50
+ const offset = size - maxBytes;
51
+ const handle = await open(filePath, "r");
52
+ try {
53
+ const buffer = Buffer.allocUnsafe(maxBytes);
54
+ await handle.read(buffer, 0, maxBytes, offset);
55
+ content = buffer.toString("utf8");
56
+ startedMidFile = true;
57
+ }
58
+ finally {
59
+ await handle.close();
60
+ }
61
+ }
62
+ }
63
+ catch {
64
+ return [];
65
+ }
66
+ // A tail read may start mid-line; drop the (possibly truncated) first line.
67
+ const firstNewline = content.indexOf("\n");
68
+ const safeContent = startedMidFile && firstNewline >= 0 ? content.slice(firstNewline + 1) : content;
69
+ const entries = [];
70
+ for (const line of safeContent.split("\n")) {
71
+ const trimmed = line.trim();
72
+ if (!trimmed)
73
+ continue;
74
+ try {
75
+ const parsed = JSON.parse(trimmed);
76
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
77
+ entries.push(parsed);
78
+ }
79
+ }
80
+ catch {
81
+ // Skip malformed/truncated lines (expected at the start of a tail read).
82
+ }
83
+ }
84
+ return entries;
85
+ }
86
+ function extractTitle(entries, fallback) {
87
+ for (let i = entries.length - 1; i >= 0; i--) {
88
+ const entry = entries[i];
89
+ if (entry?.type === "summary" && typeof entry.summary === "string" && entry.summary.trim()) {
90
+ return entry.summary.trim();
91
+ }
92
+ }
93
+ for (const entry of entries) {
94
+ if (entry?.type !== "user")
95
+ continue;
96
+ const text = extractTextFromContent(entry.message?.content)?.trim();
97
+ if (text)
98
+ return text.length > 120 ? `${text.slice(0, 120)}...` : text;
99
+ }
100
+ return fallback;
101
+ }
102
+ function buildConfigArgs(opts) {
103
+ const args = [];
104
+ if (opts.settingsFile)
105
+ args.push("--settings", opts.settingsFile);
106
+ if (opts.mcpConfigFile)
107
+ args.push("--mcp-config", opts.mcpConfigFile);
108
+ return args;
109
+ }
110
+ export class ClaudeCodeAdapter {
111
+ constructor(options = {}) {
112
+ this.id = "claude-code";
113
+ this.eventSource = "hooks";
114
+ this.binary = options.binary ?? "claude";
115
+ this.homeDir = options.homeDir ?? homedir();
116
+ }
117
+ async doctor() {
118
+ try {
119
+ const { stdout } = await execFileAsync(this.binary, ["--version"], { timeout: 5000 });
120
+ return [{ name: "claude", ok: true, detail: stdout.trim() || "installed" }];
121
+ }
122
+ catch {
123
+ return [
124
+ {
125
+ name: "claude",
126
+ ok: false,
127
+ detail: `\`${this.binary}\` not found on PATH. Install Claude Code: https://docs.claude.com/en/docs/claude-code/setup`,
128
+ },
129
+ ];
130
+ }
131
+ }
132
+ launch(opts) {
133
+ const args = buildConfigArgs(opts);
134
+ if (opts.systemPromptFile) {
135
+ args.push("--append-system-prompt", readPromptFile(opts.systemPromptFile));
136
+ }
137
+ return {
138
+ command: this.binary,
139
+ args,
140
+ // Nested-agent conflict: Claude Code refuses to run "inside itself" if
141
+ // CLAUDECODE is already set, which it will be if the harness server
142
+ // itself was launched from within a Claude Code session.
143
+ env: { CLAUDECODE: null },
144
+ cwd: opts.cwd,
145
+ };
146
+ }
147
+ resume(agentSessionId, opts) {
148
+ const args = ["--resume", agentSessionId, ...buildConfigArgs(opts)];
149
+ if (opts.systemPromptFile) {
150
+ args.push("--append-system-prompt", readPromptFile(opts.systemPromptFile));
151
+ }
152
+ return {
153
+ command: this.binary,
154
+ args,
155
+ env: { CLAUDECODE: null },
156
+ cwd: opts.cwd,
157
+ };
158
+ }
159
+ /**
160
+ * Headless one-shot run for TaskManager (see HarnessAdapter.launchTask).
161
+ * Verified against a real `claude` binary: `-p` carries the exact same
162
+ * --settings/--mcp-config/--append-system-prompt injection as launch()
163
+ * (all six hooks fire), skips the trust dialog entirely, and exits on its
164
+ * own when the turn completes. The extra flags:
165
+ * - --permission-mode acceptEdits: a headless task has no human to click
166
+ * through a permission prompt — without it a tool call hangs forever.
167
+ * - --output-format stream-json --verbose: line-oriented JSON progress on
168
+ * stdout (parsed by core/task-stream.ts) instead of a bare final answer.
169
+ * - --model / --max-turns: only when the caller sets them — a bounded task
170
+ * (canvas enrichment) pins a cheaper model and a hard turn cap instead of
171
+ * inheriting the user's interactive defaults.
172
+ */
173
+ launchTask(opts) {
174
+ if (!opts.prompt) {
175
+ throw new Error("claude-code adapter: launchTask requires opts.prompt");
176
+ }
177
+ const args = ["-p", opts.prompt, ...buildConfigArgs(opts)];
178
+ if (opts.systemPromptFile) {
179
+ args.push("--append-system-prompt", readPromptFile(opts.systemPromptFile));
180
+ }
181
+ if (opts.model)
182
+ args.push("--model", opts.model);
183
+ if (opts.maxTurns != null)
184
+ args.push("--max-turns", String(opts.maxTurns));
185
+ args.push("--permission-mode", "acceptEdits", "--output-format", "stream-json", "--verbose");
186
+ return {
187
+ command: this.binary,
188
+ args,
189
+ env: { CLAUDECODE: null },
190
+ cwd: opts.cwd,
191
+ };
192
+ }
193
+ async listPastSessions(cwd) {
194
+ const projectDir = join(this.homeDir, ".claude", "projects", encodeProjectPath(cwd));
195
+ let entries;
196
+ try {
197
+ entries = await readdir(projectDir);
198
+ }
199
+ catch {
200
+ return [];
201
+ }
202
+ const summaries = [];
203
+ for (const file of entries) {
204
+ if (!file.endsWith(".jsonl"))
205
+ continue;
206
+ const filePath = join(projectDir, file);
207
+ const agentSessionId = basename(file, ".jsonl");
208
+ const transcript = await readTranscriptTail(filePath);
209
+ if (transcript.length === 0)
210
+ continue;
211
+ const fileStat = await stat(filePath).catch(() => undefined);
212
+ const lastActiveAt = fileStat ? fileStat.mtime.toISOString() : new Date(0).toISOString();
213
+ summaries.push({
214
+ agentSessionId,
215
+ harness: "claude-code",
216
+ cwd,
217
+ title: extractTitle(transcript, agentSessionId),
218
+ lastActiveAt,
219
+ source: "transcript",
220
+ });
221
+ }
222
+ return summaries.sort((a, b) => (a.lastActiveAt < b.lastActiveAt ? 1 : -1));
223
+ }
224
+ }
225
+ function readPromptFile(path) {
226
+ // launch()/resume() are synchronous per the adapter contract, so this reads
227
+ // synchronously; profile prompts are small (a few KB), read once at spawn time.
228
+ try {
229
+ return readFileSync(path, "utf8");
230
+ }
231
+ catch (err) {
232
+ throw new Error(`claude-code adapter: failed to read systemPromptFile "${path}": ${err.message}`);
233
+ }
234
+ }
235
+ export function createClaudeCodeAdapter(options) {
236
+ return new ClaudeCodeAdapter(options);
237
+ }
238
+ //# sourceMappingURL=claude-code.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-code.js","sourceRoot":"","sources":["../../../src/core/adapters/claude-code.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * codex adapter — launches/resumes the `codex` CLI and scans its rollout
3
+ * store for resumable history. Codex has no hook system (see
4
+ * core/collector/codex-tailer.ts for how its analytics eventSource works
5
+ * instead); this file only covers process launch/resume/doctor/history.
6
+ *
7
+ * Verified against a locally installed `codex-cli 0.134.0`: `codex resume
8
+ * [SESSION_ID] [PROMPT]` (positional UUID or thread name) via `codex resume
9
+ * --help`; the generic `-c key=value` config-override mechanism via `codex
10
+ * --help`; and, via real spawns with `--strict-config`, that both
11
+ * `developer_instructions` and `model_instructions_file` are real,
12
+ * recognized keys (an earlier version of this file used the latter and
13
+ * flagged both as unconfirmed — see buildConfigArgs below for why the
14
+ * adapter now uses `developer_instructions` instead: `model_instructions_file`
15
+ * makes codex's own startup depend on re-reading a file we already have the
16
+ * content of, and an unreadable path there kills the process instantly with
17
+ * no trust prompt, no TUI — precisely the "session has no live pty" a user
18
+ * sees with zero indication why).
19
+ */
20
+ import type { DoctorCheck, HarnessAdapter, LaunchOpts, SessionSummary, SpawnSpec } from "../../shared/types.js";
21
+ /** Strips ANSI/CSI/OSC control sequences a pty stream is otherwise full of,
22
+ * so text pattern-matching (e.g. `TRUST_PROMPT_PATTERN`) sees the words a
23
+ * human would actually read rather than raw terminal-control bytes. Not
24
+ * exhaustive of every escape sequence a TUI could emit, but covers what
25
+ * Codex's TUI is confirmed (via real captures) to use: CSI (cursor moves,
26
+ * SGR color/style) and OSC (window title) sequences. */
27
+ export declare function stripAnsi(text: string): string;
28
+ export interface CodexAdapterOptions {
29
+ /** Overridable for tests. */
30
+ binary?: string;
31
+ /** Overridable for tests. Defaults to the real home directory. */
32
+ homeDir?: string;
33
+ }
34
+ export declare class CodexAdapter implements HarnessAdapter {
35
+ readonly id: "codex";
36
+ readonly eventSource: "transcript-tail";
37
+ private readonly binary;
38
+ private readonly homeDir;
39
+ constructor(options?: CodexAdapterOptions);
40
+ doctor(): Promise<DoctorCheck[]>;
41
+ launch(opts: LaunchOpts): SpawnSpec;
42
+ resume(agentSessionId: string, opts: LaunchOpts): SpawnSpec;
43
+ /**
44
+ * See `HarnessAdapter.detectBlockingPrompt`. Codex needs this (Claude Code
45
+ * doesn't) because its rollout file — and therefore the SessionStart-
46
+ * equivalent event `SessionManager` otherwise waits on — isn't written
47
+ * until the *first* turn is actually submitted, confirmed empirically
48
+ * against a real `codex-cli 0.134.0`: an idle, fully-interactive session
49
+ * (trust prompt already accepted, composer visibly ready) produces zero
50
+ * files under `~/.codex/sessions` for as long as nothing is submitted.
51
+ * That real signal therefore can't arrive before the very first
52
+ * injection that needs it — a scrollback check is the only thing that
53
+ * can stand in for it up to that point.
54
+ */
55
+ detectBlockingPrompt(scrollback: string): boolean;
56
+ listPastSessions(cwd: string): Promise<SessionSummary[]>;
57
+ }
58
+ export declare function createCodexAdapter(options?: CodexAdapterOptions): HarnessAdapter;
59
+ //# sourceMappingURL=codex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codex.d.ts","sourceRoot":"","sources":["../../../src/core/adapters/codex.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AASH,OAAO,KAAK,EACV,WAAW,EACX,cAAc,EACd,UAAU,EACV,cAAc,EACd,SAAS,EACV,MAAM,uBAAuB,CAAC;AA0B/B;;;;;yDAKyD;AACzD,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAgB9C;AAED,MAAM,WAAW,mBAAmB;IAClC,6BAA6B;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAmGD,qBAAa,YAAa,YAAW,cAAc;IACjD,QAAQ,CAAC,EAAE,EAAG,OAAO,CAAU;IAC/B,QAAQ,CAAC,WAAW,EAAG,iBAAiB,CAAU;IAClD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,OAAO,GAAE,mBAAwB;IAKvC,MAAM,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAetC,MAAM,CAAC,IAAI,EAAE,UAAU,GAAG,SAAS;IAWnC,MAAM,CAAC,cAAc,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,SAAS;IAS3D;;;;;;;;;;;OAWG;IACH,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAI3C,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;CAwC/D;AA8DD,wBAAgB,kBAAkB,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,cAAc,CAEhF"}