negotium 0.1.7 → 0.1.9

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 (270) hide show
  1. package/README.md +26 -2
  2. package/dist/canonical-mcp-bridge.js +14 -0
  3. package/dist/canonical-mcp-bridge.js.map +9 -0
  4. package/dist/chunk-10ct8814.js +429 -0
  5. package/dist/chunk-10ct8814.js.map +11 -0
  6. package/dist/chunk-18fzftjf.js +214 -0
  7. package/dist/chunk-18fzftjf.js.map +10 -0
  8. package/dist/chunk-1k7t7bnx.js +97 -0
  9. package/dist/chunk-1k7t7bnx.js.map +10 -0
  10. package/dist/chunk-1msje2zf.js +23 -0
  11. package/dist/chunk-1msje2zf.js.map +10 -0
  12. package/dist/chunk-2dgkah1z.js +706 -0
  13. package/dist/chunk-2dgkah1z.js.map +14 -0
  14. package/dist/chunk-3t4dtm26.js +61 -0
  15. package/dist/chunk-3t4dtm26.js.map +10 -0
  16. package/dist/chunk-3vn2vnj1.js +51 -0
  17. package/dist/chunk-3vn2vnj1.js.map +10 -0
  18. package/dist/chunk-6hknphtc.js +2054 -0
  19. package/dist/chunk-6hknphtc.js.map +29 -0
  20. package/dist/chunk-6tfzpcvk.js +2149 -0
  21. package/dist/chunk-6tfzpcvk.js.map +15 -0
  22. package/dist/chunk-8g0c4as0.js +23 -0
  23. package/dist/chunk-8g0c4as0.js.map +10 -0
  24. package/dist/chunk-9spx3h9n.js +279 -0
  25. package/dist/chunk-9spx3h9n.js.map +10 -0
  26. package/dist/chunk-c500xe9z.js +33 -0
  27. package/dist/chunk-c500xe9z.js.map +10 -0
  28. package/dist/chunk-e9b2xpff.js +147 -0
  29. package/dist/chunk-e9b2xpff.js.map +10 -0
  30. package/dist/chunk-edb07sjp.js +567 -0
  31. package/dist/chunk-edb07sjp.js.map +14 -0
  32. package/dist/chunk-eexqsee7.js +368 -0
  33. package/dist/chunk-eexqsee7.js.map +11 -0
  34. package/dist/chunk-efyxk97v.js +500 -0
  35. package/dist/chunk-efyxk97v.js.map +13 -0
  36. package/dist/chunk-epqxwkvh.js +3534 -0
  37. package/dist/chunk-epqxwkvh.js.map +20 -0
  38. package/dist/chunk-f1pzh3xh.js +115 -0
  39. package/dist/chunk-f1pzh3xh.js.map +10 -0
  40. package/dist/chunk-f5xgprg1.js +59 -0
  41. package/dist/chunk-f5xgprg1.js.map +10 -0
  42. package/dist/chunk-f97qybwk.js +584 -0
  43. package/dist/chunk-f97qybwk.js.map +11 -0
  44. package/dist/chunk-fvc7ncxd.js +252 -0
  45. package/dist/chunk-fvc7ncxd.js.map +10 -0
  46. package/dist/chunk-g7pvfj5e.js +574 -0
  47. package/dist/chunk-g7pvfj5e.js.map +14 -0
  48. package/dist/chunk-h8vmb9gf.js +315 -0
  49. package/dist/chunk-h8vmb9gf.js.map +10 -0
  50. package/dist/chunk-hgw7n8cm.js +43 -0
  51. package/dist/chunk-hgw7n8cm.js.map +10 -0
  52. package/dist/chunk-hsq7j9jk.js +126 -0
  53. package/dist/chunk-hsq7j9jk.js.map +10 -0
  54. package/dist/chunk-hwy3n31w.js +21 -0
  55. package/dist/chunk-hwy3n31w.js.map +9 -0
  56. package/dist/chunk-j0kjscb7.js +53 -0
  57. package/dist/chunk-j0kjscb7.js.map +10 -0
  58. package/dist/chunk-jzz91jwh.js +70 -0
  59. package/dist/chunk-jzz91jwh.js.map +10 -0
  60. package/dist/chunk-k9z47mab.js +247 -0
  61. package/dist/chunk-k9z47mab.js.map +12 -0
  62. package/dist/chunk-kbm35t7w.js +1331 -0
  63. package/dist/chunk-kbm35t7w.js.map +16 -0
  64. package/dist/chunk-m4veth0c.js +1649 -0
  65. package/dist/chunk-m4veth0c.js.map +17 -0
  66. package/dist/chunk-mhty3nna.js +150 -0
  67. package/dist/chunk-mhty3nna.js.map +11 -0
  68. package/dist/chunk-mmgh670x.js +248 -0
  69. package/dist/chunk-mmgh670x.js.map +11 -0
  70. package/dist/chunk-n0kk5xbq.js +2358 -0
  71. package/dist/chunk-n0kk5xbq.js.map +26 -0
  72. package/dist/chunk-nmv41qx9.js +2847 -0
  73. package/dist/chunk-nmv41qx9.js.map +23 -0
  74. package/dist/chunk-nqdp52aj.js +113 -0
  75. package/dist/chunk-nqdp52aj.js.map +12 -0
  76. package/dist/chunk-ns2m25dz.js +139 -0
  77. package/dist/chunk-ns2m25dz.js.map +10 -0
  78. package/dist/chunk-pkdmypdd.js +147 -0
  79. package/dist/chunk-pkdmypdd.js.map +11 -0
  80. package/dist/chunk-rmyfmrrr.js +1634 -0
  81. package/dist/chunk-rmyfmrrr.js.map +15 -0
  82. package/dist/chunk-sa874j5c.js +9 -0
  83. package/dist/chunk-sa874j5c.js.map +10 -0
  84. package/dist/chunk-sgsz2fpq.js +289 -0
  85. package/dist/chunk-sgsz2fpq.js.map +10 -0
  86. package/dist/chunk-sn51wnf1.js +3020 -0
  87. package/dist/chunk-sn51wnf1.js.map +32 -0
  88. package/dist/chunk-sqhr1a7e.js +587 -0
  89. package/dist/chunk-sqhr1a7e.js.map +11 -0
  90. package/dist/chunk-tmj5r2zv.js +2933 -0
  91. package/dist/chunk-tmj5r2zv.js.map +20 -0
  92. package/dist/chunk-vefj3880.js +56 -0
  93. package/dist/chunk-vefj3880.js.map +10 -0
  94. package/dist/chunk-vp2db42d.js +121 -0
  95. package/dist/chunk-vp2db42d.js.map +10 -0
  96. package/dist/chunk-wry79q55.js +108 -0
  97. package/dist/chunk-wry79q55.js.map +10 -0
  98. package/dist/chunk-x1arr7ze.js +63 -0
  99. package/dist/chunk-x1arr7ze.js.map +10 -0
  100. package/dist/chunk-z2xy5n2q.js +103 -0
  101. package/dist/chunk-z2xy5n2q.js.map +10 -0
  102. package/dist/hosted-agent.js +53 -0
  103. package/dist/hosted-agent.js.map +10 -0
  104. package/dist/main.js +132 -20
  105. package/dist/main.js.map +4 -4
  106. package/dist/runtime/scripts/faster-whisper-wrapper.py +87 -0
  107. package/dist/runtime/scripts/mcp-patchright-http.mjs +349 -0
  108. package/dist/runtime/src/agents/api-topic-agent-switch.ts +178 -0
  109. package/dist/runtime/src/agents/archiver.ts +396 -0
  110. package/dist/runtime/src/agents/auth-check.ts +64 -0
  111. package/dist/runtime/src/agents/claude-provider.ts +737 -0
  112. package/dist/runtime/src/agents/claude-registry.ts +115 -0
  113. package/dist/runtime/src/agents/codex-native-multi-agent.ts +177 -0
  114. package/dist/runtime/src/agents/codex-provider.ts +785 -0
  115. package/dist/runtime/src/agents/codex-registry.ts +115 -0
  116. package/dist/runtime/src/agents/codex-tree-kill.ts +365 -0
  117. package/dist/runtime/src/agents/contracts.ts +69 -0
  118. package/dist/runtime/src/agents/deep-map.ts +13 -0
  119. package/dist/runtime/src/agents/execution-host.ts +91 -0
  120. package/dist/runtime/src/agents/fixtures/claude-attachments.jsonl +2 -0
  121. package/dist/runtime/src/agents/fixtures/codex-shell.jsonl +5 -0
  122. package/dist/runtime/src/agents/fork.ts +65 -0
  123. package/dist/runtime/src/agents/hosted-agent.ts +49 -0
  124. package/dist/runtime/src/agents/idle-archiver.ts +128 -0
  125. package/dist/runtime/src/agents/index.ts +205 -0
  126. package/dist/runtime/src/agents/maestro-provider.ts +206 -0
  127. package/dist/runtime/src/agents/maestro-registry.ts +31 -0
  128. package/dist/runtime/src/agents/mcp-tools/ask-user.ts +280 -0
  129. package/dist/runtime/src/agents/mcp-tools/common.ts +21 -0
  130. package/dist/runtime/src/agents/mcp-tools/self-config.ts +220 -0
  131. package/dist/runtime/src/agents/mcp-tools/spawn-subagent.ts +525 -0
  132. package/dist/runtime/src/agents/mcp-tools/visuals.ts +88 -0
  133. package/dist/runtime/src/agents/model-catalog.ts +151 -0
  134. package/dist/runtime/src/agents/registry.ts +24 -0
  135. package/dist/runtime/src/agents/rollout/claude.ts +331 -0
  136. package/dist/runtime/src/agents/rollout/codex.ts +569 -0
  137. package/dist/runtime/src/agents/rollout/shared.ts +205 -0
  138. package/dist/runtime/src/agents/self-config-core.ts +415 -0
  139. package/dist/runtime/src/agents/task-events.ts +44 -0
  140. package/dist/runtime/src/agents/tool-format.ts +352 -0
  141. package/dist/runtime/src/agents/topic-agent-switch.ts +193 -0
  142. package/dist/runtime/src/agents/topic-cleanup.ts +231 -0
  143. package/dist/runtime/src/agents/vault-tool-policy.ts +33 -0
  144. package/dist/runtime/src/application/execute-external-user-turn.ts +23 -0
  145. package/dist/runtime/src/application/submit-user-message.ts +50 -0
  146. package/dist/runtime/src/application/switch-topic-effort.ts +48 -0
  147. package/dist/runtime/src/application/switch-topic-model.ts +79 -0
  148. package/dist/runtime/src/application/topic-service.ts +139 -0
  149. package/dist/runtime/src/application/vault-command.ts +170 -0
  150. package/dist/runtime/src/bus.ts +319 -0
  151. package/dist/runtime/src/index.ts +333 -0
  152. package/dist/runtime/src/mcp/agent-health-server.ts +309 -0
  153. package/dist/runtime/src/mcp/background-bash-server.ts +346 -0
  154. package/dist/runtime/src/mcp/canonical-bridge-config.ts +84 -0
  155. package/dist/runtime/src/mcp/canonical-proxy-server.ts +171 -0
  156. package/dist/runtime/src/mcp/dm-ipc.ts +60 -0
  157. package/dist/runtime/src/mcp/mcp-helpers.ts +40 -0
  158. package/dist/runtime/src/mcp/paddleocr-server.ts +105 -0
  159. package/dist/runtime/src/mcp/peer-bridge.ts +114 -0
  160. package/dist/runtime/src/mcp/runtime-spec.ts +137 -0
  161. package/dist/runtime/src/mcp/session-comm/bridge-ipc-config.ts +27 -0
  162. package/dist/runtime/src/mcp/session-comm/peer-forward.ts +165 -0
  163. package/dist/runtime/src/mcp/session-comm/runtime.ts +115 -0
  164. package/dist/runtime/src/mcp/session-comm/server.ts +649 -0
  165. package/dist/runtime/src/mcp/session-comm/topic-config.ts +131 -0
  166. package/dist/runtime/src/mcp/session-comm/topics.ts +229 -0
  167. package/dist/runtime/src/mcp/stdio-protect.ts +84 -0
  168. package/dist/runtime/src/mcp/system-health-server.ts +130 -0
  169. package/dist/runtime/src/mcp/task-server.ts +173 -0
  170. package/dist/runtime/src/mcp/token-stats-server.ts +98 -0
  171. package/dist/runtime/src/mcp/vault-http.ts +219 -0
  172. package/dist/runtime/src/mcp/vault-run.ts +153 -0
  173. package/dist/runtime/src/mcp/vault-server.ts +104 -0
  174. package/dist/runtime/src/mcp/wiki-server.ts +755 -0
  175. package/dist/runtime/src/media/file-events.ts +33 -0
  176. package/dist/runtime/src/media/text-extractor.ts +312 -0
  177. package/dist/runtime/src/media/video.ts +92 -0
  178. package/dist/runtime/src/outbox/file-ops.ts +210 -0
  179. package/dist/runtime/src/outbox/index.ts +8 -0
  180. package/dist/runtime/src/outbox/utils.ts +51 -0
  181. package/dist/runtime/src/platform/background-bash/manager.ts +279 -0
  182. package/dist/runtime/src/platform/config.ts +371 -0
  183. package/dist/runtime/src/platform/constants.ts +18 -0
  184. package/dist/runtime/src/platform/debug-users.ts +14 -0
  185. package/dist/runtime/src/platform/delay.ts +1 -0
  186. package/dist/runtime/src/platform/error.ts +9 -0
  187. package/dist/runtime/src/platform/file-utils.ts +16 -0
  188. package/dist/runtime/src/platform/jsonl.ts +202 -0
  189. package/dist/runtime/src/platform/lifecycle.ts +174 -0
  190. package/dist/runtime/src/platform/logger.ts +31 -0
  191. package/dist/runtime/src/platform/maestro-bootstrap-env.ts +3 -0
  192. package/dist/runtime/src/platform/mcp-config.ts +878 -0
  193. package/dist/runtime/src/platform/modules.ts +145 -0
  194. package/dist/runtime/src/platform/node-plugins.ts +46 -0
  195. package/dist/runtime/src/platform/playwright/manager.ts +1233 -0
  196. package/dist/runtime/src/prompts/agents/wiki-archiver.md +261 -0
  197. package/dist/runtime/src/prompts/builders.ts +368 -0
  198. package/dist/runtime/src/prompts/sessions/channel-system.md +48 -0
  199. package/dist/runtime/src/prompts/sessions/manager-system.md +37 -0
  200. package/dist/runtime/src/prompts/sessions/topic-system.md +47 -0
  201. package/dist/runtime/src/prompts/sessions/visual-design.md +86 -0
  202. package/dist/runtime/src/query/active-rooms.ts +534 -0
  203. package/dist/runtime/src/query/control.ts +313 -0
  204. package/dist/runtime/src/query/index.ts +24 -0
  205. package/dist/runtime/src/query/session-inbox-cleanup.ts +44 -0
  206. package/dist/runtime/src/query/session-inbox-path.ts +42 -0
  207. package/dist/runtime/src/query/state.ts +94 -0
  208. package/dist/runtime/src/query/types.ts +83 -0
  209. package/dist/runtime/src/runtime/ask-callbacks.ts +153 -0
  210. package/dist/runtime/src/runtime/attachments.ts +151 -0
  211. package/dist/runtime/src/runtime/background-sessions.ts +83 -0
  212. package/dist/runtime/src/runtime/channel-context.ts +107 -0
  213. package/dist/runtime/src/runtime/errors.ts +69 -0
  214. package/dist/runtime/src/runtime/event-heartbeat.ts +77 -0
  215. package/dist/runtime/src/runtime/file-hooks.ts +81 -0
  216. package/dist/runtime/src/runtime/footer.ts +23 -0
  217. package/dist/runtime/src/runtime/inbox.ts +946 -0
  218. package/dist/runtime/src/runtime/tasks.ts +56 -0
  219. package/dist/runtime/src/runtime/topic-config.ts +29 -0
  220. package/dist/runtime/src/runtime/turn-runner.ts +2298 -0
  221. package/dist/runtime/src/runtime/usage-alert.ts +84 -0
  222. package/dist/runtime/src/runtime/visual-html.ts +109 -0
  223. package/dist/runtime/src/runtime/visual-store.ts +430 -0
  224. package/dist/runtime/src/runtime/visuals.ts +171 -0
  225. package/dist/runtime/src/security/sanitize.ts +27 -0
  226. package/dist/runtime/src/security/sensitive-path.ts +34 -0
  227. package/dist/runtime/src/storage/activity-log.ts +152 -0
  228. package/dist/runtime/src/storage/api-messages.ts +617 -0
  229. package/dist/runtime/src/storage/api-topic-brief.ts +127 -0
  230. package/dist/runtime/src/storage/api-topic-config.ts +192 -0
  231. package/dist/runtime/src/storage/api-topics.ts +844 -0
  232. package/dist/runtime/src/storage/app-settings.ts +44 -0
  233. package/dist/runtime/src/storage/browser-profiles.ts +133 -0
  234. package/dist/runtime/src/storage/conversations.ts +268 -0
  235. package/dist/runtime/src/storage/forum/agent-settings.ts +20 -0
  236. package/dist/runtime/src/storage/forum/index.ts +4 -0
  237. package/dist/runtime/src/storage/forum/repository.ts +283 -0
  238. package/dist/runtime/src/storage/forum/schema.ts +300 -0
  239. package/dist/runtime/src/storage/forum-db.ts +23 -0
  240. package/dist/runtime/src/storage/runtime-events.ts +130 -0
  241. package/dist/runtime/src/storage/runtime-leases.ts +189 -0
  242. package/dist/runtime/src/storage/runtime-process-leases.ts +216 -0
  243. package/dist/runtime/src/storage/runtime-topic-state.ts +148 -0
  244. package/dist/runtime/src/storage/runtime-turn-requests.ts +301 -0
  245. package/dist/runtime/src/storage/self-schedules.ts +298 -0
  246. package/dist/runtime/src/storage/session-asks.ts +347 -0
  247. package/dist/runtime/src/storage/sqlite.ts +129 -0
  248. package/dist/runtime/src/storage/tasks.ts +179 -0
  249. package/dist/runtime/src/storage/token-stats.ts +144 -0
  250. package/dist/runtime/src/storage/topic-archive-state.ts +59 -0
  251. package/dist/runtime/src/storage/topic-archive.ts +74 -0
  252. package/dist/runtime/src/storage/topic-settings.ts +67 -0
  253. package/dist/runtime/src/storage/topic-transcript.ts +98 -0
  254. package/dist/runtime/src/storage/vault-crypto.ts +65 -0
  255. package/dist/runtime/src/storage/vault.ts +225 -0
  256. package/dist/runtime/src/storage/wiki-summary-names.ts +33 -0
  257. package/dist/runtime/src/storage/wiki.ts +85 -0
  258. package/dist/runtime/src/topics/create.ts +123 -0
  259. package/dist/runtime/src/topics/derive.ts +398 -0
  260. package/dist/runtime/src/topics/lifecycle.ts +311 -0
  261. package/dist/runtime/src/topics/links.ts +7 -0
  262. package/dist/runtime/src/topics/personal-general.ts +65 -0
  263. package/dist/runtime/src/topics/session.ts +381 -0
  264. package/dist/runtime/src/types/api.ts +497 -0
  265. package/dist/runtime/src/types.ts +257 -0
  266. package/dist/runtime/src/version.ts +1 -0
  267. package/dist/runtime/tsconfig.json +13 -0
  268. package/package.json +28 -3
  269. package/dist/main.d.ts +0 -3
  270. package/dist/main.d.ts.map +0 -1
