negotium 0.1.6 → 0.1.8

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 (256) hide show
  1. package/README.md +2 -2
  2. package/dist/chunk-0ay6cjdy.js +1331 -0
  3. package/dist/chunk-0ay6cjdy.js.map +16 -0
  4. package/dist/chunk-1k7t7bnx.js +97 -0
  5. package/dist/chunk-1k7t7bnx.js.map +10 -0
  6. package/dist/chunk-5ahkew9c.js +806 -0
  7. package/dist/chunk-5ahkew9c.js.map +17 -0
  8. package/dist/chunk-6cpn7ht2.js +287 -0
  9. package/dist/chunk-6cpn7ht2.js.map +13 -0
  10. package/dist/chunk-6kctn20e.js +3529 -0
  11. package/dist/chunk-6kctn20e.js.map +20 -0
  12. package/dist/chunk-7xqj7aex.js +77 -0
  13. package/dist/chunk-7xqj7aex.js.map +11 -0
  14. package/dist/chunk-9p0ww3tk.js +41 -0
  15. package/dist/chunk-9p0ww3tk.js.map +10 -0
  16. package/dist/chunk-a92a7nrk.js +252 -0
  17. package/dist/chunk-a92a7nrk.js.map +10 -0
  18. package/dist/chunk-c500xe9z.js +33 -0
  19. package/dist/chunk-c500xe9z.js.map +10 -0
  20. package/dist/chunk-cds1qxsf.js +1644 -0
  21. package/dist/chunk-cds1qxsf.js.map +17 -0
  22. package/dist/chunk-e4407g8x.js +274 -0
  23. package/dist/chunk-e4407g8x.js.map +10 -0
  24. package/dist/chunk-e5pbggsh.js +363 -0
  25. package/dist/chunk-e5pbggsh.js.map +11 -0
  26. package/dist/chunk-edb07sjp.js +567 -0
  27. package/dist/chunk-edb07sjp.js.map +14 -0
  28. package/dist/chunk-exd0v74a.js +3012 -0
  29. package/dist/chunk-exd0v74a.js.map +32 -0
  30. package/dist/chunk-fgrme9tr.js +4398 -0
  31. package/dist/chunk-fgrme9tr.js.map +47 -0
  32. package/dist/chunk-fka9eqe1.js +581 -0
  33. package/dist/chunk-fka9eqe1.js.map +11 -0
  34. package/dist/chunk-g82jygez.js +108 -0
  35. package/dist/chunk-g82jygez.js.map +10 -0
  36. package/dist/chunk-ghs2j1fr.js +312 -0
  37. package/dist/chunk-ghs2j1fr.js.map +10 -0
  38. package/dist/chunk-gmy11efb.js +125 -0
  39. package/dist/chunk-gmy11efb.js.map +10 -0
  40. package/dist/chunk-gxma4c70.js +110 -0
  41. package/dist/chunk-gxma4c70.js.map +10 -0
  42. package/dist/chunk-hkdqhm34.js +61 -0
  43. package/dist/chunk-hkdqhm34.js.map +10 -0
  44. package/dist/chunk-hpa0cpzh.js +134 -0
  45. package/dist/chunk-hpa0cpzh.js.map +10 -0
  46. package/dist/chunk-hwy3n31w.js +21 -0
  47. package/dist/chunk-hwy3n31w.js.map +9 -0
  48. package/dist/chunk-j2behpy7.js +214 -0
  49. package/dist/chunk-j2behpy7.js.map +10 -0
  50. package/dist/chunk-jc0at0tc.js +102 -0
  51. package/dist/chunk-jc0at0tc.js.map +10 -0
  52. package/dist/chunk-jkqq5ef6.js +243 -0
  53. package/dist/chunk-jkqq5ef6.js.map +11 -0
  54. package/dist/chunk-jzkw2f01.js +51 -0
  55. package/dist/chunk-jzkw2f01.js.map +10 -0
  56. package/dist/chunk-jzz91jwh.js +70 -0
  57. package/dist/chunk-jzz91jwh.js.map +10 -0
  58. package/dist/chunk-k9z47mab.js +247 -0
  59. package/dist/chunk-k9z47mab.js.map +12 -0
  60. package/dist/chunk-n17yxpa8.js +23 -0
  61. package/dist/chunk-n17yxpa8.js.map +10 -0
  62. package/dist/chunk-naa09ags.js +2143 -0
  63. package/dist/chunk-naa09ags.js.map +15 -0
  64. package/dist/chunk-ns3ke88n.js +142 -0
  65. package/dist/chunk-ns3ke88n.js.map +10 -0
  66. package/dist/chunk-p3cr2ejg.js +1626 -0
  67. package/dist/chunk-p3cr2ejg.js.map +15 -0
  68. package/dist/chunk-q5bcpf23.js +2925 -0
  69. package/dist/chunk-q5bcpf23.js.map +20 -0
  70. package/dist/chunk-rafc55bs.js +424 -0
  71. package/dist/chunk-rafc55bs.js.map +11 -0
  72. package/dist/chunk-rbrxjvbj.js +2830 -0
  73. package/dist/chunk-rbrxjvbj.js.map +23 -0
  74. package/dist/chunk-sa874j5c.js +9 -0
  75. package/dist/chunk-sa874j5c.js.map +10 -0
  76. package/dist/chunk-sgsz2fpq.js +289 -0
  77. package/dist/chunk-sgsz2fpq.js.map +10 -0
  78. package/dist/chunk-sm6fh5c5.js +569 -0
  79. package/dist/chunk-sm6fh5c5.js.map +14 -0
  80. package/dist/chunk-t9zcg8f1.js +499 -0
  81. package/dist/chunk-t9zcg8f1.js.map +13 -0
  82. package/dist/chunk-vp2db42d.js +121 -0
  83. package/dist/chunk-vp2db42d.js.map +10 -0
  84. package/dist/chunk-w13jjf4a.js +46 -0
  85. package/dist/chunk-w13jjf4a.js.map +10 -0
  86. package/dist/chunk-x1arr7ze.js +63 -0
  87. package/dist/chunk-x1arr7ze.js.map +10 -0
  88. package/dist/chunk-xfq68rch.js +583 -0
  89. package/dist/chunk-xfq68rch.js.map +11 -0
  90. package/dist/main.js +132 -20
  91. package/dist/main.js.map +4 -4
  92. package/dist/runtime/scripts/faster-whisper-wrapper.py +87 -0
  93. package/dist/runtime/scripts/mcp-patchright-http.mjs +349 -0
  94. package/dist/runtime/src/agents/api-topic-agent-switch.ts +178 -0
  95. package/dist/runtime/src/agents/archiver.ts +396 -0
  96. package/dist/runtime/src/agents/auth-check.ts +64 -0
  97. package/dist/runtime/src/agents/claude-provider.ts +737 -0
  98. package/dist/runtime/src/agents/claude-registry.ts +115 -0
  99. package/dist/runtime/src/agents/codex-native-multi-agent.ts +177 -0
  100. package/dist/runtime/src/agents/codex-provider.ts +785 -0
  101. package/dist/runtime/src/agents/codex-registry.ts +115 -0
  102. package/dist/runtime/src/agents/codex-tree-kill.ts +365 -0
  103. package/dist/runtime/src/agents/contracts.ts +69 -0
  104. package/dist/runtime/src/agents/deep-map.ts +13 -0
  105. package/dist/runtime/src/agents/execution-host.ts +91 -0
  106. package/dist/runtime/src/agents/fixtures/claude-attachments.jsonl +2 -0
  107. package/dist/runtime/src/agents/fixtures/codex-shell.jsonl +5 -0
  108. package/dist/runtime/src/agents/fork.ts +65 -0
  109. package/dist/runtime/src/agents/hosted-agent.ts +48 -0
  110. package/dist/runtime/src/agents/idle-archiver.ts +128 -0
  111. package/dist/runtime/src/agents/index.ts +205 -0
  112. package/dist/runtime/src/agents/maestro-provider.ts +206 -0
  113. package/dist/runtime/src/agents/maestro-registry.ts +31 -0
  114. package/dist/runtime/src/agents/mcp-tools/ask-user.ts +280 -0
  115. package/dist/runtime/src/agents/mcp-tools/common.ts +21 -0
  116. package/dist/runtime/src/agents/mcp-tools/self-config.ts +220 -0
  117. package/dist/runtime/src/agents/mcp-tools/spawn-subagent.ts +525 -0
  118. package/dist/runtime/src/agents/mcp-tools/visuals.ts +88 -0
  119. package/dist/runtime/src/agents/model-catalog.ts +151 -0
  120. package/dist/runtime/src/agents/registry.ts +24 -0
  121. package/dist/runtime/src/agents/rollout/claude.ts +331 -0
  122. package/dist/runtime/src/agents/rollout/codex.ts +569 -0
  123. package/dist/runtime/src/agents/rollout/shared.ts +205 -0
  124. package/dist/runtime/src/agents/self-config-core.ts +415 -0
  125. package/dist/runtime/src/agents/task-events.ts +44 -0
  126. package/dist/runtime/src/agents/tool-format.ts +352 -0
  127. package/dist/runtime/src/agents/topic-agent-switch.ts +193 -0
  128. package/dist/runtime/src/agents/topic-cleanup.ts +231 -0
  129. package/dist/runtime/src/agents/vault-tool-policy.ts +33 -0
  130. package/dist/runtime/src/application/execute-external-user-turn.ts +23 -0
  131. package/dist/runtime/src/application/submit-user-message.ts +47 -0
  132. package/dist/runtime/src/application/switch-topic-effort.ts +48 -0
  133. package/dist/runtime/src/application/switch-topic-model.ts +79 -0
  134. package/dist/runtime/src/application/topic-service.ts +139 -0
  135. package/dist/runtime/src/application/vault-command.ts +170 -0
  136. package/dist/runtime/src/bus.ts +319 -0
  137. package/dist/runtime/src/index.ts +333 -0
  138. package/dist/runtime/src/mcp/agent-health-server.ts +309 -0
  139. package/dist/runtime/src/mcp/background-bash-server.ts +346 -0
  140. package/dist/runtime/src/mcp/canonical-bridge-config.ts +84 -0
  141. package/dist/runtime/src/mcp/canonical-proxy-server.ts +171 -0
  142. package/dist/runtime/src/mcp/dm-ipc.ts +60 -0
  143. package/dist/runtime/src/mcp/mcp-helpers.ts +40 -0
  144. package/dist/runtime/src/mcp/paddleocr-server.ts +105 -0
  145. package/dist/runtime/src/mcp/peer-bridge.ts +114 -0
  146. package/dist/runtime/src/mcp/runtime-spec.ts +134 -0
  147. package/dist/runtime/src/mcp/session-comm/bridge-ipc-config.ts +27 -0
  148. package/dist/runtime/src/mcp/session-comm/peer-forward.ts +165 -0
  149. package/dist/runtime/src/mcp/session-comm/runtime.ts +115 -0
  150. package/dist/runtime/src/mcp/session-comm/server.ts +649 -0
  151. package/dist/runtime/src/mcp/session-comm/topic-config.ts +131 -0
  152. package/dist/runtime/src/mcp/session-comm/topics.ts +229 -0
  153. package/dist/runtime/src/mcp/stdio-protect.ts +84 -0
  154. package/dist/runtime/src/mcp/system-health-server.ts +130 -0
  155. package/dist/runtime/src/mcp/task-server.ts +173 -0
  156. package/dist/runtime/src/mcp/token-stats-server.ts +98 -0
  157. package/dist/runtime/src/mcp/vault-http.ts +219 -0
  158. package/dist/runtime/src/mcp/vault-run.ts +153 -0
  159. package/dist/runtime/src/mcp/vault-server.ts +104 -0
  160. package/dist/runtime/src/mcp/wiki-server.ts +755 -0
  161. package/dist/runtime/src/media/file-events.ts +33 -0
  162. package/dist/runtime/src/media/text-extractor.ts +312 -0
  163. package/dist/runtime/src/media/video.ts +92 -0
  164. package/dist/runtime/src/outbox/file-ops.ts +210 -0
  165. package/dist/runtime/src/outbox/index.ts +8 -0
  166. package/dist/runtime/src/outbox/utils.ts +51 -0
  167. package/dist/runtime/src/platform/background-bash/manager.ts +279 -0
  168. package/dist/runtime/src/platform/config.ts +371 -0
  169. package/dist/runtime/src/platform/constants.ts +18 -0
  170. package/dist/runtime/src/platform/debug-users.ts +14 -0
  171. package/dist/runtime/src/platform/delay.ts +1 -0
  172. package/dist/runtime/src/platform/error.ts +9 -0
  173. package/dist/runtime/src/platform/file-utils.ts +16 -0
  174. package/dist/runtime/src/platform/jsonl.ts +202 -0
  175. package/dist/runtime/src/platform/lifecycle.ts +174 -0
  176. package/dist/runtime/src/platform/logger.ts +31 -0
  177. package/dist/runtime/src/platform/maestro-bootstrap-env.ts +3 -0
  178. package/dist/runtime/src/platform/mcp-config.ts +865 -0
  179. package/dist/runtime/src/platform/modules.ts +145 -0
  180. package/dist/runtime/src/platform/node-plugins.ts +46 -0
  181. package/dist/runtime/src/platform/playwright/manager.ts +1233 -0
  182. package/dist/runtime/src/prompts/agents/wiki-archiver.md +261 -0
  183. package/dist/runtime/src/prompts/builders.ts +357 -0
  184. package/dist/runtime/src/prompts/sessions/channel-system.md +48 -0
  185. package/dist/runtime/src/prompts/sessions/manager-system.md +37 -0
  186. package/dist/runtime/src/prompts/sessions/topic-system.md +47 -0
  187. package/dist/runtime/src/prompts/sessions/visual-design.md +86 -0
  188. package/dist/runtime/src/query/active-rooms.ts +532 -0
  189. package/dist/runtime/src/query/control.ts +313 -0
  190. package/dist/runtime/src/query/index.ts +24 -0
  191. package/dist/runtime/src/query/session-inbox-cleanup.ts +44 -0
  192. package/dist/runtime/src/query/session-inbox-path.ts +42 -0
  193. package/dist/runtime/src/query/state.ts +94 -0
  194. package/dist/runtime/src/query/types.ts +83 -0
  195. package/dist/runtime/src/runtime/ask-callbacks.ts +153 -0
  196. package/dist/runtime/src/runtime/attachments.ts +151 -0
  197. package/dist/runtime/src/runtime/background-sessions.ts +83 -0
  198. package/dist/runtime/src/runtime/channel-context.ts +107 -0
  199. package/dist/runtime/src/runtime/errors.ts +69 -0
  200. package/dist/runtime/src/runtime/event-heartbeat.ts +77 -0
  201. package/dist/runtime/src/runtime/file-hooks.ts +81 -0
  202. package/dist/runtime/src/runtime/footer.ts +23 -0
  203. package/dist/runtime/src/runtime/inbox.ts +946 -0
  204. package/dist/runtime/src/runtime/tasks.ts +56 -0
  205. package/dist/runtime/src/runtime/topic-config.ts +29 -0
  206. package/dist/runtime/src/runtime/turn-runner.ts +2286 -0
  207. package/dist/runtime/src/runtime/usage-alert.ts +84 -0
  208. package/dist/runtime/src/runtime/visual-html.ts +109 -0
  209. package/dist/runtime/src/runtime/visual-store.ts +430 -0
  210. package/dist/runtime/src/runtime/visuals.ts +171 -0
  211. package/dist/runtime/src/security/sanitize.ts +27 -0
  212. package/dist/runtime/src/security/sensitive-path.ts +34 -0
  213. package/dist/runtime/src/storage/activity-log.ts +152 -0
  214. package/dist/runtime/src/storage/api-messages.ts +617 -0
  215. package/dist/runtime/src/storage/api-topic-brief.ts +127 -0
  216. package/dist/runtime/src/storage/api-topic-config.ts +192 -0
  217. package/dist/runtime/src/storage/api-topics.ts +844 -0
  218. package/dist/runtime/src/storage/app-settings.ts +44 -0
  219. package/dist/runtime/src/storage/browser-profiles.ts +133 -0
  220. package/dist/runtime/src/storage/conversations.ts +268 -0
  221. package/dist/runtime/src/storage/forum/agent-settings.ts +20 -0
  222. package/dist/runtime/src/storage/forum/index.ts +4 -0
  223. package/dist/runtime/src/storage/forum/repository.ts +283 -0
  224. package/dist/runtime/src/storage/forum/schema.ts +300 -0
  225. package/dist/runtime/src/storage/forum-db.ts +23 -0
  226. package/dist/runtime/src/storage/runtime-events.ts +130 -0
  227. package/dist/runtime/src/storage/runtime-leases.ts +189 -0
  228. package/dist/runtime/src/storage/runtime-process-leases.ts +216 -0
  229. package/dist/runtime/src/storage/runtime-topic-state.ts +148 -0
  230. package/dist/runtime/src/storage/runtime-turn-requests.ts +300 -0
  231. package/dist/runtime/src/storage/self-schedules.ts +298 -0
  232. package/dist/runtime/src/storage/session-asks.ts +347 -0
  233. package/dist/runtime/src/storage/sqlite.ts +129 -0
  234. package/dist/runtime/src/storage/tasks.ts +179 -0
  235. package/dist/runtime/src/storage/token-stats.ts +144 -0
  236. package/dist/runtime/src/storage/topic-archive-state.ts +59 -0
  237. package/dist/runtime/src/storage/topic-archive.ts +74 -0
  238. package/dist/runtime/src/storage/topic-settings.ts +67 -0
  239. package/dist/runtime/src/storage/topic-transcript.ts +98 -0
  240. package/dist/runtime/src/storage/vault-crypto.ts +65 -0
  241. package/dist/runtime/src/storage/vault.ts +225 -0
  242. package/dist/runtime/src/storage/wiki-summary-names.ts +33 -0
  243. package/dist/runtime/src/storage/wiki.ts +85 -0
  244. package/dist/runtime/src/topics/create.ts +123 -0
  245. package/dist/runtime/src/topics/derive.ts +398 -0
  246. package/dist/runtime/src/topics/lifecycle.ts +311 -0
  247. package/dist/runtime/src/topics/links.ts +7 -0
  248. package/dist/runtime/src/topics/personal-general.ts +65 -0
  249. package/dist/runtime/src/topics/session.ts +381 -0
  250. package/dist/runtime/src/types/api.ts +497 -0
  251. package/dist/runtime/src/types.ts +255 -0
  252. package/dist/runtime/src/version.ts +1 -0
  253. package/dist/runtime/tsconfig.json +13 -0
  254. package/package.json +16 -3
  255. package/dist/main.d.ts +0 -3
  256. package/dist/main.d.ts.map +0 -1