@@ -0,0 +1,115 @@
1
+ import { unlinkSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import type { AgentRegistry } from "#agents/contracts";
5
+ import { writeCodexRollout } from "#agents/rollout/codex";
6
+ import { logger } from "#platform/logger";
7
+ import { readConversation } from "#storage/conversations";
8
+ import { CODEX_EFFORT_VALUES, type EffortLevel } from "#types";
9
+
10
+ const VALID_EFFORTS = new Set<EffortLevel>(CODEX_EFFORT_VALUES);
11
+
12
+ // Codex CLI's own empirical default is gpt-5.6-sol (`codex exec` 2026-07-10 →
13
+ // "model: gpt-5.6-sol"), but we deliberately default to gpt-5.6-luna — the
14
+ // cheapest/fastest GPT-5.6 tier — for a general always-on assistant where most
15
+ // queries are light. Heavier work escalates to terra/sol via set_model. This
16
+ // value is passed explicitly to the SDK (see event-processor resolveDefaultModel),
17
+ // so the footer and the actual model stay in sync.
18
+ export const codexRegistry: AgentRegistry = {
19
+ kind: "codex",
20
+ defaultModel: "gpt-5.6-luna",
21
+ // defaultEffort intentionally omitted — Codex SDK treats absence as
22
+ // "reasoning off". Setting "high"/etc. would silently flip on reasoning.
23
+
24
+ expandModelAlias(s) {
25
+ return s;
26
+ },
27
+
28
+ validateModel(s) {
29
+ // Codex doesn't publish a closed model list and OpenAI ships new IDs
30
+ // (gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna, o3, ...) frequently. Best-effort: accept any
31
+ // non-empty string. Bad IDs surface at SDK call time with a clear error.
32
+ return typeof s === "string" && s.length > 0;
33
+ },
34
+
35
+ validEfforts: CODEX_EFFORT_VALUES,
36
+ validateEffort(s) {
37
+ // GPT-5.6 accepts low/medium/high/xhigh/max; 'minimal'
38
+ // was removed because the Codex API rejects it when default tools
39
+ // (image_gen, web_search) are active.
40
+ return VALID_EFFORTS.has(s);
41
+ },
42
+
43
+ footerLabel(model, effort) {
44
+ // Codex omits effort to mean "reasoning off". Show `(off)` explicitly so
45
+ // the user can distinguish from claude (which always has a default).
46
+ return `${model} · ${effort ?? "(off)"}`;
47
+ },
48
+
49
+ writeRollout(opts) {
50
+ // Codex SDK exposes the resume key as `threadId`; AgentRegistry unifies
51
+ // the name to `sessionId` so callers don't branch on agent.
52
+ // `reuseSessionId` (if any) is forwarded as `threadId` so claude→codex→claude
53
+ // round-trips also keep one continuous codex thread instead of orphaning a
54
+ // fresh uuidv7 on every switch.
55
+ const { threadId, rolloutPath } = writeCodexRollout({
56
+ cwd: opts.cwd,
57
+ entries: opts.entries,
58
+ model: opts.model ?? codexRegistry.defaultModel,
59
+ ...(opts.effort ? { effort: opts.effort } : {}),
60
+ ...(opts.reuseSessionId ? { threadId: opts.reuseSessionId } : {}),
61
+ });
62
+ return { sessionId: threadId, rolloutPath };
63
+ },
64
+
65
+ // Codex SDK has no fork API. We synthesize a rollout from the
66
+ // provider-agnostic conversation log (same path as `set_agent` cross-agent
67
+ // bridging). Caveats: extractChatPairs folds tool_use/tool_result into
68
+ // assistant text as `[Tool: ...]` annotations, so structural tool history
69
+ // is lost.
70
+ async forkSession({ cwd, userId, topicName, model, effort }) {
71
+ const entries = readConversation(userId, topicName);
72
+ const { threadId, rolloutPath } = writeCodexRollout({
73
+ cwd,
74
+ entries,
75
+ model: model ?? codexRegistry.defaultModel,
76
+ ...(effort ? { effort } : {}),
77
+ });
78
+ return { forkId: threadId, rolloutPath };
79
+ },
80
+
81
+ // Codex stores rollouts at `~/.codex/sessions/<YYYY>/<MM>/<DD>/rollout-<ts>-<threadId>.jsonl`.
82
+ // The date prefix is unknown at cleanup time (it's the *original* write
83
+ // timestamp, not "now"), so we glob across the whole sessions tree by
84
+ // threadId suffix. With at most a few thousand files in active use this
85
+ // is well under a millisecond on a warm filesystem.
86
+ async cleanupRollouts({ sessionIds }) {
87
+ if (sessionIds.length === 0) return;
88
+ const sessionsDir = join(process.env.CODEX_HOME || join(homedir(), ".codex"), "sessions");
89
+ const failures: unknown[] = [];
90
+ // One Glob per threadId so a single corrupt entry can't poison the rest.
91
+ // Bun.Glob's `scan` yields paths relative to its base dir.
92
+ for (const tid of sessionIds) {
93
+ try {
94
+ const glob = new Bun.Glob(`**/rollout-*-${tid}.jsonl`);
95
+ for await (const rel of glob.scan({ cwd: sessionsDir, onlyFiles: true })) {
96
+ const path = join(sessionsDir, rel);
97
+ try {
98
+ unlinkSync(path);
99
+ } catch (e) {
100
+ if ((e as NodeJS.ErrnoException)?.code !== "ENOENT") {
101
+ logger.warn({ err: e, path }, "codex cleanupRollouts: unlink failed");
102
+ failures.push(e);
103
+ }
104
+ }
105
+ }
106
+ } catch (e) {
107
+ logger.warn({ err: e, threadId: tid }, "codex cleanupRollouts: scan failed");
108
+ failures.push(e);
109
+ }
110
+ }
111
+ if (failures.length > 0) {
112
+ throw new AggregateError(failures, "codex cleanupRollouts failed");
113
+ }
114
+ },
115
+ };
@@ -0,0 +1,365 @@
1
+ /**
2
+ * Codex process tree kill helper.
3
+ *
4
+ * The @openai/codex-sdk spawns `codex` as a direct child of our bot process but
5
+ * does not expose the resulting PID and does not detach the child into its own
6
+ * process group. So when we call `abortController.abort()`, the SDK sends a
7
+ * single SIGTERM that may or may not reach grandchild tool processes (shells,
8
+ * MCP servers spawned by codex itself). To guarantee abort actually stops
9
+ * activity we identify the codex children we spawned and SIGTERM their whole
10
+ * descendant tree, with a SIGKILL fallback for stragglers.
11
+ *
12
+ * Strategy (post-2026-05-14 fix):
13
+ * 1. Acquire a global spawn lock that's held across the entire window
14
+ * from baseline snapshot through the first SDK event (when codex is
15
+ * actually spawned — `runStreamed()` returns a lazy async generator,
16
+ * so the real `child_process.spawn()` happens on the first iterator
17
+ * `.next()`, not when `runStreamed()` is awaited).
18
+ * 2. Snapshot direct codex children of `process.pid` BEFORE forcing the
19
+ * first iterator step.
20
+ * 3. After the first event lands, diff against a fresh snapshot to
21
+ * identify our PIDs and register them in `globalOwnedCodexPids` so
22
+ * concurrent codex callers don't claim them as their own.
23
+ * 4. On abort, walk each PID's descendant tree via `pgrep -P` and signal
24
+ * every node. Unregister on exit.
25
+ *
26
+ * Platform: relies on `pgrep` and `ps` — macOS/Linux only. Windows is not
27
+ * supported (Otium already requires POSIX for the rest of the stack).
28
+ */
29
+
30
+ import { execFileSync } from "node:child_process";
31
+ import { logger } from "#platform/logger";
32
+
33
+ // 5s gives MCP stdio servers (codex spawns these as grandchildren) a realistic
34
+ // shutdown window. 2s was empirically too aggressive: stdio MCP servers in the
35
+ // middle of writing a response would get SIGKILL'd before they finished.
36
+ const SIGKILL_DELAY_MS = 5_000;
37
+ const CODEX_PROC_NAME_MATCH = /codex/i;
38
+ // Cap tree-walk depth. Defends against pathological or malicious cycles that
39
+ // slip past the visited-set guard (e.g. via PID reuse), and against deeply
40
+ // nested codex+MCP+shell trees blowing the stack.
41
+ const TREE_DEPTH_CAP = 16;
42
+
43
+ /** Return direct child PIDs of `parentPid`. Returns [] on error or no children. */
44
+ function getDirectChildren(parentPid: number): number[] {
45
+ try {
46
+ const out = execFileSync("pgrep", ["-P", String(parentPid)], {
47
+ encoding: "utf8",
48
+ stdio: ["ignore", "pipe", "ignore"],
49
+ }).trim();
50
+ if (!out) return [];
51
+ return out
52
+ .split("\n")
53
+ .map((s) => Number(s))
54
+ .filter((n) => Number.isFinite(n) && n > 0);
55
+ } catch {
56
+ // pgrep exits 1 when no matches; treat as empty.
57
+ return [];
58
+ }
59
+ }
60
+
61
+ /** Return the executable name (comm) of a process, or "" if unavailable. */
62
+ function getProcName(pid: number): string {
63
+ try {
64
+ return execFileSync("ps", ["-p", String(pid), "-o", "comm="], {
65
+ encoding: "utf8",
66
+ stdio: ["ignore", "pipe", "ignore"],
67
+ }).trim();
68
+ } catch {
69
+ return "";
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Return the start time of `pid` as the raw `ps -o lstart=` string (e.g.
75
+ * "Tue May 12 10:49:35 2026"), or "" if unavailable. Used as a fingerprint
76
+ * to defend against PID reuse — even if the OS reassigns a baseline PID to
77
+ * a fresh codex spawn, the lstart string will differ.
78
+ *
79
+ * macOS and Linux both support `ps -o lstart=` with the same date format.
80
+ */
81
+ function getProcLstart(pid: number): string {
82
+ try {
83
+ return execFileSync("ps", ["-p", String(pid), "-o", "lstart="], {
84
+ encoding: "utf8",
85
+ stdio: ["ignore", "pipe", "ignore"],
86
+ }).trim();
87
+ } catch {
88
+ return "";
89
+ }
90
+ }
91
+
92
+ /** (pid, lstart) fingerprint pair — same PID + same lstart = same process. */
93
+ export type CodexProcStamp = { pid: number; lstart: string };
94
+
95
+ /**
96
+ * Recursively collect `rootPid` and all its descendants, up to `TREE_DEPTH_CAP`
97
+ * levels deep. Cycle-safe via the `out` Set; depth-cap-safe via the `depth`
98
+ * parameter (defends against pathological trees the Set guard might miss,
99
+ * e.g. PID reuse mid-walk).
100
+ */
101
+ function collectTree(rootPid: number, out: Set<number> = new Set(), depth = 0): Set<number> {
102
+ if (out.has(rootPid)) return out;
103
+ out.add(rootPid);
104
+ if (depth >= TREE_DEPTH_CAP) {
105
+ logger.warn(
106
+ { rootPid, depth, cap: TREE_DEPTH_CAP },
107
+ "codex-tree-kill: tree depth cap reached, descendants beyond this point not collected",
108
+ );
109
+ return out;
110
+ }
111
+ for (const child of getDirectChildren(rootPid)) {
112
+ collectTree(child, out, depth + 1);
113
+ }
114
+ return out;
115
+ }
116
+
117
+ /**
118
+ * Snapshot current codex-named direct children of `process.pid` along with
119
+ * each PID's lstart (start time) fingerprint. Returning the lstart map lets
120
+ * `findNewCodexChildren` distinguish "PID X was in baseline and is the same
121
+ * process" from "PID X was in baseline but the OS reassigned that PID to a
122
+ * fresh codex" — only the latter is novel.
123
+ */
124
+ export function snapshotCodexChildren(): Map<number, string> {
125
+ const out = new Map<number, string>();
126
+ for (const pid of getDirectChildren(process.pid)) {
127
+ const name = getProcName(pid);
128
+ if (!name || !CODEX_PROC_NAME_MATCH.test(name)) continue;
129
+ const lstart = getProcLstart(pid);
130
+ // If lstart can't be read (race: process died between getDirectChildren
131
+ // and getProcLstart), still record the PID with an empty fingerprint —
132
+ // any later snapshot with a real lstart will compare as "different" and
133
+ // count it as novel, which is the safer side to err on.
134
+ out.set(pid, lstart);
135
+ }
136
+ return out;
137
+ }
138
+
139
+ /**
140
+ * PIDs already owned by an active codex caller (normal provider or fork).
141
+ * `findNewCodexChildren` excludes these so concurrent callers can't claim
142
+ * each other's PIDs when their baselines happen to miss them (e.g. the other
143
+ * caller's spawn raced in between baseline and first-event diff).
144
+ *
145
+ * Callers must `registerOwnedCodexPids` after diff, and `unregisterOwnedCodexPids`
146
+ * in their cleanup `finally` block to avoid leaking entries on long-running bots.
147
+ */
148
+ const globalOwnedCodexPids = new Set<number>();
149
+
150
+ export function registerOwnedCodexPids(pids: Iterable<number>): void {
151
+ for (const p of pids) globalOwnedCodexPids.add(p);
152
+ }
153
+
154
+ export function unregisterOwnedCodexPids(pids: Iterable<number>): void {
155
+ for (const p of pids) globalOwnedCodexPids.delete(p);
156
+ }
157
+
158
+ /**
159
+ * Codex children that are novel relative to `baseline`. A PID is novel if:
160
+ * - it wasn't in baseline, OR
161
+ * - it was in baseline but with a different lstart (PID reuse — old codex
162
+ * died and the OS reassigned the PID to our new spawn)
163
+ * AND it isn't already owned by a concurrent codex caller.
164
+ */
165
+ export function findNewCodexChildren(baseline: Map<number, string>): number[] {
166
+ const current = snapshotCodexChildren();
167
+ const novel: number[] = [];
168
+ for (const [pid, lstart] of current) {
169
+ const baselineLstart = baseline.get(pid);
170
+ // Same PID + same lstart fingerprint → continuation of the same process,
171
+ // not novel. Same PID + different lstart → PID reuse, treat as novel.
172
+ if (baselineLstart !== undefined && baselineLstart === lstart) continue;
173
+ if (globalOwnedCodexPids.has(pid)) continue;
174
+ novel.push(pid);
175
+ }
176
+ return novel;
177
+ }
178
+
179
+ /**
180
+ * Serialize the spawn critical section across concurrent codex callers
181
+ * (`codexProvider` and `queryCodexForkSession`). The lock MUST be held from
182
+ * baseline snapshot through the first generator event, because the SDK's
183
+ * `runStreamed()` is lazy — the actual `child_process.spawn()` happens when
184
+ * the iterator advances, not when `runStreamed()` resolves. Without a lock
185
+ * spanning that window, two callers see overlapping codex children and
186
+ * mis-attribute them on abort, leading to cross-kills.
187
+ *
188
+ * Only the spawn phase is serialized (typically a few hundred ms — codex's
189
+ * first event lands quickly). The actual LLM streaming after the first event
190
+ * runs concurrently as before.
191
+ */
192
+ let codexSpawnChain: Promise<void> = Promise.resolve();
193
+
194
+ /**
195
+ * Acquire the codex-spawn lock. Returns a release callback. Caller MUST invoke
196
+ * the release callback in a `finally` block (or via the convenience wrapper
197
+ * `withCodexSpawnSerial`) — otherwise the chain stalls indefinitely.
198
+ */
199
+ export async function acquireCodexSpawnLock(): Promise<() => void> {
200
+ const prev = codexSpawnChain;
201
+ let release!: () => void;
202
+ let released = false;
203
+ codexSpawnChain = new Promise<void>((r) => {
204
+ release = () => {
205
+ if (released) return;
206
+ released = true;
207
+ r();
208
+ };
209
+ });
210
+ await prev;
211
+ return release;
212
+ }
213
+
214
+ /**
215
+ * Convenience wrapper for short, single-await critical sections. Prefer
216
+ * `acquireCodexSpawnLock` when the lock must span multiple awaits including
217
+ * iterator advancement (the codex spawn case).
218
+ */
219
+ export async function withCodexSpawnSerial<T>(fn: () => Promise<T>): Promise<T> {
220
+ const release = await acquireCodexSpawnLock();
221
+ try {
222
+ return await fn();
223
+ } finally {
224
+ release();
225
+ }
226
+ }
227
+
228
+ /** True iff `pid` is still alive. */
229
+ function isAlive(pid: number): boolean {
230
+ try {
231
+ process.kill(pid, 0);
232
+ return true;
233
+ } catch {
234
+ return false;
235
+ }
236
+ }
237
+
238
+ /**
239
+ * True iff `pid` is alive and still appears to be the same process captured by
240
+ * `lstart`. An empty lstart means we could not fingerprint the original process,
241
+ * so we refuse to SIGKILL it later rather than risk killing a reused PID.
242
+ */
243
+ function isSameProcess(pid: number, lstart: string): boolean {
244
+ return lstart !== "" && isAlive(pid) && getProcLstart(pid) === lstart;
245
+ }
246
+
247
+ type CodexKillPlan = {
248
+ rootPids: number[];
249
+ targets: Map<number, string>;
250
+ };
251
+
252
+ /** Capture the complete tree before signalling changes parent/child relationships. */
253
+ function signalCodexTrees(rootPids: number[]): CodexKillPlan | null {
254
+ if (rootPids.length === 0) return null;
255
+
256
+ const targets = new Map<number, string>();
257
+ for (const root of rootPids) {
258
+ for (const pid of collectTree(root)) {
259
+ if (!targets.has(pid)) targets.set(pid, getProcLstart(pid));
260
+ }
261
+ }
262
+
263
+ logger.warn({ rootPids, totalPids: targets.size }, "codex-tree-kill: SIGTERM process tree");
264
+ for (const pid of targets.keys()) {
265
+ try {
266
+ process.kill(pid, "SIGTERM");
267
+ } catch {
268
+ // already dead or permission denied
269
+ }
270
+ }
271
+ return { rootPids, targets };
272
+ }
273
+
274
+ function hasSameProcessSurvivors(targets: Map<number, string>): boolean {
275
+ for (const [pid, lstart] of targets) {
276
+ if (isSameProcess(pid, lstart)) return true;
277
+ }
278
+ return false;
279
+ }
280
+
281
+ /** Kill survivors from a previously fingerprinted plan without risking PID reuse. */
282
+ function killCodexStragglers({ rootPids, targets }: CodexKillPlan): void {
283
+ const aliveTargets: number[] = [];
284
+ let droppedAsPossibleReuse = 0;
285
+ for (const [pid, lstart] of targets) {
286
+ if (isSameProcess(pid, lstart)) {
287
+ aliveTargets.push(pid);
288
+ } else if (isAlive(pid)) {
289
+ droppedAsPossibleReuse++;
290
+ }
291
+ }
292
+ if (aliveTargets.length === 0) return;
293
+
294
+ // PID-reuse defence: re-walk the trees (children may have spawned siblings
295
+ // mid-SIGTERM), but ONLY signal PIDs that were in the SIGTERM-time targets.
296
+ const reachable = new Set<number>(aliveTargets);
297
+ for (const root of rootPids) {
298
+ const rootLstart = targets.get(root);
299
+ if (rootLstart && isSameProcess(root, rootLstart)) {
300
+ for (const pid of collectTree(root)) reachable.add(pid);
301
+ }
302
+ }
303
+ const stragglers: number[] = [];
304
+ for (const pid of reachable) {
305
+ const lstart = targets.get(pid);
306
+ if (lstart && isSameProcess(pid, lstart)) stragglers.push(pid);
307
+ else if (isAlive(pid)) droppedAsPossibleReuse++;
308
+ }
309
+ logger.warn(
310
+ {
311
+ aliveRoots: rootPids.filter((pid) => {
312
+ const lstart = targets.get(pid);
313
+ return lstart ? isSameProcess(pid, lstart) : false;
314
+ }),
315
+ totalStragglers: stragglers.length,
316
+ droppedAsPossibleReuse,
317
+ },
318
+ "codex-tree-kill: SIGKILL fallback after SIGTERM timeout",
319
+ );
320
+ for (const pid of stragglers) {
321
+ try {
322
+ process.kill(pid, "SIGKILL");
323
+ } catch {
324
+ // already dead
325
+ }
326
+ }
327
+ }
328
+
329
+ /**
330
+ * SIGTERM every PID in the descendant tree of each root in `rootPids`, then
331
+ * schedule a SIGKILL fallback after `SIGKILL_DELAY_MS` for any survivors.
332
+ * Safe to call multiple times; no-op if all PIDs are already dead.
333
+ */
334
+ export function killCodexTrees(rootPids: number[]): void {
335
+ const plan = signalCodexTrees(rootPids);
336
+ if (!plan) return;
337
+
338
+ const killTimer = setTimeout(() => {
339
+ killCodexStragglers(plan);
340
+ }, SIGKILL_DELAY_MS);
341
+ killTimer.unref?.();
342
+ }
343
+
344
+ /**
345
+ * Reap every Codex tree owned by this node during process shutdown.
346
+ *
347
+ * The direct-child snapshot covers the narrow spawn window before a provider
348
+ * has registered its PID. Unlike the normal abort path, this function awaits
349
+ * the grace period with a referenced timer so node shutdown cannot exit before
350
+ * SIGKILL fallback has had a chance to remove resistant MCP/shell descendants.
351
+ */
352
+ export async function killOwnedCodexTreesForShutdown(graceMs = 3_000): Promise<void> {
353
+ const roots = new Set(globalOwnedCodexPids);
354
+ for (const pid of snapshotCodexChildren().keys()) roots.add(pid);
355
+ const plan = signalCodexTrees([...roots]);
356
+ if (!plan) return;
357
+
358
+ const deadline = Date.now() + Math.max(0, graceMs);
359
+ while (Date.now() < deadline && hasSameProcessSurvivors(plan.targets)) {
360
+ await new Promise<void>((resolve) =>
361
+ setTimeout(resolve, Math.min(100, Math.max(1, deadline - Date.now()))),
362
+ );
363
+ }
364
+ killCodexStragglers(plan);
365
+ }
@@ -0,0 +1,69 @@
1
+ import type { ConversationEntry } from "#storage/conversations";
2
+ import type { AgentKind, EffortLevel } from "#types";
3
+
4
+ /**
5
+ * Inputs to a rollout synthesis call. The conversation log is provider-agnostic
6
+ * — each registry's encoder reshapes it into the SDK-native rollout format
7
+ * (claude jsonl / codex jsonl) so a subsequent `resume`/`resumeThread` picks
8
+ * up the cross-agent dialogue as if it had always been native.
9
+ */
10
+ export interface WriteRolloutOptions {
11
+ /** Working directory the resumed session will report. */
12
+ cwd: string;
13
+ /** Provider-agnostic event log to encode. */
14
+ entries: ConversationEntry[];
15
+ /**
16
+ * If set, the synthetic rollout is written using this id as the SDK
17
+ * session/thread key — i.e. the file lands at the SAME path the SDK
18
+ * already manages for an earlier run on this agent.
19
+ */
20
+ reuseSessionId?: string;
21
+ /** Effective model the synthesized provider session will resume with. */
22
+ model?: string;
23
+ /** Effective reasoning effort recorded by providers whose rollout format carries it. */
24
+ effort?: EffortLevel;
25
+ }
26
+
27
+ export interface WriteRolloutResult {
28
+ sessionId: string;
29
+ rolloutPath: string;
30
+ }
31
+
32
+ export interface ForkRegistryOptions {
33
+ parentSessionId: string;
34
+ cwd: string;
35
+ userId: number | string;
36
+ topicName: string;
37
+ title?: string;
38
+ /** Effective model/effort of the fork's first resumed turn. */
39
+ model?: string;
40
+ effort?: EffortLevel;
41
+ }
42
+
43
+ export interface ForkRegistryResult {
44
+ forkId: string;
45
+ rolloutPath: string;
46
+ }
47
+
48
+ export interface CleanupRolloutsOptions {
49
+ cwd: string;
50
+ sessionIds: string[];
51
+ }
52
+
53
+ /**
54
+ * Per-agent runtime config registry. Centralizes the bits that differ between
55
+ * agents so caller code can stay agent-agnostic.
56
+ */
57
+ export interface AgentRegistry {
58
+ kind: AgentKind;
59
+ defaultModel: string;
60
+ defaultEffort?: EffortLevel;
61
+ expandModelAlias(s: string): string;
62
+ validateModel(s: string): boolean;
63
+ validEfforts: readonly EffortLevel[];
64
+ validateEffort(s: EffortLevel): boolean;
65
+ footerLabel(model: string, effort?: EffortLevel): string;
66
+ writeRollout(opts: WriteRolloutOptions): WriteRolloutResult;
67
+ forkSession(opts: ForkRegistryOptions): Promise<ForkRegistryResult>;
68
+ cleanupRollouts(opts: CleanupRolloutsOptions): Promise<void>;
69
+ }
@@ -0,0 +1,13 @@
1
+ /** Recursively apply fn to every string value in a nested object/array. */
2
+ export function deepMapStrings(value: unknown, fn: (s: string) => string): unknown {
3
+ if (typeof value === "string") return fn(value);
4
+ if (Array.isArray(value)) return value.map((v) => deepMapStrings(v, fn));
5
+ if (value && typeof value === "object") {
6
+ const out: Record<string, unknown> = {};
7
+ for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
8
+ out[k] = deepMapStrings(v, fn);
9
+ }
10
+ return out;
11
+ }
12
+ return value;
13
+ }
@@ -0,0 +1,91 @@
1
+ import {
2
+ referencesRuntimeSecretStorage as defaultReferencesRuntimeSecretStorage,
3
+ shouldRedirectVaultTool as defaultShouldRedirectVaultTool,
4
+ } from "#agents/vault-tool-policy";
5
+ import { CLAUDE_EXECUTABLE, codexAuthFilePath } from "#platform/config";
6
+ import { getMcpServersForQuery as defaultGetMcpServersForQuery } from "#platform/mcp-config";
7
+ import { redactVaultSecrets as defaultRedactVaultSecrets } from "#storage/vault";
8
+ import type { AgentQueryOptions } from "#types";
9
+
10
+ /**
11
+ * Host-owned services used by the provider execution layer.
12
+ *
13
+ * The default implementation preserves standalone Negotium behaviour. An
14
+ * embedding host may replace these callbacks once during process bootstrap so
15
+ * providers use that host's MCP catalog and device-local secret store without
16
+ * importing any host-specific runtime code into Negotium.
17
+ */
18
+ export interface AgentExecutionHost {
19
+ getMcpServersForQuery(opts: AgentQueryOptions): Record<string, unknown>;
20
+ redactVaultSecrets(userId: string, value: string): string;
21
+ referencesRuntimeSecretStorage(value: unknown): boolean;
22
+ shouldRedirectVaultTool(userId: string, toolName: string, input: unknown): boolean;
23
+ claudeCodeExecutablePath(): string | undefined;
24
+ codexAuthFilePath(): string;
25
+ transformQueryOptions?(opts: AgentQueryOptions): AgentQueryOptions;
26
+ }
27
+
28
+ const defaultHost: AgentExecutionHost = {
29
+ getMcpServersForQuery: defaultGetMcpServersForQuery,
30
+ redactVaultSecrets: defaultRedactVaultSecrets,
31
+ referencesRuntimeSecretStorage: defaultReferencesRuntimeSecretStorage,
32
+ shouldRedirectVaultTool: defaultShouldRedirectVaultTool,
33
+ claudeCodeExecutablePath: () => CLAUDE_EXECUTABLE,
34
+ codexAuthFilePath,
35
+ };
36
+
37
+ const hostRegistrations: Array<{
38
+ id: symbol;
39
+ overrides: Partial<AgentExecutionHost>;
40
+ }> = [];
41
+
42
+ function activeHost(): AgentExecutionHost {
43
+ const host = { ...defaultHost };
44
+ for (const registration of hostRegistrations) Object.assign(host, registration.overrides);
45
+ return host;
46
+ }
47
+
48
+ /** Configure provider host services. Returns a disposer useful for tests. */
49
+ export function configureAgentExecutionHost(overrides: Partial<AgentExecutionHost>): () => void {
50
+ const registration = { id: Symbol("agent-execution-host"), overrides };
51
+ hostRegistrations.push(registration);
52
+ let disposed = false;
53
+ return () => {
54
+ if (disposed) return;
55
+ disposed = true;
56
+ const index = hostRegistrations.findIndex((entry) => entry.id === registration.id);
57
+ if (index >= 0) hostRegistrations.splice(index, 1);
58
+ };
59
+ }
60
+
61
+ export function hostedMcpServers(opts: AgentQueryOptions): Record<string, unknown> {
62
+ return activeHost().getMcpServersForQuery(opts);
63
+ }
64
+
65
+ export function redactHostedSecrets(userId: string, value: string): string {
66
+ return activeHost().redactVaultSecrets(userId, value);
67
+ }
68
+
69
+ export function referencesHostedSecretStorage(value: unknown): boolean {
70
+ return activeHost().referencesRuntimeSecretStorage(value);
71
+ }
72
+
73
+ export function shouldRedirectHostedVaultTool(
74
+ userId: string,
75
+ toolName: string,
76
+ input: unknown,
77
+ ): boolean {
78
+ return activeHost().shouldRedirectVaultTool(userId, toolName, input);
79
+ }
80
+
81
+ export function transformHostedQueryOptions(opts: AgentQueryOptions): AgentQueryOptions {
82
+ return activeHost().transformQueryOptions?.(opts) ?? opts;
83
+ }
84
+
85
+ export function hostedClaudeCodeExecutablePath(): string | undefined {
86
+ return activeHost().claudeCodeExecutablePath();
87
+ }
88
+
89
+ export function hostedCodexAuthFilePath(): string {
90
+ return activeHost().codexAuthFilePath();
91
+ }
@@ -0,0 +1,2 @@
1
+ {"parentUuid":"88e296c8-370a-4c40-a248-10fb661fc81d","isSidechain":false,"attachment":{"type":"deferred_tools_delta","addedNames":["AskUserQuestion","CronCreate","CronDelete","CronList","EnterPlanMode","EnterWorktree","ExitPlanMode","ExitWorktree","Monitor","NotebookEdit","PushNotification","RemoteTrigger","TaskOutput","TaskStop","TodoWrite","WebFetch","WebSearch","mcp__claude_ai_Google_Drive__authenticate","mcp__claude_ai_Google_Drive__complete_authentication"],"addedLines":["AskUserQuestion","CronCreate","CronDelete","CronList","EnterPlanMode","EnterWorktree","ExitPlanMode","ExitWorktree","Monitor","NotebookEdit","PushNotification","RemoteTrigger","TaskOutput","TaskStop","TodoWrite","WebFetch","WebSearch","mcp__claude_ai_Google_Drive__authenticate","mcp__claude_ai_Google_Drive__complete_authentication"],"removedNames":[]},"type":"attachment","uuid":"72cd3544-9370-45f4-97c7-700ac90fcb7f","timestamp":"2026-05-01T13:31:34.909Z","userType":"external","entrypoint":"sdk-ts","cwd":"/private/tmp/claude-poc-cwd","sessionId":"104a73cb-ae08-4464-b655-753b906cb197","version":"2.1.126","gitBranch":"HEAD"}
2
+ {"parentUuid":"72cd3544-9370-45f4-97c7-700ac90fcb7f","isSidechain":false,"attachment":{"type":"skill_listing","content":"- update-config: Use this skill to configure the Claude Code harness via settings.json. Automated behaviors (\"from now on when X\", \"each time X\", \"whenever X\", \"before/after X\") require hooks configured in settings.json - the harness executes these, not Claude, so memory/preferences cannot fulfill them. Also use for: permissions (\"allow X\", \"add permission\", \"move permission to\"), env vars (\"set X=Y\"), hook troubleshooting, or any changes to settings.json/settings.local.json files. Examples: \"allow npm commands\", \"add bq permission to global settings\", \"move permission to user settings\", \"set DEBUG=true\", \"when claude stops show X\". For simple settings like theme/model, suggest the /config command.\n- keybindings-help: Use when the user wants to customize keyboard shortcuts, rebind keys, add chord bindings, or modify ~/.claude/keybindings.json. Examples: \"rebind ctrl+s\", \"add a chord shortcut\", \"change the submit key\", \"customize keybindings\".\n- simplify: Review changed code for reuse, quality, and efficiency, then fix any issues found.\n- fewer-permission-prompts: Scan your transcripts for common read-only Bash and MCP tool calls, then add a prioritized allowlist to project .claude/settings.json to reduce permission prompts.\n- loop: Run a prompt or slash command on a recurring interval (e.g. /loop 5m /foo). Omit the interval to let the model self-pace. - When the user wants to set up a recurring task, poll for status, or run something repeatedly on an interval (e.g. \"check the deploy every 5 minutes\", \"keep running /babysit-prs\"). Do NOT invoke for one-off tasks.\n- schedule: Create, update, list, or run scheduled remote agents (routines) that execute on a cron schedule. - When the user wants to schedule a recurring or one-time remote agent (\"run this every Monday\", \"open a cleanup PR for X in 2 weeks\"), or to manage existing routines. ALSO OFFER PROACTIVELY: after you finish work that has a natural future follow-up, end your reply with a one-line offer to schedule a background agent to do it. Strong signals: a feature flag / gate / experiment / staged rollout was just shipped (offer a one-time agent in ~2 weeks to open a cleanup PR or evaluate results), a new alert/monitor was created (offer a recurring agent to triage it), a TODO/migration with a \"remove once X\" condition was left behind (offer a one-time agent to do the removal). Skip the offer for refactors, bug fixes, and anything that is done once it ships. Name a concrete action and cadence (\"in 2 weeks\", \"every Monday\") and only offer when the run just succeeded — do not pitch a schedule for something that has not happened yet.\n- claude-api: Build, debug, and optimize Claude API / Anthropic SDK apps. Apps built with this skill should include prompt caching. Also handles migrating existing Claude API code between Claude model versions (4.5 → 4.6, 4.6 → 4.7, retired-model replacements).\nTRIGGER when: code imports `anthropic`/`@anthropic-ai/sdk`; user asks for the Claude API, Anthropic SDK, or Managed Agents; user adds/modifies/tunes a Claude feature (caching, thinking, compaction, tool use, batch, files, citations, memory) or model (Opus/Sonnet/Haiku) in a file; questions about prompt caching / cache hit rate in an Anthropic SDK project.\nSKIP: file imports `openai`/other-provider SDK, filename like `*-openai.py`/`*-generic.py`, provider-neutral code, general programming/ML.\n- init: Initialize a new CLAUDE.md file with codebase documentation\n- review: Review a pull request\n- security-review: Complete a security review of the pending changes on the current branch","skillCount":10,"isInitial":true},"type":"attachment","uuid":"51fe817e-380d-42d7-a4f4-b6d75e19ba63","timestamp":"2026-05-01T13:31:34.909Z","userType":"external","entrypoint":"sdk-ts","cwd":"/private/tmp/claude-poc-cwd","sessionId":"104a73cb-ae08-4464-b655-753b906cb197","version":"2.1.126","gitBranch":"HEAD"}