@@ -0,0 +1,115 @@
1
+ import { existsSync, mkdirSync, readdirSync, renameSync, 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 { encodeClaudeCwd, writeClaudeRollout } from "#agents/rollout/claude";
6
+ import { MODEL_FABLE, MODEL_OPUS, MODEL_SONNET } from "#platform/config";
7
+ import { logger } from "#platform/logger";
8
+ import { CLAUDE_EFFORT_VALUES, type EffortLevel } from "#types";
9
+
10
+ const ALIAS_MAP: Record<string, string> = {
11
+ sonnet: MODEL_SONNET,
12
+ opus: MODEL_OPUS,
13
+ fable: MODEL_FABLE,
14
+ };
15
+
16
+ const VALID_ALIASES = new Set(Object.keys(ALIAS_MAP));
17
+ const VALID_EFFORTS = new Set<EffortLevel>(CLAUDE_EFFORT_VALUES);
18
+
19
+ export const claudeRegistry: AgentRegistry = {
20
+ kind: "claude",
21
+ defaultModel: "sonnet",
22
+ defaultEffort: "high",
23
+
24
+ expandModelAlias(s) {
25
+ return ALIAS_MAP[s] ?? s;
26
+ },
27
+
28
+ validateModel(s) {
29
+ return VALID_ALIASES.has(s);
30
+ },
31
+
32
+ validEfforts: CLAUDE_EFFORT_VALUES,
33
+ validateEffort(s) {
34
+ return VALID_EFFORTS.has(s);
35
+ },
36
+
37
+ footerLabel(model, effort) {
38
+ return effort ? `${model} · ${effort}` : model;
39
+ },
40
+
41
+ writeRollout(opts) {
42
+ // `reuseSessionId` (if any) becomes the SDK resume key + path component,
43
+ // so claude→codex→claude lands at the original `~/.claude/projects/<dir>/<id>.jsonl`
44
+ // instead of orphaning a fresh UUID every switch.
45
+ const { sessionId, rolloutPath } = writeClaudeRollout({
46
+ cwd: opts.cwd,
47
+ entries: opts.entries,
48
+ model: claudeRegistry.expandModelAlias(opts.model ?? claudeRegistry.defaultModel),
49
+ ...(opts.reuseSessionId ? { sessionId: opts.reuseSessionId } : {}),
50
+ });
51
+ return { sessionId, rolloutPath };
52
+ },
53
+
54
+ // Claude has a native fork API in @anthropic-ai/claude-agent-sdk that produces
55
+ // a byte-equivalent copy of the parent rollout. Preserves tool history
56
+ // structurally, so the fork is indistinguishable from the parent on resume.
57
+ //
58
+ // SDK ≥0.3.19x semantics: `dir` is where to FIND the parent session (lookup
59
+ // scope), NOT the destination — passing the derived topic's cwd made lookup
60
+ // fail ("Session … not found in project directory"). The fork file is always
61
+ // written NEXT TO the parent rollout, so after forking we move it into the
62
+ // derived cwd's project dir where a resume from that cwd expects it.
63
+ async forkSession({ parentSessionId, cwd, title }) {
64
+ const { forkSession } = await import("@anthropic-ai/claude-agent-sdk");
65
+ // Omit `dir`: the parent session lives under the SOURCE topic's project
66
+ // dir, which this registry never receives — global search finds it.
67
+ const result = await forkSession(parentSessionId, {
68
+ ...(title ? { title } : {}),
69
+ });
70
+ const projectsRoot = join(homedir(), ".claude", "projects");
71
+ const destDir = join(projectsRoot, encodeClaudeCwd(cwd));
72
+ const destPath = join(destDir, `${result.sessionId}.jsonl`);
73
+ if (!existsSync(destPath)) {
74
+ const sourcePath = readdirSync(projectsRoot)
75
+ .map((d) => join(projectsRoot, d, `${result.sessionId}.jsonl`))
76
+ .find((p) => existsSync(p));
77
+ if (!sourcePath) {
78
+ throw new Error(
79
+ `claude forkSession: fork rollout ${result.sessionId}.jsonl not found under ${projectsRoot}`,
80
+ );
81
+ }
82
+ mkdirSync(destDir, { recursive: true });
83
+ renameSync(sourcePath, destPath);
84
+ }
85
+ return {
86
+ forkId: result.sessionId,
87
+ // Same project-dir layout as cleanupRollouts below — NOT under
88
+ // `cwd/.claude/sessions/`. A wrong path here makes cleanupAgentFork's
89
+ // existsSync always false, so silent-fork rollouts accumulate forever.
90
+ rolloutPath: destPath,
91
+ };
92
+ },
93
+
94
+ // Each Claude rollout lives at `~/.claude/projects/<encoded-cwd>/<sessionId>.jsonl`.
95
+ // The encoded-cwd is deterministic so we can compute the path directly
96
+ // without globbing — fast and robust against the SDK changing date layout.
97
+ async cleanupRollouts({ cwd, sessionIds }) {
98
+ const projectsDir = join(homedir(), ".claude", "projects", encodeClaudeCwd(cwd));
99
+ const failures: unknown[] = [];
100
+ for (const sid of sessionIds) {
101
+ const path = join(projectsDir, `${sid}.jsonl`);
102
+ try {
103
+ unlinkSync(path);
104
+ } catch (e) {
105
+ if ((e as NodeJS.ErrnoException)?.code !== "ENOENT") {
106
+ logger.warn({ err: e, path }, "claude cleanupRollouts: unlink failed");
107
+ failures.push(e);
108
+ }
109
+ }
110
+ }
111
+ if (failures.length > 0) {
112
+ throw new AggregateError(failures, "claude cleanupRollouts failed");
113
+ }
114
+ },
115
+ };
@@ -0,0 +1,177 @@
1
+ import { spawn } from "node:child_process";
2
+ import {
3
+ chmodSync,
4
+ existsSync,
5
+ readFileSync,
6
+ renameSync,
7
+ unlinkSync,
8
+ writeFileSync,
9
+ } from "node:fs";
10
+ import { createRequire } from "node:module";
11
+ import { dirname, join } from "node:path";
12
+ import { NEGOTIUM_VERSION } from "#version";
13
+
14
+ type CodexModel = Record<string, unknown>;
15
+ type CodexModelCache = {
16
+ models?: unknown;
17
+ };
18
+
19
+ const NEGOTIUM_MODEL_CATALOG = "negotium-model-catalog.json";
20
+ const moduleRequire = createRequire(import.meta.url);
21
+
22
+ function codexCliScriptPath(): string {
23
+ const sdkPackagePath = moduleRequire.resolve("@openai/codex-sdk/package.json");
24
+ const sdkRequire = createRequire(sdkPackagePath);
25
+ return join(dirname(sdkRequire.resolve("@openai/codex/package.json")), "bin", "codex.js");
26
+ }
27
+
28
+ async function bootstrapCodexModelCache(codexHome: string, cachePath: string): Promise<void> {
29
+ const child = spawn(process.execPath, [codexCliScriptPath(), "app-server", "--stdio"], {
30
+ env: { ...process.env, CODEX_HOME: codexHome },
31
+ stdio: ["pipe", "pipe", "pipe"],
32
+ });
33
+
34
+ await new Promise<void>((resolve, reject) => {
35
+ let settled = false;
36
+ let stdoutBuffer = "";
37
+ let stderr = "";
38
+ const timer = setTimeout(
39
+ () => finish(new Error("timed out while refreshing the Codex model catalog")),
40
+ 15_000,
41
+ );
42
+
43
+ const finish = (error?: Error) => {
44
+ if (settled) return;
45
+ settled = true;
46
+ clearTimeout(timer);
47
+ try {
48
+ child.stdin.end();
49
+ child.kill();
50
+ } catch {
51
+ // The app server may already have exited after stdin closed.
52
+ }
53
+ if (error) reject(error);
54
+ else if (!existsSync(cachePath)) reject(new Error("Codex did not create its model cache"));
55
+ else resolve();
56
+ };
57
+
58
+ const send = (message: Record<string, unknown>) => {
59
+ child.stdin.write(`${JSON.stringify(message)}\n`);
60
+ };
61
+
62
+ child.stderr.on("data", (chunk) => {
63
+ if (stderr.length < 4_096) stderr += String(chunk);
64
+ });
65
+ child.on("error", (error) => finish(error));
66
+ child.on("exit", (code, signal) => {
67
+ if (!settled) {
68
+ finish(
69
+ new Error(
70
+ `Codex model catalog refresh exited with ${signal ? `signal ${signal}` : `code ${code ?? 1}`}${stderr.trim() ? `: ${stderr.trim()}` : ""}`,
71
+ ),
72
+ );
73
+ }
74
+ });
75
+ child.stdout.on("data", (chunk) => {
76
+ stdoutBuffer += String(chunk);
77
+ for (;;) {
78
+ const newline = stdoutBuffer.indexOf("\n");
79
+ if (newline < 0) break;
80
+ const line = stdoutBuffer.slice(0, newline);
81
+ stdoutBuffer = stdoutBuffer.slice(newline + 1);
82
+ let message: { id?: number; error?: { message?: string } };
83
+ try {
84
+ message = JSON.parse(line) as typeof message;
85
+ } catch {
86
+ continue;
87
+ }
88
+ if (message.id === 1) {
89
+ if (message.error) {
90
+ finish(new Error(message.error.message || "Codex initialization failed"));
91
+ return;
92
+ }
93
+ send({ method: "initialized" });
94
+ send({ id: 2, method: "model/list", params: { includeHidden: true } });
95
+ } else if (message.id === 2) {
96
+ if (message.error) {
97
+ finish(new Error(message.error.message || "Codex model listing failed"));
98
+ } else {
99
+ finish();
100
+ }
101
+ return;
102
+ }
103
+ }
104
+ });
105
+
106
+ send({
107
+ id: 1,
108
+ method: "initialize",
109
+ params: {
110
+ clientInfo: { name: "negotium", version: NEGOTIUM_VERSION },
111
+ capabilities: { experimentalApi: true },
112
+ },
113
+ });
114
+ });
115
+ }
116
+
117
+ export async function ensureCodexModelCache(
118
+ authFilePath: string,
119
+ bootstrap: (codexHome: string, cachePath: string) => Promise<void> = bootstrapCodexModelCache,
120
+ ): Promise<void> {
121
+ const codexHome = dirname(authFilePath);
122
+ const cachePath =
123
+ process.env.NEGOTIUM_CODEX_MODELS_CACHE_FILE ?? join(codexHome, "models_cache.json");
124
+ const hardenedCatalogPath = join(codexHome, NEGOTIUM_MODEL_CATALOG);
125
+ if (existsSync(cachePath) || existsSync(hardenedCatalogPath)) return;
126
+ if (process.env.NEGOTIUM_CODEX_MODELS_CACHE_FILE) {
127
+ throw new Error(`Configured Codex model cache does not exist: ${cachePath}`);
128
+ }
129
+ await bootstrap(codexHome, cachePath);
130
+ }
131
+
132
+ /**
133
+ * Codex 0.144 resolves model metadata before `features.multi_agent=false`, so
134
+ * a model-advertised v1/v2 value still registers native collaboration tools.
135
+ * Feed Codex an authoritative copy of its own catalog with only that field
136
+ * disabled. Runtime MCP delegation remains available independently.
137
+ */
138
+ export function writeCodexCatalogWithNativeMultiAgentDisabled(authFilePath: string): string {
139
+ const codexHome = dirname(authFilePath);
140
+ const sourcePath =
141
+ process.env.NEGOTIUM_CODEX_MODELS_CACHE_FILE ??
142
+ (existsSync(join(codexHome, "models_cache.json"))
143
+ ? join(codexHome, "models_cache.json")
144
+ : join(codexHome, NEGOTIUM_MODEL_CATALOG));
145
+ const outputPath = join(codexHome, NEGOTIUM_MODEL_CATALOG);
146
+
147
+ const parsed = JSON.parse(readFileSync(sourcePath, "utf8")) as CodexModelCache;
148
+ if (!Array.isArray(parsed.models) || parsed.models.length === 0) {
149
+ throw new Error(`Codex model cache has no models: ${sourcePath}`);
150
+ }
151
+
152
+ const models = parsed.models.map((model, index): CodexModel => {
153
+ if (!model || typeof model !== "object" || Array.isArray(model)) {
154
+ throw new Error(`Codex model cache entry ${index} is invalid: ${sourcePath}`);
155
+ }
156
+ return { ...(model as CodexModel), multi_agent_version: "disabled" };
157
+ });
158
+ const contents = `${JSON.stringify({ models }, null, 2)}\n`;
159
+
160
+ if (existsSync(outputPath) && readFileSync(outputPath, "utf8") === contents) {
161
+ return outputPath;
162
+ }
163
+
164
+ const tempPath = `${outputPath}.${process.pid}.${Date.now()}.tmp`;
165
+ try {
166
+ writeFileSync(tempPath, contents, { encoding: "utf8", mode: 0o600 });
167
+ renameSync(tempPath, outputPath);
168
+ chmodSync(outputPath, 0o600);
169
+ } finally {
170
+ try {
171
+ unlinkSync(tempPath);
172
+ } catch {
173
+ // renameSync normally consumed the temporary file.
174
+ }
175
+ }
176
+ return outputPath;
177
+ }