negotium 0.1.8 → 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 (103) hide show
  1. package/README.md +24 -0
  2. package/dist/canonical-mcp-bridge.js +14 -0
  3. package/dist/canonical-mcp-bridge.js.map +9 -0
  4. package/dist/{chunk-rafc55bs.js → chunk-10ct8814.js} +21 -16
  5. package/dist/{chunk-rafc55bs.js.map → chunk-10ct8814.js.map} +2 -2
  6. package/dist/{chunk-j2behpy7.js → chunk-18fzftjf.js} +9 -9
  7. package/dist/chunk-1msje2zf.js +23 -0
  8. package/dist/chunk-1msje2zf.js.map +10 -0
  9. package/dist/{chunk-5ahkew9c.js → chunk-2dgkah1z.js} +6 -106
  10. package/dist/{chunk-5ahkew9c.js.map → chunk-2dgkah1z.js.map} +5 -8
  11. package/dist/{chunk-hkdqhm34.js → chunk-3t4dtm26.js} +1 -1
  12. package/dist/{chunk-w13jjf4a.js → chunk-3vn2vnj1.js} +21 -16
  13. package/dist/{chunk-w13jjf4a.js.map → chunk-3vn2vnj1.js.map} +2 -2
  14. package/dist/{chunk-fgrme9tr.js → chunk-6hknphtc.js} +119 -2463
  15. package/dist/chunk-6hknphtc.js.map +29 -0
  16. package/dist/{chunk-naa09ags.js → chunk-6tfzpcvk.js} +28 -22
  17. package/dist/{chunk-naa09ags.js.map → chunk-6tfzpcvk.js.map} +3 -3
  18. package/dist/{chunk-n17yxpa8.js → chunk-8g0c4as0.js} +3 -3
  19. package/dist/{chunk-e4407g8x.js → chunk-9spx3h9n.js} +23 -18
  20. package/dist/{chunk-e4407g8x.js.map → chunk-9spx3h9n.js.map} +2 -2
  21. package/dist/{chunk-ns3ke88n.js → chunk-e9b2xpff.js} +21 -16
  22. package/dist/{chunk-ns3ke88n.js.map → chunk-e9b2xpff.js.map} +2 -2
  23. package/dist/{chunk-e5pbggsh.js → chunk-eexqsee7.js} +21 -16
  24. package/dist/{chunk-e5pbggsh.js.map → chunk-eexqsee7.js.map} +2 -2
  25. package/dist/{chunk-t9zcg8f1.js → chunk-efyxk97v.js} +3 -2
  26. package/dist/chunk-efyxk97v.js.map +13 -0
  27. package/dist/{chunk-6kctn20e.js → chunk-epqxwkvh.js} +29 -24
  28. package/dist/{chunk-6kctn20e.js.map → chunk-epqxwkvh.js.map} +2 -2
  29. package/dist/{chunk-gxma4c70.js → chunk-f1pzh3xh.js} +22 -17
  30. package/dist/{chunk-gxma4c70.js.map → chunk-f1pzh3xh.js.map} +2 -2
  31. package/dist/{chunk-7xqj7aex.js → chunk-f5xgprg1.js} +2 -20
  32. package/dist/{chunk-7xqj7aex.js.map → chunk-f5xgprg1.js.map} +3 -4
  33. package/dist/{chunk-xfq68rch.js → chunk-f97qybwk.js} +3 -2
  34. package/dist/{chunk-xfq68rch.js.map → chunk-f97qybwk.js.map} +2 -2
  35. package/dist/{chunk-a92a7nrk.js → chunk-fvc7ncxd.js} +1 -1
  36. package/dist/{chunk-sm6fh5c5.js → chunk-g7pvfj5e.js} +20 -15
  37. package/dist/{chunk-sm6fh5c5.js.map → chunk-g7pvfj5e.js.map} +2 -2
  38. package/dist/{chunk-ghs2j1fr.js → chunk-h8vmb9gf.js} +18 -15
  39. package/dist/{chunk-ghs2j1fr.js.map → chunk-h8vmb9gf.js.map} +2 -2
  40. package/dist/{chunk-9p0ww3tk.js → chunk-hgw7n8cm.js} +5 -3
  41. package/dist/{chunk-9p0ww3tk.js.map → chunk-hgw7n8cm.js.map} +2 -2
  42. package/dist/{chunk-gmy11efb.js → chunk-hsq7j9jk.js} +3 -2
  43. package/dist/{chunk-gmy11efb.js.map → chunk-hsq7j9jk.js.map} +2 -2
  44. package/dist/chunk-j0kjscb7.js +53 -0
  45. package/dist/chunk-j0kjscb7.js.map +10 -0
  46. package/dist/chunk-k9z47mab.js.map +1 -1
  47. package/dist/{chunk-0ay6cjdy.js → chunk-kbm35t7w.js} +10 -10
  48. package/dist/{chunk-0ay6cjdy.js.map → chunk-kbm35t7w.js.map} +1 -1
  49. package/dist/{chunk-cds1qxsf.js → chunk-m4veth0c.js} +24 -19
  50. package/dist/{chunk-cds1qxsf.js.map → chunk-m4veth0c.js.map} +2 -2
  51. package/dist/chunk-mhty3nna.js +150 -0
  52. package/dist/chunk-mhty3nna.js.map +11 -0
  53. package/dist/{chunk-jkqq5ef6.js → chunk-mmgh670x.js} +22 -17
  54. package/dist/{chunk-jkqq5ef6.js.map → chunk-mmgh670x.js.map} +2 -2
  55. package/dist/chunk-n0kk5xbq.js +2358 -0
  56. package/dist/chunk-n0kk5xbq.js.map +26 -0
  57. package/dist/{chunk-rbrxjvbj.js → chunk-nmv41qx9.js} +42 -25
  58. package/dist/chunk-nmv41qx9.js.map +23 -0
  59. package/dist/chunk-nqdp52aj.js +113 -0
  60. package/dist/chunk-nqdp52aj.js.map +12 -0
  61. package/dist/{chunk-hpa0cpzh.js → chunk-ns2m25dz.js} +24 -19
  62. package/dist/{chunk-hpa0cpzh.js.map → chunk-ns2m25dz.js.map} +2 -2
  63. package/dist/chunk-pkdmypdd.js +147 -0
  64. package/dist/chunk-pkdmypdd.js.map +11 -0
  65. package/dist/{chunk-p3cr2ejg.js → chunk-rmyfmrrr.js} +39 -31
  66. package/dist/{chunk-p3cr2ejg.js.map → chunk-rmyfmrrr.js.map} +3 -3
  67. package/dist/{chunk-exd0v74a.js → chunk-sn51wnf1.js} +83 -75
  68. package/dist/{chunk-exd0v74a.js.map → chunk-sn51wnf1.js.map} +3 -3
  69. package/dist/{chunk-fka9eqe1.js → chunk-sqhr1a7e.js} +24 -18
  70. package/dist/chunk-sqhr1a7e.js.map +11 -0
  71. package/dist/{chunk-q5bcpf23.js → chunk-tmj5r2zv.js} +63 -55
  72. package/dist/{chunk-q5bcpf23.js.map → chunk-tmj5r2zv.js.map} +3 -3
  73. package/dist/{chunk-jzkw2f01.js → chunk-vefj3880.js} +23 -18
  74. package/dist/{chunk-jzkw2f01.js.map → chunk-vefj3880.js.map} +2 -2
  75. package/dist/{chunk-g82jygez.js → chunk-wry79q55.js} +6 -6
  76. package/dist/{chunk-jc0at0tc.js → chunk-z2xy5n2q.js} +3 -2
  77. package/dist/{chunk-jc0at0tc.js.map → chunk-z2xy5n2q.js.map} +2 -2
  78. package/dist/hosted-agent.js +53 -0
  79. package/dist/hosted-agent.js.map +10 -0
  80. package/dist/main.js +12 -12
  81. package/dist/main.js.map +1 -1
  82. package/dist/runtime/src/agents/hosted-agent.ts +1 -0
  83. package/dist/runtime/src/application/submit-user-message.ts +3 -0
  84. package/dist/runtime/src/mcp/runtime-spec.ts +3 -0
  85. package/dist/runtime/src/platform/mcp-config.ts +13 -0
  86. package/dist/runtime/src/prompts/builders.ts +20 -9
  87. package/dist/runtime/src/query/active-rooms.ts +2 -0
  88. package/dist/runtime/src/runtime/turn-runner.ts +13 -1
  89. package/dist/runtime/src/storage/runtime-turn-requests.ts +1 -0
  90. package/dist/runtime/src/types.ts +2 -0
  91. package/dist/runtime/src/version.ts +1 -1
  92. package/package.json +13 -1
  93. package/dist/chunk-6cpn7ht2.js +0 -287
  94. package/dist/chunk-6cpn7ht2.js.map +0 -13
  95. package/dist/chunk-fgrme9tr.js.map +0 -47
  96. package/dist/chunk-fka9eqe1.js.map +0 -11
  97. package/dist/chunk-rbrxjvbj.js.map +0 -23
  98. package/dist/chunk-t9zcg8f1.js.map +0 -13
  99. /package/dist/{chunk-j2behpy7.js.map → chunk-18fzftjf.js.map} +0 -0
  100. /package/dist/{chunk-hkdqhm34.js.map → chunk-3t4dtm26.js.map} +0 -0
  101. /package/dist/{chunk-n17yxpa8.js.map → chunk-8g0c4as0.js.map} +0 -0
  102. /package/dist/{chunk-a92a7nrk.js.map → chunk-fvc7ncxd.js.map} +0 -0
  103. /package/dist/{chunk-g82jygez.js.map → chunk-wry79q55.js.map} +0 -0
@@ -8,2412 +8,65 @@ import {
8
8
  getMessagesForTopicAfterRowid,
9
9
  listApiMessagesByKind,
10
10
  updateApiMessageSubagentCard
11
- } from "./chunk-0ay6cjdy.js";
11
+ } from "./chunk-kbm35t7w.js";
12
12
  import {
13
13
  getTopic,
14
14
  getTopicMemoryOrigin,
15
15
  listTopics
16
- } from "./chunk-xfq68rch.js";
16
+ } from "./chunk-f97qybwk.js";
17
17
  import {
18
- TOPIC_MAINTENANCE_STALE_MS,
19
- TURN_LEASE_STALE_MS,
20
- getRoomQuery,
21
- getRuntimeTopicEpoch
22
- } from "./chunk-t9zcg8f1.js";
23
- import {
24
- Database,
25
- db
26
- } from "./chunk-7xqj7aex.js";
27
- import {
28
- claudeRegistry,
29
- encodeClaudeCwd,
30
- getRegistry,
31
- migrateCodexRolloutNativeMultiAgentMetadata,
32
- readLatestCodexContextUsage
33
- } from "./chunk-5ahkew9c.js";
34
- import {
35
- appendConversationEvent,
36
- readConversation,
37
- sanitizeFileName,
38
- sanitizeTopicName
39
- } from "./chunk-6cpn7ht2.js";
40
- import {
41
- AGENTS_PROMPTS_DIR,
42
- AGENT_HEALTH_SERVER,
43
- BACKGROUND_BASH_SERVER,
44
- BG_BASH_BASE_PORT,
45
- BG_BASH_MAX_PORT,
46
- CANONICAL_MCP_PROXY_SERVER,
47
- CLAUDE_EXECUTABLE,
48
- DATA_DIR,
49
- FALLBACK_AGENT,
50
- FILE_TAG_REGEX,
51
- NEGOTIUM_PORT,
52
- PADDLEOCR_SERVER,
53
- PROJECT_ROOT,
54
- RESOURCES_DIR,
55
- RUNTIME_MCP_SECRET,
56
- SESSION_COMM_SERVER,
57
- SHARED_WIKI_DIR,
58
- SYSTEM_HEALTH_SERVER,
59
- TASK_SERVER,
60
- TOKEN_STATS_SERVER,
61
- TSCONFIG_PATH,
62
- TSX_BIN,
63
- USERS_LOG_DIR,
64
- VAULT_MASTER_KEY,
65
- VAULT_SERVER,
66
- WIKI_SERVER,
67
- WORKSPACE_DIR,
68
- codexAuthFilePath,
69
- isAgentKind,
70
- logger,
71
- resolveTopicWorkspaceDir
72
- } from "./chunk-k9z47mab.js";
73
- import {
74
- __require,
75
- __toESM
76
- } from "./chunk-hwy3n31w.js";
77
-
78
- // ../../packages/core/src/agents/codex-tree-kill.ts
79
- import { execFileSync } from "child_process";
80
- var SIGKILL_DELAY_MS = 5000;
81
- var CODEX_PROC_NAME_MATCH = /codex/i;
82
- var TREE_DEPTH_CAP = 16;
83
- function getDirectChildren(parentPid) {
84
- try {
85
- const out = execFileSync("pgrep", ["-P", String(parentPid)], {
86
- encoding: "utf8",
87
- stdio: ["ignore", "pipe", "ignore"]
88
- }).trim();
89
- if (!out)
90
- return [];
91
- return out.split(`
92
- `).map((s) => Number(s)).filter((n) => Number.isFinite(n) && n > 0);
93
- } catch {
94
- return [];
95
- }
96
- }
97
- function getProcName(pid) {
98
- try {
99
- return execFileSync("ps", ["-p", String(pid), "-o", "comm="], {
100
- encoding: "utf8",
101
- stdio: ["ignore", "pipe", "ignore"]
102
- }).trim();
103
- } catch {
104
- return "";
105
- }
106
- }
107
- function getProcLstart(pid) {
108
- try {
109
- return execFileSync("ps", ["-p", String(pid), "-o", "lstart="], {
110
- encoding: "utf8",
111
- stdio: ["ignore", "pipe", "ignore"]
112
- }).trim();
113
- } catch {
114
- return "";
115
- }
116
- }
117
- function collectTree(rootPid, out = new Set, depth = 0) {
118
- if (out.has(rootPid))
119
- return out;
120
- out.add(rootPid);
121
- if (depth >= TREE_DEPTH_CAP) {
122
- logger.warn({ rootPid, depth, cap: TREE_DEPTH_CAP }, "codex-tree-kill: tree depth cap reached, descendants beyond this point not collected");
123
- return out;
124
- }
125
- for (const child of getDirectChildren(rootPid)) {
126
- collectTree(child, out, depth + 1);
127
- }
128
- return out;
129
- }
130
- function snapshotCodexChildren() {
131
- const out = new Map;
132
- for (const pid of getDirectChildren(process.pid)) {
133
- const name = getProcName(pid);
134
- if (!name || !CODEX_PROC_NAME_MATCH.test(name))
135
- continue;
136
- const lstart = getProcLstart(pid);
137
- out.set(pid, lstart);
138
- }
139
- return out;
140
- }
141
- var globalOwnedCodexPids = new Set;
142
- function registerOwnedCodexPids(pids) {
143
- for (const p of pids)
144
- globalOwnedCodexPids.add(p);
145
- }
146
- function unregisterOwnedCodexPids(pids) {
147
- for (const p of pids)
148
- globalOwnedCodexPids.delete(p);
149
- }
150
- function findNewCodexChildren(baseline) {
151
- const current = snapshotCodexChildren();
152
- const novel = [];
153
- for (const [pid, lstart] of current) {
154
- const baselineLstart = baseline.get(pid);
155
- if (baselineLstart !== undefined && baselineLstart === lstart)
156
- continue;
157
- if (globalOwnedCodexPids.has(pid))
158
- continue;
159
- novel.push(pid);
160
- }
161
- return novel;
162
- }
163
- var codexSpawnChain = Promise.resolve();
164
- async function acquireCodexSpawnLock() {
165
- const prev = codexSpawnChain;
166
- let release;
167
- let released = false;
168
- codexSpawnChain = new Promise((r) => {
169
- release = () => {
170
- if (released)
171
- return;
172
- released = true;
173
- r();
174
- };
175
- });
176
- await prev;
177
- return release;
178
- }
179
- function isAlive(pid) {
180
- try {
181
- process.kill(pid, 0);
182
- return true;
183
- } catch {
184
- return false;
185
- }
186
- }
187
- function isSameProcess(pid, lstart) {
188
- return lstart !== "" && isAlive(pid) && getProcLstart(pid) === lstart;
189
- }
190
- function signalCodexTrees(rootPids) {
191
- if (rootPids.length === 0)
192
- return null;
193
- const targets = new Map;
194
- for (const root of rootPids) {
195
- for (const pid of collectTree(root)) {
196
- if (!targets.has(pid))
197
- targets.set(pid, getProcLstart(pid));
198
- }
199
- }
200
- logger.warn({ rootPids, totalPids: targets.size }, "codex-tree-kill: SIGTERM process tree");
201
- for (const pid of targets.keys()) {
202
- try {
203
- process.kill(pid, "SIGTERM");
204
- } catch {}
205
- }
206
- return { rootPids, targets };
207
- }
208
- function hasSameProcessSurvivors(targets) {
209
- for (const [pid, lstart] of targets) {
210
- if (isSameProcess(pid, lstart))
211
- return true;
212
- }
213
- return false;
214
- }
215
- function killCodexStragglers({ rootPids, targets }) {
216
- const aliveTargets = [];
217
- let droppedAsPossibleReuse = 0;
218
- for (const [pid, lstart] of targets) {
219
- if (isSameProcess(pid, lstart)) {
220
- aliveTargets.push(pid);
221
- } else if (isAlive(pid)) {
222
- droppedAsPossibleReuse++;
223
- }
224
- }
225
- if (aliveTargets.length === 0)
226
- return;
227
- const reachable = new Set(aliveTargets);
228
- for (const root of rootPids) {
229
- const rootLstart = targets.get(root);
230
- if (rootLstart && isSameProcess(root, rootLstart)) {
231
- for (const pid of collectTree(root))
232
- reachable.add(pid);
233
- }
234
- }
235
- const stragglers = [];
236
- for (const pid of reachable) {
237
- const lstart = targets.get(pid);
238
- if (lstart && isSameProcess(pid, lstart))
239
- stragglers.push(pid);
240
- else if (isAlive(pid))
241
- droppedAsPossibleReuse++;
242
- }
243
- logger.warn({
244
- aliveRoots: rootPids.filter((pid) => {
245
- const lstart = targets.get(pid);
246
- return lstart ? isSameProcess(pid, lstart) : false;
247
- }),
248
- totalStragglers: stragglers.length,
249
- droppedAsPossibleReuse
250
- }, "codex-tree-kill: SIGKILL fallback after SIGTERM timeout");
251
- for (const pid of stragglers) {
252
- try {
253
- process.kill(pid, "SIGKILL");
254
- } catch {}
255
- }
256
- }
257
- function killCodexTrees(rootPids) {
258
- const plan = signalCodexTrees(rootPids);
259
- if (!plan)
260
- return;
261
- const killTimer = setTimeout(() => {
262
- killCodexStragglers(plan);
263
- }, SIGKILL_DELAY_MS);
264
- killTimer.unref?.();
265
- }
266
- async function killOwnedCodexTreesForShutdown(graceMs = 3000) {
267
- const roots = new Set(globalOwnedCodexPids);
268
- for (const pid of snapshotCodexChildren().keys())
269
- roots.add(pid);
270
- const plan = signalCodexTrees([...roots]);
271
- if (!plan)
272
- return;
273
- const deadline = Date.now() + Math.max(0, graceMs);
274
- while (Date.now() < deadline && hasSameProcessSurvivors(plan.targets)) {
275
- await new Promise((resolve) => setTimeout(resolve, Math.min(100, Math.max(1, deadline - Date.now()))));
276
- }
277
- killCodexStragglers(plan);
278
- }
279
-
280
- // ../../packages/core/src/security/sensitive-path.ts
281
- import { realpathSync } from "fs";
282
- import { resolve } from "path";
283
- var SENSITIVE_PATH_PATTERNS = [
284
- /\/\.env(\.|$)/i,
285
- /\/\.ssh\//i,
286
- /\/\.aws\//i,
287
- /\/\.gnupg\//i,
288
- /\/\.netrc$/i,
289
- /\/\.npmrc$/i,
290
- /\/(id_rsa|id_ed25519|id_ecdsa|id_dsa)(\.pub)?$/i,
291
- /\.(pem|key|p12|pfx|cer|crt)$/i,
292
- /\/Library\/Keychains\//i,
293
- /\/vault\.db(-wal|-shm|-journal)?$/i,
294
- /\/vault-master-key$/i,
295
- /\/runtime-mcp-secret$/i,
296
- /\/sessions\.db(-wal|-shm|-journal)?$/i
297
- ];
298
- function isSensitivePath(filePath) {
299
- const normalized = resolve(filePath);
300
- if (SENSITIVE_PATH_PATTERNS.some((p) => p.test(normalized)))
301
- return true;
302
- try {
303
- const real = realpathSync(normalized);
304
- if (real !== normalized)
305
- return SENSITIVE_PATH_PATTERNS.some((p) => p.test(real));
306
- } catch {}
307
- return false;
308
- }
309
-
310
- // ../../packages/core/src/storage/vault.ts
311
- import { chmodSync, mkdirSync } from "fs";
312
- import { join } from "path";
313
-
314
- // ../../packages/core/src/storage/vault-crypto.ts
315
- import { createCipheriv, createDecipheriv, createHash, randomBytes } from "crypto";
316
- var ENVELOPE_PREFIX = "otium-vault:v1:";
317
- var IV_BYTES = 12;
318
- var KEY_BYTES = 32;
319
- function encryptionKey() {
320
- return createHash("sha256").update("otium-vault-value-v1\x00", "utf8").update(VAULT_MASTER_KEY, "utf8").digest().subarray(0, KEY_BYTES);
321
- }
322
- function aad(userId, key) {
323
- return Buffer.from(`${userId}\x00${key.toUpperCase()}`, "utf8");
324
- }
325
- function isEncryptedVaultValue(value) {
326
- return value.startsWith(ENVELOPE_PREFIX);
327
- }
328
- function encryptVaultValue(userId, key, value) {
329
- const iv = randomBytes(IV_BYTES);
330
- const cipher = createCipheriv("aes-256-gcm", encryptionKey(), iv);
331
- cipher.setAAD(aad(userId, key));
332
- const ciphertext = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
333
- const tag = cipher.getAuthTag();
334
- return `${ENVELOPE_PREFIX}${iv.toString("base64url")}.${ciphertext.toString("base64url")}.${tag.toString("base64url")}`;
335
- }
336
- function decryptVaultValue(userId, key, storedValue) {
337
- if (!isEncryptedVaultValue(storedValue)) {
338
- return { value: storedValue, legacyPlaintext: true };
339
- }
340
- const encoded = storedValue.slice(ENVELOPE_PREFIX.length);
341
- const [ivPart, ciphertextPart, tagPart, ...extra] = encoded.split(".");
342
- if (!ivPart || ciphertextPart === undefined || !tagPart || extra.length > 0) {
343
- throw new Error("Invalid encrypted vault value");
344
- }
345
- const iv = Buffer.from(ivPart, "base64url");
346
- const ciphertext = Buffer.from(ciphertextPart, "base64url");
347
- const tag = Buffer.from(tagPart, "base64url");
348
- if (iv.length !== IV_BYTES || tag.length !== 16) {
349
- throw new Error("Invalid encrypted vault value");
350
- }
351
- const decipher = createDecipheriv("aes-256-gcm", encryptionKey(), iv);
352
- decipher.setAAD(aad(userId, key));
353
- decipher.setAuthTag(tag);
354
- const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
355
- return { value: plaintext.toString("utf8"), legacyPlaintext: false };
356
- }
357
-
358
- // ../../packages/core/src/storage/vault.ts
359
- var VAULT_DB_PATH = join(DATA_DIR, "vault.db");
360
- mkdirSync(DATA_DIR, { recursive: true });
361
- var vaultDb = new Database(VAULT_DB_PATH, { create: true });
362
- chmodSync(VAULT_DB_PATH, 384);
363
- vaultDb.exec("PRAGMA journal_mode = WAL");
364
- vaultDb.exec("PRAGMA busy_timeout = 5000");
365
- vaultDb.exec(`
366
- CREATE TABLE IF NOT EXISTS vault (
367
- user_id TEXT NOT NULL,
368
- key TEXT NOT NULL,
369
- value TEXT NOT NULL,
370
- description TEXT NOT NULL DEFAULT '',
371
- PRIMARY KEY (user_id, key)
372
- )
373
- `);
374
- {
375
- const cols = vaultDb.prepare("PRAGMA table_info(vault)").all();
376
- const uid = cols.find((c) => c.name === "user_id");
377
- if (uid && uid.type.toUpperCase() === "INTEGER") {
378
- vaultDb.exec("BEGIN");
379
- vaultDb.exec(`
380
- CREATE TABLE vault_migrated (
381
- user_id TEXT NOT NULL,
382
- key TEXT NOT NULL,
383
- value TEXT NOT NULL,
384
- description TEXT NOT NULL DEFAULT '',
385
- PRIMARY KEY (user_id, key)
386
- )
387
- `);
388
- vaultDb.exec("INSERT INTO vault_migrated SELECT CAST(user_id AS TEXT), key, value, description FROM vault");
389
- vaultDb.exec("DROP TABLE vault");
390
- vaultDb.exec("ALTER TABLE vault_migrated RENAME TO vault");
391
- vaultDb.exec("COMMIT");
392
- }
393
- }
394
- var VAULT_KEY_PATTERN = /^[A-Z][A-Z0-9_]{0,127}$/;
395
- var VAULT_VALUE_MIN_BYTES = 4;
396
- var VAULT_VALUE_MAX_BYTES = 64 * 1024;
397
- var VAULT_DESCRIPTION_MAX_LENGTH = 500;
398
- function normalizeVaultKey(key) {
399
- return key.trim().toUpperCase();
400
- }
401
- function validateVaultKey(key) {
402
- return VAULT_KEY_PATTERN.test(normalizeVaultKey(key));
403
- }
404
- function decryptRow(userId, key, storedValue) {
405
- const decoded = decryptVaultValue(userId, key, storedValue);
406
- if (decoded.legacyPlaintext) {
407
- vaultDb.prepare("UPDATE vault SET value = ? WHERE user_id = ? AND key = ? AND value = ?").run(encryptVaultValue(userId, key, decoded.value), userId, key, storedValue);
408
- }
409
- return decoded.value;
410
- }
411
- function vaultListWithValues(userId) {
412
- const rows = vaultDb.prepare("SELECT key, description, value FROM vault WHERE user_id = ? ORDER BY key").all(userId);
413
- return rows.map((row) => ({
414
- key: row.key,
415
- description: row.description,
416
- value: decryptRow(userId, row.key, row.value)
417
- }));
418
- }
419
- function vaultSet(userId, key, value, description = "") {
420
- const normalizedKey = normalizeVaultKey(key);
421
- vaultDb.prepare(`INSERT INTO vault (user_id, key, value, description) VALUES (?, ?, ?, ?)
422
- ON CONFLICT (user_id, key) DO UPDATE
423
- SET value = excluded.value, description = excluded.description`).run(userId, normalizedKey, encryptVaultValue(userId, normalizedKey, value), description);
424
- }
425
- function vaultHasKey(userId, key) {
426
- const row = vaultDb.prepare("SELECT 1 FROM vault WHERE user_id = ? AND key = ?").get(userId, normalizeVaultKey(key));
427
- return row != null;
428
- }
429
- function vaultDel(userId, key) {
430
- const result = vaultDb.prepare("DELETE FROM vault WHERE user_id = ? AND key = ?").run(userId, normalizeVaultKey(key));
431
- return result.changes > 0;
432
- }
433
- function vaultList(userId) {
434
- return vaultDb.prepare("SELECT key, description FROM vault WHERE user_id = ? ORDER BY key").all(userId);
435
- }
436
- function valueReferencesVaultKey(userId, value) {
437
- const keys = new Set(vaultList(userId).map((entry) => entry.key));
438
- const visit = (candidate) => {
439
- if (typeof candidate === "string") {
440
- for (const match of candidate.matchAll(/\{\{([^}]+)\}\}/g)) {
441
- if (keys.has(normalizeVaultKey(match[1] ?? "")))
442
- return true;
443
- }
444
- return false;
445
- }
446
- if (Array.isArray(candidate))
447
- return candidate.some(visit);
448
- if (candidate && typeof candidate === "object") {
449
- return Object.values(candidate).some(visit);
450
- }
451
- return false;
452
- };
453
- return visit(value);
454
- }
455
- function encodedSecretForms(value) {
456
- const forms = new Set([
457
- value,
458
- encodeURIComponent(value),
459
- Buffer.from(value, "utf8").toString("base64"),
460
- Buffer.from(value, "utf8").toString("base64url"),
461
- Buffer.from(value, "utf8").toString("hex")
462
- ]);
463
- forms.delete("");
464
- return [...forms].sort((a, b) => b.length - a.length);
465
- }
466
- function redactVaultSecrets(userId, text) {
467
- const candidates = vaultListWithValues(userId).flatMap((entry) => encodedSecretForms(entry.value).map((form) => ({ form, key: entry.key }))).sort((a, b) => b.form.length - a.form.length || a.key.localeCompare(b.key));
468
- if (candidates.length === 0)
469
- return text;
470
- const candidatesByFirstCharacter = new Map;
471
- for (const candidate of candidates) {
472
- const first = candidate.form[0];
473
- if (!first)
474
- continue;
475
- const bucket = candidatesByFirstCharacter.get(first) ?? [];
476
- bucket.push(candidate);
477
- candidatesByFirstCharacter.set(first, bucket);
478
- }
479
- let redacted = "";
480
- let offset = 0;
481
- while (offset < text.length) {
482
- const match = candidatesByFirstCharacter.get(text[offset] ?? "")?.find((candidate) => text.startsWith(candidate.form, offset));
483
- if (!match) {
484
- redacted += text[offset];
485
- offset += 1;
486
- continue;
487
- }
488
- redacted += `[REDACTED:${match.key}]`;
489
- offset += match.form.length;
490
- }
491
- return redacted;
492
- }
493
-
494
- // ../../packages/core/src/mcp/runtime-spec.ts
495
- import { createHmac, timingSafeEqual } from "crypto";
496
- var RUNTIME_MCP_KEY = "runtime";
497
- var RUNTIME_MCP_BASE_PATH = "/mcp/runtime";
498
- var TOKEN_TTL_MS = 4 * 60 * 60 * 1000;
499
- var CLAUDE_MCP_TOOL_TIMEOUT_MS = 600000;
500
- var runtimePort = NEGOTIUM_PORT;
501
- function setRuntimeMcpPort(port) {
502
- runtimePort = port;
503
- }
504
- function getRuntimeMcpPort() {
505
- return runtimePort;
506
- }
507
- function encodeTokenPart(value) {
508
- return Buffer.from(JSON.stringify(value), "utf-8").toString("base64url");
509
- }
510
- function decodeTokenPart(part) {
511
- return JSON.parse(Buffer.from(part, "base64url").toString("utf-8"));
512
- }
513
- function signTokenPayload(payloadPart) {
514
- return createHmac("sha256", RUNTIME_MCP_SECRET).update(payloadPart).digest("base64url");
515
- }
516
- function safeEqual(a, b) {
517
- const aa = Buffer.from(a);
518
- const bb = Buffer.from(b);
519
- return aa.length === bb.length && timingSafeEqual(aa, bb);
520
- }
521
- function isRuntimeMcpContext(value) {
522
- if (!value || typeof value !== "object" || Array.isArray(value))
523
- return false;
524
- const ctx = value;
525
- return typeof ctx.userId === "string" && typeof ctx.topicId === "string" && typeof ctx.topicTitle === "string" && typeof ctx.cwd === "string" && typeof ctx.agent === "string" && isAgentKind(ctx.agent) && (ctx.queryId === undefined || typeof ctx.queryId === "string") && (ctx.model === undefined || typeof ctx.model === "string") && (ctx.currentUserPrompt === undefined || typeof ctx.currentUserPrompt === "string") && (ctx.autoContinue === undefined || typeof ctx.autoContinue === "boolean") && (ctx.peerBridge === undefined || typeof ctx.peerBridge.hubCellId === "string" && typeof ctx.peerBridge.hostTopicId === "string" && typeof ctx.peerBridge.hostQueryId === "string" && typeof ctx.peerBridge.canSpawnSubagents === "boolean");
526
- }
527
- function issueRuntimeMcpToken(ctx) {
528
- const payloadPart = encodeTokenPart({
529
- v: 1,
530
- exp: Date.now() + TOKEN_TTL_MS,
531
- ctx
532
- });
533
- return `${payloadPart}.${signTokenPayload(payloadPart)}`;
534
- }
535
- function resolveRuntimeMcpToken(token) {
536
- if (!token)
537
- return null;
538
- const [payloadPart, signature, extra] = token.split(".");
539
- if (!payloadPart || !signature || extra !== undefined)
540
- return null;
541
- if (!safeEqual(signature, signTokenPayload(payloadPart)))
542
- return null;
543
- try {
544
- const payload = decodeTokenPart(payloadPart);
545
- if (payload.v !== 1 || typeof payload.exp !== "number" || payload.exp <= Date.now())
546
- return null;
547
- if (!isRuntimeMcpContext(payload.ctx))
548
- return null;
549
- return payload.ctx;
550
- } catch {
551
- return null;
552
- }
553
- }
554
- function buildRuntimeMcpSpec(agent, ctx) {
555
- const token = issueRuntimeMcpToken(ctx);
556
- const base = `http://127.0.0.1:${runtimePort}${RUNTIME_MCP_BASE_PATH}`;
557
- const query = `token=${encodeURIComponent(token)}`;
558
- if (agent === "codex")
559
- return { url: `${base}/mcp?${query}` };
560
- return {
561
- type: "sse",
562
- url: `${base}/sse?${query}`,
563
- timeout: CLAUDE_MCP_TOOL_TIMEOUT_MS
564
- };
565
- }
566
-
567
- // ../../packages/core/src/platform/mcp-config.ts
568
- import { createHmac as createHmac2 } from "crypto";
569
-
570
- // ../../packages/core/src/mcp/canonical-bridge-config.ts
571
- var registrations = [];
572
- var turnLeases = new Map;
573
- function turnKey(scope) {
574
- return JSON.stringify([
575
- scope.userId,
576
- scope.topicId,
577
- scope.queryId,
578
- scope.peerBridge.hubCellId,
579
- scope.peerBridge.hostTopicId,
580
- scope.peerBridge.hostQueryId
581
- ]);
582
- }
583
- function registerCanonicalMcpBridgeEnvProvider(provider) {
584
- const registration = { id: Symbol("canonical-mcp-bridge"), provider };
585
- registrations.push(registration);
586
- let disposed = false;
587
- return () => {
588
- if (disposed)
589
- return;
590
- disposed = true;
591
- const index = registrations.findIndex((entry) => entry.id === registration.id);
592
- if (index >= 0)
593
- registrations.splice(index, 1);
594
- };
595
- }
596
- function canonicalMcpBridgeEnv(scope) {
597
- const lease = registrations.at(-1)?.provider(scope);
598
- if (!lease)
599
- return;
600
- const key = turnKey(scope);
601
- const leases = turnLeases.get(key) ?? new Set;
602
- leases.add(lease.revoke);
603
- turnLeases.set(key, leases);
604
- return lease.env;
605
- }
606
-
607
- // ../../packages/core/src/mcp/session-comm/bridge-ipc-config.ts
608
- var registrations2 = [];
609
- function registerPeerSessionBridgeIpcConfig(config) {
610
- const registration = { id: Symbol("peer-session-bridge-ipc"), config: { ...config } };
611
- registrations2.push(registration);
612
- let disposed = false;
613
- return () => {
614
- if (disposed)
615
- return;
616
- disposed = true;
617
- const index = registrations2.findIndex((entry) => entry.id === registration.id);
618
- if (index >= 0)
619
- registrations2.splice(index, 1);
620
- };
621
- }
622
- function peerSessionBridgeIpcEnv() {
623
- const active = registrations2.at(-1)?.config;
624
- if (!active)
625
- return;
626
- return {
627
- NEGOTIUM_PEER_SESSION_BRIDGE_URL: active.url,
628
- NEGOTIUM_PEER_SESSION_BRIDGE_TOKEN: active.token
629
- };
630
- }
631
-
632
- // ../../packages/core/src/platform/mcp-config.ts
633
- function buildStdioMcpServer(agent, serverFile, serverArgs, env) {
634
- if (agent === "codex") {
635
- return {
636
- command: "node",
637
- args: [TSX_BIN, serverFile, ...serverArgs],
638
- env: { TSX_TSCONFIG_PATH: TSCONFIG_PATH, ...env }
639
- };
640
- }
641
- return {
642
- command: "bun",
643
- args: ["run", serverFile, ...serverArgs],
644
- ...env ? { env } : {}
645
- };
646
- }
647
- var _playwrightUnavailableNotifier;
648
- var _playwrightUnavailableLastNotifiedAt = new Map;
649
- var _PLAYWRIGHT_UNAVAILABLE_COOLDOWN_MS = 5 * 60000;
650
- var _playwrightUnavailableThisTurn = new Set;
651
- function _playwrightUnavailableKey(userId, topic) {
652
- return `${userId}:${topic ?? ""}`;
653
- }
654
- function setPlaywrightUnavailableNotifier(cb) {
655
- _playwrightUnavailableNotifier = cb;
656
- }
657
- function markPlaywrightUnavailable(ctx) {
658
- _markPlaywrightUnavailable(ctx);
659
- }
660
- function _markPlaywrightUnavailable(ctx) {
661
- _playwrightUnavailableThisTurn.add(_playwrightUnavailableKey(ctx.userId, ctx.topic));
662
- if (!_playwrightUnavailableNotifier)
663
- return;
664
- const key = _playwrightUnavailableKey(ctx.userId, ctx.topic);
665
- const now = Date.now();
666
- const last = _playwrightUnavailableLastNotifiedAt.get(key) ?? 0;
667
- if (now - last < _PLAYWRIGHT_UNAVAILABLE_COOLDOWN_MS)
668
- return;
669
- _playwrightUnavailableLastNotifiedAt.set(key, now);
670
- try {
671
- _playwrightUnavailableNotifier(ctx);
672
- } catch (err) {
673
- logger.warn({ err }, "playwright unavailable notifier threw");
674
- }
675
- }
676
- function consumePlaywrightUnavailable(userId, topic) {
677
- const key = _playwrightUnavailableKey(userId, topic);
678
- if (!_playwrightUnavailableThisTurn.has(key))
679
- return false;
680
- _playwrightUnavailableThisTurn.delete(key);
681
- return true;
682
- }
683
- var CODEX_BROWSER_CAPABILITY_ENV = "NEGOTIUM_BROWSER_CAPABILITY";
684
- function browserOwnerCapability(capability, owner) {
685
- return createHmac2("sha256", capability).update(owner).digest("hex");
686
- }
687
- function browserOwnerForContext(ctx) {
688
- if (ctx.topicId)
689
- return `topic:${ctx.topicId}`;
690
- if (ctx.userId && ctx.session)
691
- return `user:${ctx.userId}:${ctx.session}`;
692
- return;
693
- }
694
- function playwrightTransport(port, owner, capability, agent) {
695
- const ownerCapability = browserOwnerCapability(capability, owner);
696
- if (agent === "codex") {
697
- return {
698
- url: `http://127.0.0.1:${port}/mcp`,
699
- http_headers: { "X-Browser-Owner": owner },
700
- env_http_headers: { "X-Browser-Capability": CODEX_BROWSER_CAPABILITY_ENV }
701
- };
702
- }
703
- if (agent === "maestro") {
704
- const query = new URLSearchParams({ owner, capability: ownerCapability });
705
- return {
706
- type: "sse",
707
- url: `http://127.0.0.1:${port}/sse?${query}`
708
- };
709
- }
710
- return {
711
- type: "sse",
712
- url: `http://127.0.0.1:${port}/sse`,
713
- headers: { "X-Browser-Owner": owner, "X-Browser-Capability": ownerCapability }
714
- };
715
- }
716
- function longLivedHttpMcp(agent, port) {
717
- return agent === "codex" ? { url: `http://127.0.0.1:${port}/mcp` } : { type: "sse", url: `http://127.0.0.1:${port}/sse` };
718
- }
719
- var MCP_CATALOG = {
720
- playwright: {
721
- scopes: ["dm", "forum", "fork", "cron"],
722
- build({ userId, session, topicId, playwrightPort, playwrightCapability, agent }) {
723
- if (playwrightPort && playwrightCapability) {
724
- const owner = browserOwnerForContext({ userId, session, topicId });
725
- if (!owner)
726
- return null;
727
- return playwrightTransport(playwrightPort, owner, playwrightCapability, agent);
728
- }
729
- _markPlaywrightUnavailable({
730
- userId,
731
- topic: session,
732
- agent
733
- });
734
- return null;
735
- }
736
- },
737
- [RUNTIME_MCP_KEY]: {
738
- scopes: ["forum", "manager", "fork", "cron"],
739
- forumRequired: true,
740
- build({
741
- userId,
742
- session,
743
- topicId,
744
- queryId,
745
- agent,
746
- cwd,
747
- model,
748
- currentUserPrompt,
749
- autoContinue,
750
- peerBridge
751
- }) {
752
- if (!topicId || !agent)
753
- return null;
754
- return buildRuntimeMcpSpec(agent, {
755
- userId,
756
- topicId,
757
- topicTitle: session,
758
- queryId,
759
- cwd: cwd ?? resolveTopicWorkspaceDir(topicId),
760
- agent,
761
- model,
762
- currentUserPrompt,
763
- autoContinue,
764
- peerBridge
765
- });
766
- }
767
- },
768
- paddleocr: {
769
- scopes: ["dm", "forum", "fork", "cron"],
770
- build({ agent }) {
771
- return buildStdioMcpServer(agent, PADDLEOCR_SERVER, []);
772
- }
773
- },
774
- "token-stats": {
775
- scopes: ["dm", "forum", "manager", "cron"],
776
- forumRequired: true,
777
- build({ userId, agent }) {
778
- return buildStdioMcpServer(agent, TOKEN_STATS_SERVER, [`--user-id=${userId}`]);
779
- }
780
- },
781
- task: {
782
- scopes: ["dm", "forum", "manager", "cron"],
783
- forumRequired: true,
784
- build({ userId, session, topicId, queryId, agent, peerBridge }) {
785
- if (peerBridge) {
786
- if (!topicId || !queryId)
787
- return null;
788
- const env = canonicalMcpBridgeEnv({
789
- surface: "task",
790
- userId,
791
- topicId,
792
- queryId,
793
- peerBridge
794
- });
795
- return env ? buildStdioMcpServer(agent, CANONICAL_MCP_PROXY_SERVER, ["--surface=task"], env) : null;
796
- }
797
- const args = [`--user-id=${userId}`, `--topic=${session}`];
798
- if (topicId)
799
- args.push(`--topic-id=${topicId}`);
800
- return buildStdioMcpServer(agent, TASK_SERVER, args);
801
- }
802
- },
803
- "session-comm": {
804
- scopes: ["forum", "fork", "manager"],
805
- forumRequired: true,
806
- build({ userId, session, topicId, agent, depth = 0, silent, peerBridge }) {
807
- const effectiveAgent = agent ?? FALLBACK_AGENT;
808
- const args = [
809
- `--user-id=${userId}`,
810
- `--topic=${session}`,
811
- ...topicId ? [`--topic-id=${topicId}`] : [],
812
- `--depth=${depth}`,
813
- `--agent=${effectiveAgent}`,
814
- ...silent ? ["--reply-only=true"] : [],
815
- ...peerBridge ? [`--peer-host-query-id=${peerBridge.hostQueryId}`] : []
816
- ];
817
- return buildStdioMcpServer(effectiveAgent, SESSION_COMM_SERVER, args, peerBridge ? peerSessionBridgeIpcEnv() : undefined);
818
- }
819
- },
820
- wiki: {
821
- scopes: ["dm", "forum", "manager", "cron"],
822
- forumRequired: true,
823
- build({ userId, session, topicId, queryId, wikiTopicId, agent, peerBridge }) {
824
- if (peerBridge) {
825
- if (!topicId || !queryId)
826
- return null;
827
- const env = canonicalMcpBridgeEnv({
828
- surface: "wiki",
829
- userId,
830
- topicId,
831
- queryId,
832
- peerBridge
833
- });
834
- return env ? buildStdioMcpServer(agent, CANONICAL_MCP_PROXY_SERVER, ["--surface=wiki"], env) : null;
835
- }
836
- const args = [`--user-id=${userId}`];
837
- const resolvedWikiTopicId = wikiTopicId ?? topicId ?? (session !== "dm" ? session : undefined);
838
- if (resolvedWikiTopicId)
839
- args.push(`--topic-id=${resolvedWikiTopicId}`);
840
- args.push("--surface=wiki");
841
- return buildStdioMcpServer(agent, WIKI_SERVER, args);
842
- }
843
- },
844
- skills: {
845
- scopes: ["dm", "forum", "manager", "cron"],
846
- forumRequired: true,
847
- build({ userId, topicId, agent }) {
848
- const args = [`--user-id=${userId}`, "--surface=skills"];
849
- if (topicId)
850
- args.push(`--topic-id=${topicId}`);
851
- return buildStdioMcpServer(agent, WIKI_SERVER, args);
852
- }
853
- },
854
- "system-health": {
855
- scopes: ["dm", "forum", "manager", "cron"],
856
- forumRequired: true,
857
- build({ agent }) {
858
- return buildStdioMcpServer(agent, SYSTEM_HEALTH_SERVER, []);
859
- }
860
- },
861
- "background-bash": {
862
- scopes: ["forum"],
863
- build({ agent, bgBashPort }) {
864
- if (bgBashPort === undefined)
865
- return null;
866
- return longLivedHttpMcp(agent, bgBashPort);
867
- }
868
- },
869
- "agent-health": {
870
- scopes: ["forum", "manager", "cron"],
871
- forumRequired: true,
872
- build({ userId, agent }) {
873
- const args = [`--user-id=${userId}`];
874
- return buildStdioMcpServer(agent, AGENT_HEALTH_SERVER, args);
875
- }
876
- },
877
- vault: {
878
- scopes: ["dm", "forum", "manager", "cron"],
879
- forumRequired: true,
880
- build({ userId, agent }) {
881
- const args = [`--user-id=${userId}`];
882
- if (agent === "codex")
883
- args.push("--http-only=true");
884
- return buildStdioMcpServer(agent, VAULT_SERVER, args);
885
- }
886
- }
887
- };
888
- function namesInScope(scope) {
889
- return Object.entries(MCP_CATALOG).filter(([, e]) => e.scopes.includes(scope)).map(([name]) => name);
890
- }
891
- var allForumMcpServerNames = [];
892
- var requiredForumMcpServers = [];
893
- var REQUIRED_FORUM_MCP_SERVERS = requiredForumMcpServers;
894
- var optionalForumMcpServers = [];
895
- var OPTIONAL_FORUM_MCP_SERVERS = optionalForumMcpServers;
896
- function refreshForumCatalogViews() {
897
- const all = namesInScope("forum");
898
- const required = Object.entries(MCP_CATALOG).filter(([, entry]) => entry.scopes.includes("forum") && entry.forumRequired).map(([name]) => name);
899
- allForumMcpServerNames.splice(0, allForumMcpServerNames.length, ...all);
900
- requiredForumMcpServers.splice(0, requiredForumMcpServers.length, ...required);
901
- optionalForumMcpServers.splice(0, optionalForumMcpServers.length, ...all.filter((name) => !required.includes(name)));
902
- }
903
- refreshForumCatalogViews();
904
- function registerRuntimeMcpServer(name, entry) {
905
- const key = name.trim();
906
- if (!key || !/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(key)) {
907
- throw new Error(`invalid runtime MCP server name: ${name}`);
908
- }
909
- if (MCP_CATALOG[key])
910
- throw new Error(`runtime MCP server already registered: ${key}`);
911
- MCP_CATALOG[key] = entry;
912
- refreshForumCatalogViews();
913
- return () => {
914
- if (MCP_CATALOG[key] !== entry)
915
- return;
916
- delete MCP_CATALOG[key];
917
- refreshForumCatalogViews();
918
- };
919
- }
920
- var nodeMcpEntries = [];
921
- function setNodeMcpServers(entries) {
922
- nodeMcpEntries = entries.filter((entry) => !(entry.key in MCP_CATALOG));
923
- const dropped = entries.length - nodeMcpEntries.length;
924
- if (dropped > 0) {
925
- logger.warn({ dropped }, "setNodeMcpServers: entries shadowing built-in catalog keys ignored");
926
- }
927
- }
928
- function getNodeMcpServers() {
929
- return nodeMcpEntries;
930
- }
931
- function buildNodeMcpSpecs(agent, filter) {
932
- const out = {};
933
- for (const entry of nodeMcpEntries) {
934
- if (!filter(entry.key))
935
- continue;
936
- out[entry.key] = entry.kind === "http" ? longLivedHttpMcp(agent, entry.port) : {
937
- command: entry.command,
938
- args: entry.args ?? [],
939
- ...entry.env ? { env: entry.env } : {}
940
- };
941
- }
942
- return out;
943
- }
944
- function buildScope(scope, ctx, filter = () => true) {
945
- const out = {};
946
- for (const [name, entry] of Object.entries(MCP_CATALOG)) {
947
- if (!entry.scopes.includes(scope))
948
- continue;
949
- if (!filter(name))
950
- continue;
951
- const spec = entry.build(ctx);
952
- if (spec === null)
953
- continue;
954
- out[name] = spec;
955
- }
956
- if (scope !== "cron") {
957
- Object.assign(out, buildNodeMcpSpecs(ctx.agent, filter));
958
- }
959
- return out;
960
- }
961
- function getDmMcpServers(opts) {
962
- return buildScope("dm", {
963
- userId: opts.userId,
964
- session: "dm",
965
- agent: opts.agent,
966
- playwrightPort: opts.playwrightPort,
967
- playwrightCapability: opts.playwrightCapability
968
- });
969
- }
970
- function getManagerMcpServers(opts) {
971
- if (!opts.topicId) {
972
- throw new Error("getManagerMcpServers: private General topicId is required");
973
- }
974
- const topicId = opts.topicId;
975
- return buildScope("manager", {
976
- userId: opts.userId,
977
- session: opts.session ?? "General",
978
- topicId,
979
- queryId: opts.queryId,
980
- wikiTopicId: opts.wikiTopicId ?? topicId,
981
- agent: opts.agent,
982
- cwd: opts.cwd,
983
- model: opts.model,
984
- currentUserPrompt: opts.currentUserPrompt,
985
- playwrightPort: opts.playwrightPort,
986
- playwrightCapability: opts.playwrightCapability,
987
- autoContinue: opts.autoContinue
988
- });
989
- }
990
- function getForumMcpServers(opts) {
991
- const {
992
- userId,
993
- session,
994
- topicId,
995
- queryId,
996
- wikiTopicId,
997
- agent,
998
- cwd,
999
- model,
1000
- currentUserPrompt,
1001
- playwrightPort,
1002
- playwrightCapability,
1003
- depth = 0,
1004
- enabled = null,
1005
- extra = {},
1006
- silent = false,
1007
- bgBashPort,
1008
- autoContinue,
1009
- peerBridge
1010
- } = opts;
1011
- const filter = (name) => {
1012
- if (silent && name === "task")
1013
- return false;
1014
- if (enabled === null)
1015
- return true;
1016
- return enabled.includes(name) || REQUIRED_FORUM_MCP_SERVERS.includes(name);
1017
- };
1018
- const base = buildScope("forum", {
1019
- userId,
1020
- session,
1021
- topicId,
1022
- queryId,
1023
- wikiTopicId,
1024
- agent,
1025
- cwd,
1026
- model,
1027
- currentUserPrompt,
1028
- depth,
1029
- playwrightPort,
1030
- playwrightCapability,
1031
- bgBashPort,
1032
- autoContinue,
1033
- silent,
1034
- peerBridge
1035
- }, filter);
1036
- return { ...base, ...extra };
1037
- }
1038
- function getCronMcpServers(opts) {
1039
- return buildScope("cron", {
1040
- userId: opts.userId,
1041
- session: opts.session,
1042
- topicId: opts.topicId,
1043
- queryId: opts.queryId,
1044
- wikiTopicId: opts.wikiTopicId ?? opts.topicId,
1045
- agent: opts.agent,
1046
- cwd: opts.cwd,
1047
- model: opts.model,
1048
- currentUserPrompt: opts.currentUserPrompt,
1049
- playwrightPort: opts.playwrightPort,
1050
- playwrightCapability: opts.playwrightCapability,
1051
- autoContinue: false
1052
- });
1053
- }
1054
- function getMcpServersForQuery(opts) {
1055
- if (opts.sessionType === "cron") {
1056
- if (!opts.topicId)
1057
- throw new Error("getMcpServersForQuery: cron sessionType requires topicId");
1058
- return getCronMcpServers({
1059
- userId: opts.userId || "default",
1060
- session: opts.session || "cron",
1061
- topicId: opts.topicId,
1062
- queryId: opts.queryId,
1063
- wikiTopicId: opts.wikiTopicId,
1064
- agent: opts.agent,
1065
- cwd: opts.cwd,
1066
- model: opts.model,
1067
- currentUserPrompt: opts.prompt,
1068
- playwrightPort: opts.playwrightPort,
1069
- playwrightCapability: opts.playwrightCapability
1070
- });
1071
- }
1072
- if (opts.sessionType === "dm" || opts.sessionType === "ephemeral") {
1073
- return getDmMcpServers({
1074
- userId: opts.userId || "default",
1075
- agent: opts.agent,
1076
- playwrightPort: opts.playwrightPort,
1077
- playwrightCapability: opts.playwrightCapability
1078
- });
1079
- }
1080
- if (opts.sessionType === "manager") {
1081
- return getManagerMcpServers({
1082
- userId: opts.userId || "default",
1083
- session: opts.session,
1084
- topicId: opts.topicId,
1085
- queryId: opts.queryId,
1086
- wikiTopicId: opts.wikiTopicId,
1087
- agent: opts.agent,
1088
- cwd: opts.cwd,
1089
- model: opts.model,
1090
- currentUserPrompt: opts.prompt,
1091
- playwrightPort: opts.playwrightPort,
1092
- playwrightCapability: opts.playwrightCapability,
1093
- autoContinue: opts.autoContinue
1094
- });
1095
- }
1096
- return getForumMcpServers({
1097
- userId: opts.userId || "default",
1098
- session: opts.session || "default",
1099
- topicId: opts.topicId,
1100
- queryId: opts.queryId,
1101
- wikiTopicId: opts.wikiTopicId,
1102
- agent: opts.agent,
1103
- cwd: opts.cwd,
1104
- model: opts.model,
1105
- currentUserPrompt: opts.prompt,
1106
- playwrightPort: opts.playwrightPort,
1107
- playwrightCapability: opts.playwrightCapability,
1108
- bgBashPort: opts.bgBashPort,
1109
- autoContinue: opts.autoContinue,
1110
- depth: opts.depth,
1111
- enabled: opts.mcpEnabled,
1112
- extra: opts.mcpExtra,
1113
- silent: opts.silent,
1114
- peerBridge: opts.peerBridge
1115
- });
1116
- }
1117
-
1118
- // ../../packages/core/src/media/file-events.ts
1119
- function* extractFileEvents(text, source) {
1120
- const tagRegex = new RegExp(FILE_TAG_REGEX.source, "gi");
1121
- let match = tagRegex.exec(text);
1122
- while (match !== null) {
1123
- yield { type: "file", path: match[1], source, origin: "tag" };
1124
- match = tagRegex.exec(text);
1125
- }
1126
- }
1127
- function extractFileTagPaths(text) {
1128
- const seen = new Set;
1129
- for (const event of extractFileEvents(text, "tag")) {
1130
- if (event.type === "file")
1131
- seen.add(event.path);
1132
- }
1133
- return [...seen];
1134
- }
1135
- function stripFileTags(text) {
1136
- return text.replace(new RegExp(FILE_TAG_REGEX.source, "gi"), "").trim();
1137
- }
1138
-
1139
- // ../../packages/core/src/platform/error.ts
1140
- function errMsg(e, fallback) {
1141
- if (e instanceof Error)
1142
- return e.message;
1143
- return fallback ?? String(e);
1144
- }
1145
-
1146
- // ../../packages/core/src/version.ts
1147
- var NEGOTIUM_VERSION = "0.1.8";
1148
-
1149
- // ../../packages/core/src/agents/index.ts
1150
- import { existsSync as existsSync5 } from "fs";
1151
- import { homedir } from "os";
1152
- import { join as join4 } from "path";
1153
-
1154
- // ../../packages/core/src/agents/claude-provider.ts
1155
- import { spawn } from "child_process";
1156
- import { existsSync, readFileSync } from "fs";
1157
- import { query } from "@anthropic-ai/claude-agent-sdk";
1158
-
1159
- // ../../packages/core/src/agents/deep-map.ts
1160
- function deepMapStrings(value, fn) {
1161
- if (typeof value === "string")
1162
- return fn(value);
1163
- if (Array.isArray(value))
1164
- return value.map((v) => deepMapStrings(v, fn));
1165
- if (value && typeof value === "object") {
1166
- const out = {};
1167
- for (const [k, v] of Object.entries(value)) {
1168
- out[k] = deepMapStrings(v, fn);
1169
- }
1170
- return out;
1171
- }
1172
- return value;
1173
- }
1174
-
1175
- // ../../packages/core/src/agents/vault-tool-policy.ts
1176
- var SENSITIVE_RUNTIME_NAMES = [
1177
- "vault.db",
1178
- "vault-master-key",
1179
- "runtime-mcp-secret",
1180
- "sessions.db"
1181
- ];
1182
- var VAULT_BROKER_REDIRECT_ERROR = "Vault placeholders must be executed through mcp__vault__vault_run (shell/CLI) or mcp__vault__vault_http_request (HTTP) so secret values cannot enter the model transcript.";
1183
- function isVaultBrokerTool(toolName) {
1184
- return toolName.includes("vault_run") || toolName.includes("vault_http_request");
1185
- }
1186
- function referencesRuntimeSecretStorage(value) {
1187
- if (typeof value === "string") {
1188
- const lower = value.toLowerCase();
1189
- if (SENSITIVE_RUNTIME_NAMES.some((name) => lower.includes(name)))
1190
- return true;
1191
- return value.startsWith("/") && isSensitivePath(value);
1192
- }
1193
- if (Array.isArray(value))
1194
- return value.some(referencesRuntimeSecretStorage);
1195
- if (value && typeof value === "object") {
1196
- return Object.values(value).some(referencesRuntimeSecretStorage);
1197
- }
1198
- return false;
1199
- }
1200
- function shouldRedirectVaultTool(userId, toolName, input) {
1201
- return !isVaultBrokerTool(toolName) && valueReferencesVaultKey(userId, input);
1202
- }
1203
-
1204
- // ../../packages/core/src/agents/execution-host.ts
1205
- var defaultHost = {
1206
- getMcpServersForQuery,
1207
- redactVaultSecrets,
1208
- referencesRuntimeSecretStorage,
1209
- shouldRedirectVaultTool,
1210
- claudeCodeExecutablePath: () => CLAUDE_EXECUTABLE,
1211
- codexAuthFilePath
1212
- };
1213
- var hostRegistrations = [];
1214
- function activeHost() {
1215
- const host = { ...defaultHost };
1216
- for (const registration of hostRegistrations)
1217
- Object.assign(host, registration.overrides);
1218
- return host;
1219
- }
1220
- function hostedMcpServers(opts) {
1221
- return activeHost().getMcpServersForQuery(opts);
1222
- }
1223
- function redactHostedSecrets(userId, value) {
1224
- return activeHost().redactVaultSecrets(userId, value);
1225
- }
1226
- function referencesHostedSecretStorage(value) {
1227
- return activeHost().referencesRuntimeSecretStorage(value);
1228
- }
1229
- function shouldRedirectHostedVaultTool(userId, toolName, input) {
1230
- return activeHost().shouldRedirectVaultTool(userId, toolName, input);
1231
- }
1232
- function hostedClaudeCodeExecutablePath() {
1233
- return activeHost().claudeCodeExecutablePath();
1234
- }
1235
- function hostedCodexAuthFilePath() {
1236
- return activeHost().codexAuthFilePath();
1237
- }
1238
-
1239
- // ../../packages/core/src/agents/claude-provider.ts
1240
- var CLAUDE_DEFAULT_DISALLOWED_TOOLS = [
1241
- "AskUserQuestion",
1242
- "Workflow",
1243
- "TodoWrite",
1244
- "TaskCreate",
1245
- "TaskUpdate",
1246
- "TaskList",
1247
- "TaskGet",
1248
- "Task",
1249
- "Agent",
1250
- "TaskOutput",
1251
- "TaskStop"
1252
- ];
1253
- function buildClaudeDisallowedTools(extra = undefined) {
1254
- return [...new Set([...CLAUDE_DEFAULT_DISALLOWED_TOOLS, ...extra ?? []])];
1255
- }
1256
- function toClaudeEffort(e) {
1257
- if (!e || !claudeRegistry.validateEffort(e))
1258
- return;
1259
- return e;
1260
- }
1261
- var CLAUDE_IMAGE_MAX_BYTES = 10 * 1024 * 1024;
1262
- var CLAUDE_IMAGE_MIME_TYPES = new Set([
1263
- "image/jpeg",
1264
- "image/png",
1265
- "image/gif",
1266
- "image/webp"
1267
- ]);
1268
- function isClaudeImageAttachment(attachment) {
1269
- return attachment.type === "image" && CLAUDE_IMAGE_MIME_TYPES.has(attachment.mimeType);
1270
- }
1271
- async function* singleUserMessage(message) {
1272
- yield message;
1273
- }
1274
- function buildClaudePrompt(opts) {
1275
- const attachments = opts.attachments ?? [];
1276
- if (attachments.length === 0)
1277
- return opts.prompt;
1278
- const imageBlocks = [];
1279
- for (const attachment of attachments) {
1280
- if (!isClaudeImageAttachment(attachment))
1281
- continue;
1282
- if (attachment.sizeBytes > CLAUDE_IMAGE_MAX_BYTES) {
1283
- logger.info({
1284
- path: attachment.path,
1285
- mimeType: attachment.mimeType,
1286
- sizeBytes: attachment.sizeBytes,
1287
- maxBytes: CLAUDE_IMAGE_MAX_BYTES
1288
- }, "claudeProvider: skipping oversized image attachment content block");
1289
- continue;
1290
- }
1291
- try {
1292
- imageBlocks.push({
1293
- type: "image",
1294
- source: {
1295
- type: "base64",
1296
- media_type: attachment.mimeType,
1297
- data: readFileSync(attachment.path).toString("base64")
1298
- }
1299
- });
1300
- } catch (err) {
1301
- logger.warn({
1302
- path: attachment.path,
1303
- mimeType: attachment.mimeType,
1304
- err
1305
- }, "claudeProvider: failed to read image attachment for content block");
1306
- }
1307
- }
1308
- if (imageBlocks.length === 0)
1309
- return opts.prompt;
1310
- const content = [{ type: "text", text: opts.prompt }, ...imageBlocks];
1311
- return singleUserMessage({
1312
- type: "user",
1313
- message: {
1314
- role: "user",
1315
- content
1316
- },
1317
- parent_tool_use_id: null
1318
- });
1319
- }
1320
- var CLAUDE_ABORT_SIGKILL_DELAY_MS = 2500;
1321
- function signalProcessTree(pid, signal) {
1322
- try {
1323
- process.kill(-pid, signal);
1324
- return true;
1325
- } catch (groupErr) {
1326
- try {
1327
- process.kill(pid, signal);
1328
- return true;
1329
- } catch (pidErr) {
1330
- logger.debug({ groupErr, pidErr, pid, signal }, "Failed to signal Claude process tree");
1331
- return false;
1332
- }
1333
- }
1334
- }
1335
- function spawnClaudeCodeProcessWithTreeKill(options) {
1336
- const child = spawn(options.command, options.args, {
1337
- cwd: options.cwd,
1338
- detached: true,
1339
- env: options.env,
1340
- stdio: ["pipe", "pipe", "inherit"]
1341
- });
1342
- if (!child.stdin || !child.stdout) {
1343
- child.kill("SIGKILL");
1344
- throw new Error("Failed to spawn Claude Code process with piped stdin/stdout");
1345
- }
1346
- let exited = false;
1347
- let killTimer;
1348
- const clearKillTimer = () => {
1349
- if (killTimer)
1350
- clearTimeout(killTimer);
1351
- killTimer = undefined;
1352
- };
1353
- const kill = (signal) => {
1354
- if (!child.pid)
1355
- return child.kill(signal);
1356
- const ok = signalProcessTree(child.pid, signal);
1357
- if (signal !== "SIGKILL") {
1358
- clearKillTimer();
1359
- killTimer = setTimeout(() => {
1360
- if (!exited && child.pid) {
1361
- logger.warn({ pid: child.pid }, "Claude process tree still alive after SIGTERM; SIGKILL");
1362
- signalProcessTree(child.pid, "SIGKILL");
1363
- }
1364
- }, CLAUDE_ABORT_SIGKILL_DELAY_MS);
1365
- killTimer.unref?.();
1366
- }
1367
- return ok;
1368
- };
1369
- const onAbort = () => {
1370
- logger.warn({ pid: child.pid, command: options.command }, "Aborting Claude Code process tree");
1371
- kill("SIGTERM");
1372
- };
1373
- if (options.signal.aborted)
1374
- onAbort();
1375
- else
1376
- options.signal.addEventListener("abort", onAbort, { once: true });
1377
- child.once("exit", (code, signal) => {
1378
- exited = true;
1379
- clearKillTimer();
1380
- options.signal.removeEventListener("abort", onAbort);
1381
- logger.debug({ pid: child.pid, code, signal }, "Claude Code process exited");
1382
- });
1383
- child.once("error", (err) => {
1384
- exited = true;
1385
- clearKillTimer();
1386
- options.signal.removeEventListener("abort", onAbort);
1387
- logger.error({
1388
- pid: child.pid,
1389
- command: options.command,
1390
- err: err instanceof Error ? err.message : String(err)
1391
- }, "Claude Code process error event");
1392
- });
1393
- return {
1394
- stdin: child.stdin,
1395
- stdout: child.stdout,
1396
- get killed() {
1397
- return child.killed || exited;
1398
- },
1399
- get exitCode() {
1400
- return child.exitCode;
1401
- },
1402
- kill,
1403
- on(event, listener) {
1404
- child.on(event, listener);
1405
- },
1406
- once(event, listener) {
1407
- child.once(event, listener);
1408
- },
1409
- off(event, listener) {
1410
- child.off(event, listener);
1411
- }
1412
- };
1413
- }
1414
- function isAbortError(err) {
1415
- if (!err || typeof err !== "object")
1416
- return false;
1417
- const e = err;
1418
- if (e.name === "AbortError")
1419
- return true;
1420
- if (e.code === 20 || e.code === "ABORT_ERR")
1421
- return true;
1422
- return false;
1423
- }
1424
- async function* claudeProvider(opts) {
1425
- const claudeExecutable = hostedClaudeCodeExecutablePath();
1426
- if (claudeExecutable && !existsSync(claudeExecutable)) {
1427
- yield {
1428
- type: "error",
1429
- content: `Claude CLI not found at ${claudeExecutable}. Install Claude Code or configure the executable path.`
1430
- };
1431
- return;
1432
- }
1433
- const cleanEnv = { ...process.env };
1434
- delete cleanEnv.NEGOTIUM_PEER_SESSION_BRIDGE_URL;
1435
- delete cleanEnv.NEGOTIUM_PEER_SESSION_BRIDGE_TOKEN;
1436
- delete cleanEnv.CLAUDECODE;
1437
- cleanEnv.CLAUDE_CODE_STREAM_CLOSE_TIMEOUT ??= process.env.PADDLEOCR_TIMEOUT_MS ?? "300000";
1438
- cleanEnv.CLAUDE_CODE_DISABLE_WORKFLOWS = "1";
1439
- const queryOptions = {
1440
- ...claudeExecutable ? { pathToClaudeCodeExecutable: claudeExecutable } : {},
1441
- spawnClaudeCodeProcess: spawnClaudeCodeProcessWithTreeKill,
1442
- cwd: opts.cwd,
1443
- permissionMode: "bypassPermissions",
1444
- allowDangerouslySkipPermissions: true,
1445
- includePartialMessages: true,
1446
- env: cleanEnv,
1447
- mcpServers: hostedMcpServers(opts),
1448
- abortController: opts.abortController,
1449
- disallowedTools: buildClaudeDisallowedTools(opts.disallowedTools),
1450
- ...opts.model ? { model: opts.model } : {},
1451
- ...opts.maxBudgetUsd ? { maxBudgetUsd: opts.maxBudgetUsd } : {},
1452
- ...opts.agents ? {
1453
- agents: Object.fromEntries(Object.entries(opts.agents).map(([name, def]) => {
1454
- const { effort, ...rest } = def;
1455
- const narrowed = typeof effort === "string" ? toClaudeEffort(effort) : effort;
1456
- return [name, narrowed !== undefined ? { ...rest, effort: narrowed } : rest];
1457
- }))
1458
- } : {},
1459
- ...toClaudeEffort(opts.effort) ? { effort: toClaudeEffort(opts.effort) } : {},
1460
- hooks: {
1461
- PreToolUse: [
1462
- {
1463
- hooks: [
1464
- async (input) => {
1465
- const { tool_name, tool_input } = input;
1466
- if (referencesHostedSecretStorage(tool_input)) {
1467
- return {
1468
- hookSpecificOutput: {
1469
- hookEventName: "PreToolUse",
1470
- permissionDecision: "deny",
1471
- permissionDecisionReason: "Runtime secret storage access is not permitted"
1472
- }
1473
- };
1474
- }
1475
- if (tool_name === "Bash" && tool_input?.run_in_background === true) {
1476
- return {
1477
- hookSpecificOutput: {
1478
- hookEventName: "PreToolUse",
1479
- permissionDecision: "deny",
1480
- permissionDecisionReason: "run_in_background is not supported in headless mode \u2014 the bash process dies when the agent turn ends. Use mcp__background-bash__background_bash_run instead: it survives across turns and automatically injects the result into the session when done."
1481
- }
1482
- };
1483
- }
1484
- const userId = opts.userId ?? "";
1485
- if (!shouldRedirectHostedVaultTool(userId, tool_name, tool_input)) {
1486
- return { continue: true };
1487
- }
1488
- return {
1489
- hookSpecificOutput: {
1490
- hookEventName: "PreToolUse",
1491
- permissionDecision: "deny",
1492
- permissionDecisionReason: VAULT_BROKER_REDIRECT_ERROR
1493
- }
1494
- };
1495
- }
1496
- ]
1497
- }
1498
- ],
1499
- PostToolUse: [
1500
- {
1501
- hooks: [
1502
- async (input) => {
1503
- const { tool_response } = input;
1504
- const userId = opts.userId ?? "";
1505
- const redacted = deepMapStrings(tool_response, (value) => redactHostedSecrets(userId, value));
1506
- if (JSON.stringify(redacted) === JSON.stringify(tool_response)) {
1507
- return { continue: true };
1508
- }
1509
- return {
1510
- hookSpecificOutput: {
1511
- hookEventName: "PostToolUse",
1512
- updatedToolOutput: redacted
1513
- }
1514
- };
1515
- }
1516
- ]
1517
- }
1518
- ]
1519
- },
1520
- settingSources: ["project"],
1521
- settings: {
1522
- disableWorkflows: true,
1523
- workflowKeywordTriggerEnabled: false
1524
- },
1525
- systemPrompt: opts.systemPrompt
1526
- };
1527
- if (opts.sessionId) {
1528
- queryOptions.resume = opts.sessionId;
1529
- }
1530
- const THINKING_BEAT_MS = 5000;
1531
- let thinkingStart = null;
1532
- let lastThinkingBeat = 0;
1533
- let lastContextTokens;
1534
- let lastContextModel;
1535
- const thinkingBeat = () => {
1536
- const now = Date.now();
1537
- if (thinkingStart === null) {
1538
- thinkingStart = now;
1539
- lastThinkingBeat = now;
1540
- return { type: "tool_progress", toolName: "thinking", elapsed: 0 };
1541
- }
1542
- if (now - lastThinkingBeat < THINKING_BEAT_MS)
1543
- return null;
1544
- lastThinkingBeat = now;
1545
- return { type: "tool_progress", toolName: "thinking", elapsed: (now - thinkingStart) / 1000 };
1546
- };
1547
- try {
1548
- const prompt = buildClaudePrompt(opts);
1549
- for await (const message of query({
1550
- prompt,
1551
- options: queryOptions
1552
- })) {
1553
- if (message.type === "system" && message.subtype === "thinking_tokens") {
1554
- const beat = thinkingBeat();
1555
- if (beat)
1556
- yield beat;
1557
- continue;
1558
- }
1559
- if (message.type === "stream_event") {
1560
- const streamMsg = message;
1561
- const evt = streamMsg.event;
1562
- if (!evt)
1563
- continue;
1564
- if (evt.type === "content_block_delta") {
1565
- const delta = evt;
1566
- if (delta.delta.type === "thinking_delta") {
1567
- const beat = thinkingBeat();
1568
- if (beat)
1569
- yield beat;
1570
- } else if (delta.delta.type === "text_delta" && delta.delta.text) {
1571
- thinkingStart = null;
1572
- } else {
1573
- thinkingStart = null;
1574
- }
1575
- }
1576
- continue;
1577
- }
1578
- thinkingStart = null;
1579
- if (message.type === "tool_progress") {
1580
- const m = message;
1581
- yield {
1582
- type: "tool_progress",
1583
- toolName: m.tool_name,
1584
- elapsed: m.elapsed_time_seconds
1585
- };
1586
- continue;
1587
- }
1588
- if (message.type === "tool_use_summary") {
1589
- const m = message;
1590
- yield { type: "tool_use_summary", summary: m.summary };
1591
- continue;
1592
- }
1593
- if (message.type === "system") {
1594
- const m = message;
1595
- if (m.subtype === "init") {
1596
- yield { type: "session", sessionId: m.session_id };
1597
- } else if (m.subtype === "task_started") {
1598
- const t = m;
1599
- if (t.subagent_type && !t.skip_transcript) {
1600
- const desc = t.description ? ` ${t.description.slice(0, 60)}` : "";
1601
- yield { type: "status", content: `\u25B6 [${t.subagent_type}]${desc}` };
1602
- }
1603
- } else {
1604
- logger.debug({ subtype: m.subtype, msg: m }, "claudeProvider: system message (unhandled subtype)");
1605
- }
1606
- continue;
1607
- }
1608
- if (message.type === "result") {
1609
- const m = message;
1610
- if (m.subtype === "success") {
1611
- const contextWindow = (lastContextModel ? m.modelUsage?.[lastContextModel]?.contextWindow : undefined) ?? Object.values(m.modelUsage ?? {})[0]?.contextWindow;
1612
- yield {
1613
- type: "result",
1614
- content: m.result,
1615
- stopReason: m.stop_reason ?? "end_turn",
1616
- usage: m.usage ? {
1617
- inputTokens: m.usage.input_tokens,
1618
- outputTokens: m.usage.output_tokens,
1619
- cacheCreationInputTokens: m.usage.cache_creation_input_tokens ?? undefined,
1620
- cacheReadInputTokens: m.usage.cache_read_input_tokens ?? undefined,
1621
- costUsd: m.total_cost_usd,
1622
- ...lastContextTokens !== undefined && contextWindow ? { contextTokens: lastContextTokens, contextWindow } : {}
1623
- } : undefined
1624
- };
1625
- yield* extractFileEvents(m.result, "result");
1626
- return;
1627
- }
1628
- const budgetExceeded = m.subtype === "error_max_budget_usd";
1629
- const errorMsg = budgetExceeded ? "The job reached its cost limit" : m.errors?.join("; ") || "Unknown error";
1630
- yield {
1631
- type: "error",
1632
- content: errorMsg,
1633
- ...budgetExceeded ? { code: "budget_exceeded" } : {},
1634
- ...m.usage ? {
1635
- usage: {
1636
- inputTokens: m.usage.input_tokens,
1637
- outputTokens: m.usage.output_tokens,
1638
- cacheCreationInputTokens: m.usage.cache_creation_input_tokens ?? undefined,
1639
- cacheReadInputTokens: m.usage.cache_read_input_tokens ?? undefined,
1640
- costUsd: m.total_cost_usd
1641
- }
1642
- } : {}
1643
- };
1644
- return;
1645
- }
1646
- if (message.type === "assistant") {
1647
- const m = message;
1648
- const content = m.message?.content ?? [];
1649
- const usage = m.message?.usage;
1650
- if (usage) {
1651
- lastContextTokens = (usage.input_tokens ?? 0) + (usage.cache_creation_input_tokens ?? 0) + (usage.cache_read_input_tokens ?? 0) + (usage.output_tokens ?? 0);
1652
- lastContextModel = m.message.model;
1653
- }
1654
- for (const block of content) {
1655
- if (block.type === "text") {
1656
- const textBlock = block;
1657
- yield { type: "text", content: textBlock.text };
1658
- yield* extractFileEvents(textBlock.text, "text");
1659
- } else if (block.type === "tool_use") {
1660
- const tb = block;
1661
- yield {
1662
- type: "tool_use",
1663
- name: tb.name,
1664
- input: tb.input || {},
1665
- toolUseId: tb.id
1666
- };
1667
- }
1668
- }
1669
- continue;
1670
- }
1671
- if (message.type === "user") {
1672
- const um = message;
1673
- if ("isReplay" in um && um.isReplay)
1674
- continue;
1675
- const content = um.message?.content;
1676
- if (!Array.isArray(content))
1677
- continue;
1678
- for (const block of content) {
1679
- if (block.type !== "tool_result")
1680
- continue;
1681
- const trBlock = block;
1682
- const trContent = typeof trBlock.content === "string" ? trBlock.content.slice(0, 200) : "";
1683
- yield {
1684
- type: "tool_result",
1685
- toolUseId: trBlock.tool_use_id || "",
1686
- content: trContent
1687
- };
1688
- }
1689
- }
1690
- }
1691
- } catch (e) {
1692
- if (isAbortError(e) || opts.abortController?.signal.aborted)
1693
- return;
1694
- logger.error({ err: e }, "claudeProvider: SDK iteration failed");
1695
- yield { type: "error", content: errMsg(e) };
1696
- }
1697
- }
1698
-
1699
- // ../../packages/core/src/agents/codex-provider.ts
1700
- import { existsSync as existsSync3 } from "fs";
1701
- import { Codex } from "@openai/codex-sdk";
1702
-
1703
- // ../../packages/core/src/agents/codex-native-multi-agent.ts
1704
- import { spawn as spawn2 } from "child_process";
18
+ getRegistry
19
+ } from "./chunk-nqdp52aj.js";
1705
20
  import {
1706
- chmodSync as chmodSync2,
1707
- existsSync as existsSync2,
1708
- readFileSync as readFileSync2,
1709
- renameSync,
1710
- unlinkSync,
1711
- writeFileSync
1712
- } from "fs";
1713
- import { createRequire } from "module";
1714
- import { dirname, join as join2 } from "path";
1715
- var NEGOTIUM_MODEL_CATALOG = "negotium-model-catalog.json";
1716
- var moduleRequire = createRequire(import.meta.url);
1717
- function codexCliScriptPath() {
1718
- const sdkPackagePath = moduleRequire.resolve("@openai/codex-sdk/package.json");
1719
- const sdkRequire = createRequire(sdkPackagePath);
1720
- return join2(dirname(sdkRequire.resolve("@openai/codex/package.json")), "bin", "codex.js");
1721
- }
1722
- async function bootstrapCodexModelCache(codexHome, cachePath) {
1723
- const child = spawn2(process.execPath, [codexCliScriptPath(), "app-server", "--stdio"], {
1724
- env: { ...process.env, CODEX_HOME: codexHome },
1725
- stdio: ["pipe", "pipe", "pipe"]
1726
- });
1727
- await new Promise((resolve2, reject) => {
1728
- let settled = false;
1729
- let stdoutBuffer = "";
1730
- let stderr = "";
1731
- const timer = setTimeout(() => finish(new Error("timed out while refreshing the Codex model catalog")), 15000);
1732
- const finish = (error) => {
1733
- if (settled)
1734
- return;
1735
- settled = true;
1736
- clearTimeout(timer);
1737
- try {
1738
- child.stdin.end();
1739
- child.kill();
1740
- } catch {}
1741
- if (error)
1742
- reject(error);
1743
- else if (!existsSync2(cachePath))
1744
- reject(new Error("Codex did not create its model cache"));
1745
- else
1746
- resolve2();
1747
- };
1748
- const send = (message) => {
1749
- child.stdin.write(`${JSON.stringify(message)}
1750
- `);
1751
- };
1752
- child.stderr.on("data", (chunk) => {
1753
- if (stderr.length < 4096)
1754
- stderr += String(chunk);
1755
- });
1756
- child.on("error", (error) => finish(error));
1757
- child.on("exit", (code, signal) => {
1758
- if (!settled) {
1759
- finish(new Error(`Codex model catalog refresh exited with ${signal ? `signal ${signal}` : `code ${code ?? 1}`}${stderr.trim() ? `: ${stderr.trim()}` : ""}`));
1760
- }
1761
- });
1762
- child.stdout.on("data", (chunk) => {
1763
- stdoutBuffer += String(chunk);
1764
- for (;; ) {
1765
- const newline = stdoutBuffer.indexOf(`
1766
- `);
1767
- if (newline < 0)
1768
- break;
1769
- const line = stdoutBuffer.slice(0, newline);
1770
- stdoutBuffer = stdoutBuffer.slice(newline + 1);
1771
- let message;
1772
- try {
1773
- message = JSON.parse(line);
1774
- } catch {
1775
- continue;
1776
- }
1777
- if (message.id === 1) {
1778
- if (message.error) {
1779
- finish(new Error(message.error.message || "Codex initialization failed"));
1780
- return;
1781
- }
1782
- send({ method: "initialized" });
1783
- send({ id: 2, method: "model/list", params: { includeHidden: true } });
1784
- } else if (message.id === 2) {
1785
- if (message.error) {
1786
- finish(new Error(message.error.message || "Codex model listing failed"));
1787
- } else {
1788
- finish();
1789
- }
1790
- return;
1791
- }
1792
- }
1793
- });
1794
- send({
1795
- id: 1,
1796
- method: "initialize",
1797
- params: {
1798
- clientInfo: { name: "negotium", version: NEGOTIUM_VERSION },
1799
- capabilities: { experimentalApi: true }
1800
- }
1801
- });
1802
- });
1803
- }
1804
- async function ensureCodexModelCache(authFilePath, bootstrap = bootstrapCodexModelCache) {
1805
- const codexHome = dirname(authFilePath);
1806
- const cachePath = process.env.NEGOTIUM_CODEX_MODELS_CACHE_FILE ?? join2(codexHome, "models_cache.json");
1807
- const hardenedCatalogPath = join2(codexHome, NEGOTIUM_MODEL_CATALOG);
1808
- if (existsSync2(cachePath) || existsSync2(hardenedCatalogPath))
1809
- return;
1810
- if (process.env.NEGOTIUM_CODEX_MODELS_CACHE_FILE) {
1811
- throw new Error(`Configured Codex model cache does not exist: ${cachePath}`);
1812
- }
1813
- await bootstrap(codexHome, cachePath);
1814
- }
1815
- function writeCodexCatalogWithNativeMultiAgentDisabled(authFilePath) {
1816
- const codexHome = dirname(authFilePath);
1817
- const sourcePath = process.env.NEGOTIUM_CODEX_MODELS_CACHE_FILE ?? (existsSync2(join2(codexHome, "models_cache.json")) ? join2(codexHome, "models_cache.json") : join2(codexHome, NEGOTIUM_MODEL_CATALOG));
1818
- const outputPath = join2(codexHome, NEGOTIUM_MODEL_CATALOG);
1819
- const parsed = JSON.parse(readFileSync2(sourcePath, "utf8"));
1820
- if (!Array.isArray(parsed.models) || parsed.models.length === 0) {
1821
- throw new Error(`Codex model cache has no models: ${sourcePath}`);
1822
- }
1823
- const models = parsed.models.map((model, index) => {
1824
- if (!model || typeof model !== "object" || Array.isArray(model)) {
1825
- throw new Error(`Codex model cache entry ${index} is invalid: ${sourcePath}`);
1826
- }
1827
- return { ...model, multi_agent_version: "disabled" };
1828
- });
1829
- const contents = `${JSON.stringify({ models }, null, 2)}
1830
- `;
1831
- if (existsSync2(outputPath) && readFileSync2(outputPath, "utf8") === contents) {
1832
- return outputPath;
1833
- }
1834
- const tempPath = `${outputPath}.${process.pid}.${Date.now()}.tmp`;
1835
- try {
1836
- writeFileSync(tempPath, contents, { encoding: "utf8", mode: 384 });
1837
- renameSync(tempPath, outputPath);
1838
- chmodSync2(outputPath, 384);
1839
- } finally {
1840
- try {
1841
- unlinkSync(tempPath);
1842
- } catch {}
1843
- }
1844
- return outputPath;
1845
- }
1846
-
1847
- // ../../packages/core/src/agents/codex-provider.ts
1848
- function mapEffort(effort) {
1849
- if (!effort)
1850
- return;
1851
- if (effort === "max" || effort === "minimal") {
1852
- throw new Error(`codexProvider received effort='${effort}', which codex does not support \u2014 storage validation was bypassed`);
1853
- }
1854
- return effort;
1855
- }
1856
- var CODEX_MCP_SERVER_NAME_OVERRIDES = {
1857
- playwright: "otium_playwright"
1858
- };
1859
- function codexMcpServerName(name) {
1860
- return CODEX_MCP_SERVER_NAME_OVERRIDES[name] ?? name;
1861
- }
1862
- function parsePositiveInt(value, fallback) {
1863
- if (!value)
1864
- return fallback;
1865
- const parsed = Number.parseInt(value, 10);
1866
- return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
1867
- }
1868
- function withCodexMcpServerOverrides(name, server) {
1869
- if (name === "paddleocr") {
1870
- return {
1871
- ...server,
1872
- startup_timeout_sec: parsePositiveInt(process.env.PADDLEOCR_MCP_STARTUP_TIMEOUT_SEC, 30),
1873
- tool_timeout_sec: parsePositiveInt(process.env.PADDLEOCR_MCP_TOOL_TIMEOUT_SEC, 300)
1874
- };
1875
- }
1876
- return {
1877
- startup_timeout_sec: parsePositiveInt(process.env.CODEX_MCP_STARTUP_TIMEOUT_SEC, 30),
1878
- ...server
1879
- };
1880
- }
1881
- function toCodexMcpServers(claudeShape) {
1882
- const disabledBrowserStdio = () => ({
1883
- command: process.execPath,
1884
- args: ["-e", "process.exit(0)"],
1885
- enabled: false
1886
- });
1887
- const out = {
1888
- playwright: disabledBrowserStdio(),
1889
- "browser-rs": disabledBrowserStdio(),
1890
- patchright: disabledBrowserStdio()
1891
- };
1892
- for (const [name, srv] of Object.entries(claudeShape)) {
1893
- if (!srv || typeof srv !== "object")
1894
- continue;
1895
- const s = srv;
1896
- const codexName = codexMcpServerName(name);
1897
- if (typeof s.command === "string") {
1898
- out[codexName] = withCodexMcpServerOverrides(name, {
1899
- command: s.command,
1900
- ...Array.isArray(s.args) ? { args: s.args } : {},
1901
- ...s.env && typeof s.env === "object" ? { env: s.env } : {}
1902
- });
1903
- continue;
1904
- }
1905
- if (typeof s.url === "string" && s.type !== "sse") {
1906
- out[codexName] = withCodexMcpServerOverrides(name, {
1907
- url: s.url,
1908
- ...s.http_headers && typeof s.http_headers === "object" ? { http_headers: s.http_headers } : {},
1909
- ...s.env_http_headers && typeof s.env_http_headers === "object" ? { env_http_headers: s.env_http_headers } : {}
1910
- });
1911
- }
1912
- }
1913
- return out;
1914
- }
1915
- function isMissingCodexRolloutError(err) {
1916
- const msg = errMsg(err);
1917
- return /no rollout found|thread\/resume failed/i.test(msg);
1918
- }
1919
- function summarizeMcpToolCallResult(item) {
1920
- if (item.error)
1921
- return item.error.message.slice(0, 200);
1922
- if (!item.result)
1923
- return "";
1924
- const blocks = Array.isArray(item.result.content) ? item.result.content : [];
1925
- const text = blocks.map((b) => {
1926
- if (b && typeof b === "object" && "type" in b && b.type === "text" && "text" in b) {
1927
- return String(b.text ?? "");
1928
- }
1929
- return "";
1930
- }).filter(Boolean).join(`
1931
- `);
1932
- if (text)
1933
- return text.slice(0, 200);
1934
- return JSON.stringify(item.result.structured_content ?? "").slice(0, 200);
1935
- }
1936
- function fileChangeEvents(item) {
1937
- return item.changes.flatMap((change, index) => {
1938
- const name = change.kind === "add" ? "Write" : change.kind === "delete" ? "Delete" : "Edit";
1939
- const toolUseId = `${item.id}:${index}`;
1940
- const success = item.status === "completed";
1941
- return [
1942
- {
1943
- type: "tool_use",
1944
- name,
1945
- input: { file_path: change.path, change_kind: change.kind },
1946
- toolUseId
1947
- },
1948
- {
1949
- type: "tool_result",
1950
- toolUseId,
1951
- content: success ? `${change.kind} applied: ${change.path}` : `${change.kind} failed: ${change.path}`
1952
- }
1953
- ];
1954
- });
1955
- }
1956
- function promptForThread(opts, includeSystemPrompt) {
1957
- return includeSystemPrompt && opts.systemPrompt ? `[System Instructions]
1958
- ${opts.systemPrompt}
1959
-
1960
- ${opts.prompt}` : opts.prompt;
1961
- }
1962
- async function closeIterator(iter) {
1963
- try {
1964
- await iter.return?.();
1965
- } catch (err) {
1966
- logger.warn({ err }, "codexProvider: failed to close codex event iterator");
1967
- }
1968
- }
1969
- var CODEX_STARTUP_TIMEOUT_MS = 90000;
1970
- async function startStreamedWithTracking(thread, prompt, abortSignal, trackedPids) {
1971
- const release = await acquireCodexSpawnLock();
1972
- try {
1973
- const baseline = snapshotCodexChildren();
1974
- const runResult = await thread.runStreamed(prompt, {
1975
- ...abortSignal ? { signal: abortSignal } : {}
1976
- });
1977
- const iter = runResult.events[Symbol.asyncIterator]();
1978
- let first;
1979
- let startupTimer;
1980
- try {
1981
- const startupTimeout = new Promise((_, reject) => {
1982
- startupTimer = setTimeout(() => reject(new Error(`Codex\uAC00 ${CODEX_STARTUP_TIMEOUT_MS / 1000}\uCD08 \uB0B4 \uC751\uB2F5\uC744 \uC2DC\uC791\uD558\uC9C0 \uC54A\uC544 \uC911\uB2E8\uD588\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694.`)), CODEX_STARTUP_TIMEOUT_MS);
1983
- });
1984
- const nextP = iter.next();
1985
- nextP.catch(() => {});
1986
- first = await Promise.race([nextP, startupTimeout]);
1987
- } catch (err) {
1988
- const novel2 = findNewCodexChildren(baseline);
1989
- if (novel2.length > 0) {
1990
- killCodexTrees(novel2);
1991
- }
1992
- await closeIterator(iter);
1993
- throw err;
1994
- } finally {
1995
- if (startupTimer)
1996
- clearTimeout(startupTimer);
1997
- }
1998
- const novel = findNewCodexChildren(baseline);
1999
- trackedPids.pids = novel;
2000
- registerOwnedCodexPids(novel);
2001
- return { iter, firstEvent: first.value, done: !!first.done };
2002
- } finally {
2003
- release();
2004
- }
2005
- }
2006
- function prependFirstEvent(iter, firstEvent, alreadyDone) {
2007
- return {
2008
- [Symbol.asyncIterator]() {
2009
- let emittedFirst = false;
2010
- let exhausted = alreadyDone;
2011
- return {
2012
- async next() {
2013
- if (!emittedFirst) {
2014
- emittedFirst = true;
2015
- if (alreadyDone)
2016
- return { value: undefined, done: true };
2017
- return { value: firstEvent, done: false };
2018
- }
2019
- if (exhausted)
2020
- return { value: undefined, done: true };
2021
- const r = await iter.next();
2022
- if (r.done)
2023
- exhausted = true;
2024
- return r;
2025
- },
2026
- async return(value) {
2027
- exhausted = true;
2028
- if (iter.return)
2029
- return iter.return(value);
2030
- return { value, done: true };
2031
- }
2032
- };
2033
- }
2034
- };
2035
- }
2036
- async function* codexProvider(opts) {
2037
- const codexAuthPath = hostedCodexAuthFilePath();
2038
- if (!existsSync3(codexAuthPath)) {
2039
- yield {
2040
- type: "error",
2041
- content: `Codex auth file not found at ${codexAuthPath}. Run \`codex login\` to authenticate.`
2042
- };
2043
- return;
2044
- }
2045
- let codexModelCatalogPath;
2046
- try {
2047
- await ensureCodexModelCache(codexAuthPath);
2048
- codexModelCatalogPath = writeCodexCatalogWithNativeMultiAgentDisabled(codexAuthPath);
2049
- if (opts.sessionId)
2050
- migrateCodexRolloutNativeMultiAgentMetadata(opts.sessionId);
2051
- } catch (err) {
2052
- yield {
2053
- type: "error",
2054
- content: `Failed to enforce the Codex native multi-agent policy: ${errMsg(err)}`
2055
- };
2056
- return;
2057
- }
2058
- const codexMcpServers = toCodexMcpServers(hostedMcpServers(opts));
2059
- const browserOwner = browserOwnerForContext(opts);
2060
- const scopedBrowserCapability = opts.playwrightCapability && browserOwner ? browserOwnerCapability(opts.playwrightCapability, browserOwner) : undefined;
2061
- logger.info({
2062
- model: opts.model ?? "(sdk default)",
2063
- effort: opts.effort ?? "(off)",
2064
- cwd: opts.cwd,
2065
- cwdExists: existsSync3(opts.cwd),
2066
- resume: Boolean(opts.sessionId),
2067
- mcpServerCount: Object.keys(codexMcpServers).length
2068
- }, "codexProvider: starting turn");
2069
- const codex = new Codex({
2070
- ...scopedBrowserCapability ? {
2071
- env: {
2072
- ...Object.fromEntries(Object.entries(process.env).filter((entry) => typeof entry[1] === "string")),
2073
- [CODEX_BROWSER_CAPABILITY_ENV]: scopedBrowserCapability
2074
- }
2075
- } : {},
2076
- config: {
2077
- features: { multi_agent: false, multi_agent_v2: false, enable_fanout: false },
2078
- model_catalog_json: codexModelCatalogPath,
2079
- mcp_servers: codexMcpServers
2080
- }
2081
- });
2082
- const threadOptions = {
2083
- workingDirectory: opts.cwd,
2084
- skipGitRepoCheck: true,
2085
- sandboxMode: "danger-full-access",
2086
- approvalPolicy: "never",
2087
- ...opts.model ? { model: opts.model } : {},
2088
- ...opts.effort ? { modelReasoningEffort: mapEffort(opts.effort) } : {}
2089
- };
2090
- let currentSessionId = opts.sessionId;
2091
- let thread = currentSessionId ? codex.resumeThread(currentSessionId, threadOptions) : codex.startThread(threadOptions);
2092
- let prompt = promptForThread(opts, true);
2093
- let agentTextSoFar = "";
2094
- let currentAgentMessageId;
2095
- let finalText = "";
2096
- let stopReason = "end_turn";
2097
- const trackedPids = { pids: [] };
2098
- const abortSignal = opts.abortController?.signal;
2099
- const onAbortKill = () => {
2100
- if (trackedPids.pids.length > 0) {
2101
- killCodexTrees(trackedPids.pids);
2102
- }
2103
- };
2104
- abortSignal?.addEventListener("abort", onAbortKill, { once: true });
2105
- let heartbeatMs = 8 * 60 * 1000;
2106
- let heartbeatAttempts = 0;
2107
- const HEARTBEAT_MAX_MS = 16 * 60 * 1000;
2108
- const HEARTBEAT_MAX_ATTEMPTS = 2;
2109
- const HEARTBEAT_CONTINUATION = "\uACC4\uC18D \uC9C4\uD589\uD574\uC918.";
2110
- try {
2111
- while (true) {
2112
- if (abortSignal?.aborted)
2113
- return;
2114
- const attemptAbort = new AbortController;
2115
- const propagateOuter = () => attemptAbort.abort();
2116
- abortSignal?.addEventListener("abort", propagateOuter, { once: true });
2117
- trackedPids.pids = [];
2118
- let heartbeatTriggered = false;
2119
- let attemptCompleted = false;
2120
- let heartbeatTimer = null;
2121
- const scheduleHeartbeat = () => {
2122
- if (heartbeatTimer !== null)
2123
- clearTimeout(heartbeatTimer);
2124
- if (abortSignal?.aborted || attemptAbort.signal.aborted)
2125
- return;
2126
- heartbeatTimer = setTimeout(() => {
2127
- heartbeatTriggered = true;
2128
- if (trackedPids.pids.length > 0)
2129
- killCodexTrees(trackedPids.pids);
2130
- attemptAbort.abort();
2131
- }, heartbeatMs);
2132
- };
2133
- try {
2134
- scheduleHeartbeat();
2135
- let startResult;
2136
- try {
2137
- startResult = await startStreamedWithTracking(thread, prompt, attemptAbort.signal, trackedPids);
2138
- } catch (err) {
2139
- if (err instanceof Error && err.name === "AbortError")
2140
- throw err;
2141
- if (!currentSessionId || !isMissingCodexRolloutError(err))
2142
- throw err;
2143
- logger.warn({ staleSessionId: currentSessionId, err: errMsg(err) }, "codexProvider: stale/missing rollout, restarting fresh thread");
2144
- thread = codex.startThread(threadOptions);
2145
- prompt = promptForThread(opts, true);
2146
- currentSessionId = undefined;
2147
- startResult = await startStreamedWithTracking(thread, prompt, attemptAbort.signal, trackedPids);
2148
- }
2149
- if (attemptAbort.signal.aborted) {
2150
- killCodexTrees(trackedPids.pids);
2151
- await closeIterator(startResult.iter);
2152
- throw Object.assign(new Error("heartbeat abort"), { name: "AbortError" });
2153
- }
2154
- const eventStream = prependFirstEvent(startResult.iter, startResult.firstEvent, startResult.done);
2155
- for await (const rawEvent of eventStream) {
2156
- scheduleHeartbeat();
2157
- const event = rawEvent;
2158
- switch (event.type) {
2159
- case "thread.started": {
2160
- if (event.thread_id) {
2161
- currentSessionId = event.thread_id;
2162
- yield { type: "session", sessionId: event.thread_id };
2163
- }
2164
- break;
2165
- }
2166
- case "item.started": {
2167
- const item = event.item;
2168
- if (!item)
2169
- break;
2170
- if (item.type === "command_execution") {
2171
- yield {
2172
- type: "tool_use",
2173
- name: "Bash",
2174
- input: { command: String(item.command ?? "") },
2175
- toolUseId: String(item.id ?? "")
2176
- };
2177
- } else if (item.type === "mcp_tool_call") {
2178
- yield {
2179
- type: "tool_use",
2180
- name: String(item.tool ?? "unknown"),
2181
- input: item.arguments && typeof item.arguments === "object" ? item.arguments : {},
2182
- toolUseId: String(item.id ?? "")
2183
- };
2184
- }
2185
- break;
2186
- }
2187
- case "item.updated": {
2188
- const item = event.item;
2189
- if (!item)
2190
- break;
2191
- if (item.type === "agent_message") {
2192
- const messageId = item.id != null ? String(item.id) : undefined;
2193
- if (messageId !== currentAgentMessageId) {
2194
- currentAgentMessageId = messageId;
2195
- agentTextSoFar = "";
2196
- }
2197
- const text = String(item.text ?? "");
2198
- const newChars = text.slice(agentTextSoFar.length);
2199
- if (newChars) {
2200
- yield { type: "text_delta", content: newChars };
2201
- agentTextSoFar = text;
2202
- }
2203
- }
2204
- break;
2205
- }
2206
- case "item.completed": {
2207
- const item = event.item;
2208
- if (!item)
2209
- break;
2210
- if (item.type === "agent_message") {
2211
- finalText = String(item.text ?? "");
2212
- agentTextSoFar = finalText;
2213
- yield { type: "text", content: finalText };
2214
- yield* extractFileEvents(finalText, "text");
2215
- } else if (item.type === "reasoning") {
2216
- const reasoning = String(item.text ?? "").trim();
2217
- if (reasoning)
2218
- yield { type: "reasoning", content: reasoning };
2219
- } else if (item.type === "mcp_tool_call") {
2220
- yield {
2221
- type: "tool_result",
2222
- toolUseId: String(item.id ?? ""),
2223
- content: summarizeMcpToolCallResult(item)
2224
- };
2225
- } else if (item.type === "command_execution") {
2226
- yield {
2227
- type: "tool_result",
2228
- toolUseId: String(item.id ?? ""),
2229
- content: String(item.aggregated_output ?? "").slice(0, 200)
2230
- };
2231
- } else if (item.type === "file_change") {
2232
- for (const fileEvent of fileChangeEvents(item)) {
2233
- yield fileEvent;
2234
- }
2235
- } else if (item.type === "error") {
2236
- yield { type: "error", content: String(item.message ?? "") };
2237
- }
2238
- break;
2239
- }
2240
- case "turn.completed": {
2241
- const usage = event.usage;
2242
- if (!usage)
2243
- break;
2244
- attemptCompleted = true;
2245
- const contextUsage = currentSessionId ? readLatestCodexContextUsage(currentSessionId) : undefined;
2246
- yield {
2247
- type: "result",
2248
- content: finalText,
2249
- stopReason,
2250
- usage: {
2251
- inputTokens: usage.input_tokens,
2252
- outputTokens: usage.output_tokens,
2253
- cacheReadInputTokens: usage.cached_input_tokens,
2254
- ...contextUsage
2255
- }
2256
- };
2257
- if (finalText)
2258
- yield* extractFileEvents(finalText, "result");
2259
- break;
2260
- }
2261
- case "turn.failed": {
2262
- stopReason = "error";
2263
- yield {
2264
- type: "error",
2265
- content: event.error?.message || "Codex turn failed"
2266
- };
2267
- break;
2268
- }
2269
- default:
2270
- break;
2271
- }
2272
- }
2273
- } catch (err) {
2274
- if (abortSignal?.aborted)
2275
- return;
2276
- if (err instanceof Error && err.name === "AbortError") {
2277
- if (!heartbeatTriggered)
2278
- return;
2279
- } else {
2280
- logger.error({ err }, "codexProvider: attempt failed");
2281
- yield { type: "error", content: errMsg(err) };
2282
- break;
2283
- }
2284
- } finally {
2285
- if (heartbeatTimer !== null)
2286
- clearTimeout(heartbeatTimer);
2287
- abortSignal?.removeEventListener("abort", propagateOuter);
2288
- if (trackedPids.pids.length > 0)
2289
- unregisterOwnedCodexPids(trackedPids.pids);
2290
- }
2291
- if (attemptCompleted || abortSignal?.aborted)
2292
- break;
2293
- if (!heartbeatTriggered)
2294
- break;
2295
- heartbeatAttempts++;
2296
- if (heartbeatAttempts > HEARTBEAT_MAX_ATTEMPTS) {
2297
- logger.warn({ attempts: heartbeatAttempts }, "codexProvider: max heartbeat attempts reached");
2298
- yield {
2299
- type: "error",
2300
- content: `Codex\uAC00 ${HEARTBEAT_MAX_ATTEMPTS}\uD68C \uC7AC\uC2DC\uB3C4 \uD6C4\uC5D0\uB3C4 \uC751\uB2F5\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`
2301
- };
2302
- break;
2303
- }
2304
- heartbeatMs = Math.min(heartbeatMs * 2, HEARTBEAT_MAX_MS);
2305
- logger.info({ attempt: heartbeatAttempts, nextIntervalMs: heartbeatMs }, "codexProvider: heartbeat fired, resuming thread with continuation prompt");
2306
- agentTextSoFar = "";
2307
- finalText = "";
2308
- thread = currentSessionId ? codex.resumeThread(currentSessionId, threadOptions) : codex.startThread(threadOptions);
2309
- prompt = currentSessionId ? HEARTBEAT_CONTINUATION : promptForThread(opts, true);
2310
- }
2311
- } catch (err) {
2312
- if (err instanceof Error && err.name === "AbortError")
2313
- return;
2314
- if (abortSignal?.aborted)
2315
- return;
2316
- logger.error({ err }, "codexProvider: setup failed");
2317
- yield { type: "error", content: errMsg(err) };
2318
- } finally {
2319
- abortSignal?.removeEventListener("abort", onAbortKill);
2320
- if (trackedPids.pids.length > 0)
2321
- unregisterOwnedCodexPids(trackedPids.pids);
2322
- }
2323
- }
21
+ appendConversationEvent,
22
+ readConversation,
23
+ sanitizeFileName,
24
+ sanitizeTopicName
25
+ } from "./chunk-mhty3nna.js";
26
+ import {
27
+ claudeProvider,
28
+ codexProvider,
29
+ maestroProvider
30
+ } from "./chunk-n0kk5xbq.js";
31
+ import {
32
+ encodeClaudeCwd
33
+ } from "./chunk-2dgkah1z.js";
34
+ import {
35
+ TOPIC_MAINTENANCE_STALE_MS,
36
+ TURN_LEASE_STALE_MS,
37
+ getRoomQuery,
38
+ getRuntimeTopicEpoch
39
+ } from "./chunk-efyxk97v.js";
40
+ import {
41
+ db
42
+ } from "./chunk-1msje2zf.js";
43
+ import {
44
+ AGENTS_PROMPTS_DIR,
45
+ BACKGROUND_BASH_SERVER,
46
+ BG_BASH_BASE_PORT,
47
+ BG_BASH_MAX_PORT,
48
+ DATA_DIR,
49
+ PROJECT_ROOT,
50
+ RESOURCES_DIR,
51
+ SHARED_WIKI_DIR,
52
+ USERS_LOG_DIR,
53
+ WORKSPACE_DIR,
54
+ isAgentKind,
55
+ logger
56
+ } from "./chunk-k9z47mab.js";
57
+ import {
58
+ __require,
59
+ __toESM
60
+ } from "./chunk-hwy3n31w.js";
2324
61
 
2325
- // ../../packages/core/src/agents/maestro-provider.ts
2326
- import { maestroProvider as sdkMaestroProvider, setMcpResolver } from "maestro-agent-sdk";
2327
- var MAESTRO_DEFAULT_MAX_TOKENS = 32768;
2328
- var PROVIDER_ASK_USER_TOOL = "AskUserQuestion";
2329
- var PROVIDER_SUBAGENT_TOOL = "Agent";
2330
- var MAESTRO_NATIVE_TASK_TOOLS = [
2331
- "TaskCreate",
2332
- "TaskUpdate",
2333
- "TaskList",
2334
- "TaskGet",
2335
- "TaskOutput",
2336
- "TaskStop"
2337
- ];
2338
- var MAESTRO_PROVIDER_OWNED_TOOL_SET = new Set([
2339
- PROVIDER_ASK_USER_TOOL,
2340
- PROVIDER_SUBAGENT_TOOL,
2341
- ...MAESTRO_NATIVE_TASK_TOOLS
2342
- ]);
2343
- var DEFAULT_MAESTRO_DISALLOWED_TOOLS = [
2344
- PROVIDER_ASK_USER_TOOL,
2345
- PROVIDER_SUBAGENT_TOOL,
2346
- ...MAESTRO_NATIVE_TASK_TOOLS
2347
- ];
2348
- function buildMaestroDisallowedTools(callerDisallowedTools = []) {
2349
- return [...new Set([...DEFAULT_MAESTRO_DISALLOWED_TOOLS, ...callerDisallowedTools])];
2350
- }
2351
- function buildVaultHook(userId) {
2352
- return {
2353
- name: "vault-guard",
2354
- pre({ toolName, input }) {
2355
- if (referencesHostedSecretStorage(input)) {
2356
- return { decision: "block", error: "Runtime secret storage access is not permitted" };
2357
- }
2358
- if (shouldRedirectHostedVaultTool(userId, toolName, input)) {
2359
- return { decision: "block", error: VAULT_BROKER_REDIRECT_ERROR };
2360
- }
2361
- return { decision: "allow" };
2362
- },
2363
- post({ output }) {
2364
- const redacted = redactHostedSecrets(userId, output);
2365
- return redacted === output ? {} : { output: redacted };
2366
- }
2367
- };
2368
- }
2369
- function buildMaestroToolHooks(userId) {
2370
- return [buildVaultHook(userId), buildProviderOwnedToolBlockHook()];
2371
- }
2372
- function providerOwnedToolRedirect(toolName) {
2373
- if (toolName === PROVIDER_ASK_USER_TOOL) {
2374
- return "Use the runtime ask_user_question MCP tool instead.";
2375
- }
2376
- if (toolName === PROVIDER_SUBAGENT_TOOL) {
2377
- return "Use the runtime spawn_subagent MCP tool instead.";
2378
- }
2379
- return "Use the shared task MCP tools instead " + "(mcp__task__task_create / task_update / task_list / task_get / task_delete).";
2380
- }
2381
- function buildProviderOwnedToolBlockHook() {
2382
- return {
2383
- name: "provider-owned-tool-redirect",
2384
- pre({ toolName }) {
2385
- if (!MAESTRO_PROVIDER_OWNED_TOOL_SET.has(toolName))
2386
- return { decision: "allow" };
2387
- return {
2388
- decision: "block",
2389
- error: `${toolName} is disabled in this environment. ${providerOwnedToolRedirect(toolName)}`
2390
- };
2391
- }
2392
- };
2393
- }
2394
- function maestroProvider(opts) {
2395
- if (opts.agent !== undefined && opts.agent !== "maestro") {
2396
- throw new Error(`maestroProvider: unexpected agent "${opts.agent}", expected "maestro"`);
2397
- }
2398
- const userId = opts.userId ?? "";
2399
- setMcpResolver(hostedMcpServers);
2400
- const callerDisallowedTools = opts.disallowedTools ?? [];
2401
- const callerToolHooks = opts.toolHooks ?? [];
2402
- const sdkOpts = {
2403
- enableBackgroundBash: false,
2404
- maxTokens: MAESTRO_DEFAULT_MAX_TOKENS,
2405
- enableToolSearch: true,
2406
- ...opts,
2407
- agent: "maestro",
2408
- disallowedTools: buildMaestroDisallowedTools(callerDisallowedTools),
2409
- toolHooks: [...buildMaestroToolHooks(userId), ...callerToolHooks]
2410
- };
2411
- return sdkMaestroProvider(sdkOpts);
2412
- }
62
+ // ../../packages/core/src/agents/index.ts
63
+ import { existsSync as existsSync2 } from "fs";
64
+ import { homedir } from "os";
65
+ import { join as join2 } from "path";
2413
66
 
2414
67
  // ../../packages/core/src/storage/tasks.ts
2415
- import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync3, renameSync as renameSync2, statSync, writeFileSync as writeFileSync2 } from "fs";
2416
- import { dirname as dirname2, join as join3 } from "path";
68
+ import { existsSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "fs";
69
+ import { dirname, join } from "path";
2417
70
  function safeUserIdComponent(userId) {
2418
71
  const str = String(userId);
2419
72
  if (!str || /[/\\]|\.\./.test(str)) {
@@ -2432,14 +85,14 @@ function taskScopeKey(opts) {
2432
85
  return opts.topicId?.trim() || opts.session || "default";
2433
86
  }
2434
87
  function getTaskFilePath(userId, scopeKey) {
2435
- return join3(DATA_DIR, "tasks", safeUserIdComponent(userId), `${safeTaskScopeKey(scopeKey)}.json`);
88
+ return join(DATA_DIR, "tasks", safeUserIdComponent(userId), `${safeTaskScopeKey(scopeKey)}.json`);
2436
89
  }
2437
90
  function readTasks(userId, scopeKey) {
2438
91
  const path = getTaskFilePath(userId, scopeKey);
2439
- if (!existsSync4(path))
92
+ if (!existsSync(path))
2440
93
  return [];
2441
94
  try {
2442
- const parsed = JSON.parse(readFileSync3(path, "utf-8"));
95
+ const parsed = JSON.parse(readFileSync(path, "utf-8"));
2443
96
  return Array.isArray(parsed?.tasks) ? parsed.tasks : [];
2444
97
  } catch (e) {
2445
98
  logger.warn({ err: e, path }, "tasks: failed to read task store");
@@ -2515,11 +168,11 @@ async function resolveSessionFileMissing(agent, sessionId, cwd) {
2515
168
  switch (agent) {
2516
169
  case "claude": {
2517
170
  const encodedCwd = encodeClaudeCwd(cwd);
2518
- const path = join4(homedir(), ".claude", "projects", encodedCwd, `${sessionId}.jsonl`);
2519
- return !existsSync5(path);
171
+ const path = join2(homedir(), ".claude", "projects", encodedCwd, `${sessionId}.jsonl`);
172
+ return !existsSync2(path);
2520
173
  }
2521
174
  case "codex": {
2522
- const sessionsDir = join4(process.env.CODEX_HOME || join4(homedir(), ".codex"), "sessions");
175
+ const sessionsDir = join2(process.env.CODEX_HOME || join2(homedir(), ".codex"), "sessions");
2523
176
  const glob = new Bun.Glob(`**/rollout-*-${sessionId}.jsonl`);
2524
177
  for await (const _rel of glob.scan({ cwd: sessionsDir, onlyFiles: true })) {
2525
178
  return false;
@@ -2527,8 +180,8 @@ async function resolveSessionFileMissing(agent, sessionId, cwd) {
2527
180
  return true;
2528
181
  }
2529
182
  case "maestro": {
2530
- const path = join4(homedir(), ".maestro", "sessions", `${sessionId}.jsonl`);
2531
- return !existsSync5(path);
183
+ const path = join2(homedir(), ".maestro", "sessions", `${sessionId}.jsonl`);
184
+ return !existsSync2(path);
2532
185
  }
2533
186
  default: {
2534
187
  const _exhaustive = agent;
@@ -2592,12 +245,12 @@ function errorResult(text) {
2592
245
  }
2593
246
 
2594
247
  // ../../packages/core/src/prompts/builders.ts
2595
- import { readFileSync as readFileSync4 } from "fs";
2596
- import { resolve as resolve2 } from "path";
2597
- var PROMPTS_DIR = resolve2(PROJECT_ROOT, "src/prompts");
2598
- var SESSIONS_DIR = resolve2(PROMPTS_DIR, "sessions");
248
+ import { readFileSync as readFileSync2 } from "fs";
249
+ import { resolve } from "path";
250
+ var PROMPTS_DIR = resolve(PROJECT_ROOT, "src/prompts");
251
+ var SESSIONS_DIR = resolve(PROMPTS_DIR, "sessions");
2599
252
  function loadPrompt(filename, dir = SESSIONS_DIR) {
2600
- const raw = readFileSync4(resolve2(dir, filename), "utf-8");
253
+ const raw = readFileSync2(resolve(dir, filename), "utf-8");
2601
254
  return raw.replace(/\{\{RESOURCES_DIR\}\}/g, RESOURCES_DIR);
2602
255
  }
2603
256
  function replaceVars(template, vars) {
@@ -2669,7 +322,7 @@ function visualDesignGuide() {
2669
322
  return _visualDesignGuide;
2670
323
  }
2671
324
  function loadAgentPrompt(filename) {
2672
- const raw = readFileSync4(resolve2(AGENTS_PROMPTS_DIR, filename), "utf-8");
325
+ const raw = readFileSync2(resolve(AGENTS_PROMPTS_DIR, filename), "utf-8");
2673
326
  const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
2674
327
  if (!match)
2675
328
  throw new Error(`Agent prompt ${filename} is missing frontmatter`);
@@ -2700,7 +353,7 @@ function loadAgentPrompt(filename) {
2700
353
  prompt: match[2].trim()
2701
354
  };
2702
355
  }
2703
- function buildRuntimeToolSection(agentKind, canSpawnSubagents = false) {
356
+ function buildRuntimeToolSection(agentKind, canSpawnSubagents = false, visualTools = false) {
2704
357
  const runtimeNamespace = "mcp__runtime";
2705
358
  const taskNamespace = "mcp__task";
2706
359
  const visualToolLine = agentKind === "codex" ? `To display charts, tables, or interactive HTML results to the user, call the \`show_html\` function in the \`${runtimeNamespace}\` namespace with { html: "<complete HTML string>", title?: "optional title" }.` : `To display charts, tables, or interactive HTML results to the user, call the MCP tool "${runtimeNamespace}__show_html" with { html: "<complete HTML string>", title?: "optional title" }.`;
@@ -2720,19 +373,22 @@ function buildRuntimeToolSection(agentKind, canSpawnSubagents = false) {
2720
373
  "Use it for parallelizable or long-running side work; keep quick inline work in this room."
2721
374
  ] : [];
2722
375
  const nativeTaskPolicyLine = agentKind === "claude" ? `Do not use provider-native todo/task/subagent tools such as "TodoWrite", "Task", "Agent", "TaskCreate", "TaskUpdate", "TaskList", "TaskOutput", or "TaskStop"; they are disabled or not shared across agents.${canSpawnSubagents ? " For delegation, use the runtime spawn_subagent tool instead." : ""}` : agentKind === "maestro" ? `Do not use provider-native task-store tools such as "TaskCreate", "TaskUpdate", "TaskList", "TaskGet", "TaskOutput", or "TaskStop"; they are disabled or not shared across agents. Do not use the Maestro "Agent" sub-agent tool either; it is disabled.${canSpawnSubagents ? " Use the runtime spawn_subagent tool for delegation so work is visible in its own room and the result returns here automatically." : " Delegation is unavailable in this room."}` : 'Do not use provider-native todo/plan surfaces such as "todo_list" or "update_plan"; they are ignored or not shared across agents.';
2723
- const shared = [
2724
- "",
2725
- "",
2726
- "## Runtime Tools",
376
+ const visualSection = visualTools ? [
2727
377
  visualToolLine,
2728
378
  mermaidToolLine,
2729
379
  mediaToolLine,
2730
380
  'Do not call a bare "show_html"; use the exposed visuals MCP tool. A successful call means the card was shown in the user chat.',
2731
- "Visual HTML runs in a sandbox. Use inline CSS/JS only; local buttons, tabs, filters, forms with preventDefault, canvas, and SVG interactions are supported. External navigation, scripts, network fetches, form posts, popups, and parent-window access are blocked.",
381
+ "Visual HTML runs in a sandbox. Use inline CSS/JS only; local buttons, tabs, filters, forms with preventDefault, canvas, and SVG interactions are supported. External navigation, scripts, network fetches, form posts, popups, and parent-window access are blocked."
382
+ ] : [];
383
+ const shared = [
384
+ "",
385
+ "",
386
+ "## Runtime Tools",
387
+ ...visualSection,
2732
388
  askUserToolLine,
2733
389
  'Do not use provider built-in "AskUserQuestion"; it is disabled or unsupported in this headless chat runtime. Use the runtime ask_user_question tool instead.',
2734
390
  scheduleSelfToolLine,
2735
- ...visualDesignGuide() ? ["", visualDesignGuide()] : [],
391
+ ...visualTools ? ["", visualDesignGuide()] : [],
2736
392
  "",
2737
393
  "## Shared Tasks",
2738
394
  taskToolLine,
@@ -2788,7 +444,7 @@ function buildTopicSystemPrompt(opts) {
2788
444
  TOPIC_TITLE: opts.topicTitle,
2789
445
  WORKSPACE_CWD: opts.workspaceCwd,
2790
446
  UPLOADS_DIR: uploadsDir
2791
- }) + buildRuntimeToolSection(opts.agentKind, opts.canSpawnSubagents);
447
+ }) + buildRuntimeToolSection(opts.agentKind, opts.canSpawnSubagents, opts.visualTools);
2792
448
  if (opts.description?.trim()) {
2793
449
  prompt += `
2794
450
 
@@ -2804,7 +460,7 @@ function buildChannelSystemPrompt(opts) {
2804
460
  TOPIC_TITLE: opts.topicTitle,
2805
461
  WORKSPACE_CWD: opts.workspaceCwd,
2806
462
  UPLOADS_DIR: uploadsDir
2807
- }) + buildRuntimeToolSection(opts.agentKind);
463
+ }) + buildRuntimeToolSection(opts.agentKind, false, opts.visualTools);
2808
464
  }
2809
465
  function buildManagerSystemPrompt(opts) {
2810
466
  return `${buildTopicSystemPrompt(opts)}
@@ -2883,9 +539,9 @@ function deleteTopicBrief(topicId) {
2883
539
  }
2884
540
 
2885
541
  // ../../packages/core/src/storage/wiki.ts
2886
- import { basename, dirname as dirname3, join as join5 } from "path";
542
+ import { basename, dirname as dirname2, join as join3 } from "path";
2887
543
  function getSharedWikiDir(workspaceDir = WORKSPACE_DIR) {
2888
- return workspaceDir === WORKSPACE_DIR ? SHARED_WIKI_DIR : join5(workspaceDir, "wiki");
544
+ return workspaceDir === WORKSPACE_DIR ? SHARED_WIKI_DIR : join3(workspaceDir, "wiki");
2889
545
  }
2890
546
 
2891
547
  // ../../packages/core/src/storage/wiki-summary-names.ts
@@ -2904,8 +560,8 @@ function wikiSummaryFilename(date, rawTopic, topicId) {
2904
560
 
2905
561
  // ../../packages/core/src/agents/archiver.ts
2906
562
  import { randomUUID } from "crypto";
2907
- import { existsSync as existsSync6, readdirSync, readFileSync as readFileSync5, statSync as statSync2 } from "fs";
2908
- import { join as join6 } from "path";
563
+ import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync3, statSync as statSync2 } from "fs";
564
+ import { join as join4 } from "path";
2909
565
  var MAX_BRIEF_ENTRIES = 8;
2910
566
  var MIN_ARCHIVE_MESSAGES = 4;
2911
567
  var MAX_SESSION_STEPS = 20;
@@ -3072,17 +728,17 @@ function distillOneLine(summaryMd) {
3072
728
  return "";
3073
729
  }
3074
730
  function findSummaryFile(topicTitle, date, sinceMs, topicId) {
3075
- const dir = join6(getSharedWikiDir(), "summaries");
3076
- if (!existsSync6(dir))
731
+ const dir = join4(getSharedWikiDir(), "summaries");
732
+ if (!existsSync3(dir))
3077
733
  return null;
3078
- const predicted = join6(dir, wikiSummaryFilename(date, topicTitle, topicId));
3079
- if (existsSync6(predicted))
734
+ const predicted = join4(dir, wikiSummaryFilename(date, topicTitle, topicId));
735
+ if (existsSync3(predicted))
3080
736
  return predicted;
3081
737
  let best = null;
3082
738
  for (const f of readdirSync(dir)) {
3083
739
  if (!f.endsWith(".md"))
3084
740
  continue;
3085
- const p = join6(dir, f);
741
+ const p = join4(dir, f);
3086
742
  try {
3087
743
  const m = statSync2(p).mtimeMs;
3088
744
  if (m >= sinceMs && (!best || m > best.mtime))
@@ -3096,7 +752,7 @@ function finalizeGeneralMemory(userId, topicTitle, messageCount, startMs, ok, to
3096
752
  const date = new Date().toISOString().slice(0, 10);
3097
753
  try {
3098
754
  const summaryPath = ok ? findSummaryFile(topicTitle, date, startMs, topicId) : null;
3099
- const summaryMd = summaryPath ? readFileSync5(summaryPath, "utf-8") : "";
755
+ const summaryMd = summaryPath ? readFileSync3(summaryPath, "utf-8") : "";
3100
756
  const oneLine = summaryMd && distillOneLine(summaryMd) || `${messageCount}\uAC1C \uBA54\uC2DC\uC9C0 \uC544\uCE74\uC774\uBE0C`;
3101
757
  const prev = getTopicBrief(generalTopicId);
3102
758
  const prevEntries = (prev?.briefMd ?? "").split(`
@@ -3145,8 +801,8 @@ ${rolled.join(`
3145
801
  }
3146
802
 
3147
803
  // ../../packages/core/src/storage/topic-archive.ts
3148
- import { existsSync as existsSync7, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3 } from "fs";
3149
- import { join as join7 } from "path";
804
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
805
+ import { join as join5 } from "path";
3150
806
 
3151
807
  // ../../packages/core/src/storage/topic-transcript.ts
3152
808
  function parseJsonField(value) {
@@ -3210,22 +866,22 @@ function archiveTopicMessages(topicId, topicTitle, options = {}) {
3210
866
  if (rows.length === 0)
3211
867
  return null;
3212
868
  const safeTopic = sanitizeTopicName(topicTitle, true);
3213
- const archiveDir = join7(getSharedWikiDir(), "archive");
3214
- mkdirSync3(archiveDir, { recursive: true });
869
+ const archiveDir = join5(getSharedWikiDir(), "archive");
870
+ mkdirSync2(archiveDir, { recursive: true });
3215
871
  const date = new Date().toISOString().slice(0, 10);
3216
872
  const reasonSuffix = options.reason === "idle" ? "_idle" : "";
3217
873
  let filename = `${safeTopic}_${date}${reasonSuffix}.jsonl`;
3218
874
  let counter = 1;
3219
- while (existsSync7(join7(archiveDir, filename))) {
875
+ while (existsSync4(join5(archiveDir, filename))) {
3220
876
  counter++;
3221
877
  filename = `${safeTopic}_${date}${reasonSuffix}_${counter}.jsonl`;
3222
878
  }
3223
- const path = join7(archiveDir, filename);
879
+ const path = join5(archiveDir, filename);
3224
880
  const lastRowid = rows.reduce((max, row) => Math.max(max, row.rowid ?? 0), 0);
3225
881
  const body = `${rows.map((r, index) => JSON.stringify(formatTopicArchiveTranscriptRecord(r, topicTitle, index + 1))).join(`
3226
882
  `)}
3227
883
  `;
3228
- writeFileSync3(path, body);
884
+ writeFileSync2(path, body);
3229
885
  logger.info({ topicId, topicTitle, archive: path, messageCount: rows.length, lastRowid }, "archiveTopicMessages: archived topic messages");
3230
886
  return { path, messageCount: rows.length, lastRowid };
3231
887
  }
@@ -3362,7 +1018,7 @@ function runIdleArchiveForTopic(topicId, userId) {
3362
1018
  }
3363
1019
 
3364
1020
  // ../../packages/core/src/platform/background-bash/manager.ts
3365
- import { execFileSync as execFileSync2, spawn as spawn3 } from "child_process";
1021
+ import { execFileSync, spawn } from "child_process";
3366
1022
  function makeBgBashKey(_userId, topic) {
3367
1023
  return `topic:${topic}`;
3368
1024
  }
@@ -3371,7 +1027,7 @@ var usedPorts = new Set;
3371
1027
  var spawning = new Map;
3372
1028
  function pidsOnPort(port) {
3373
1029
  try {
3374
- return execFileSync2("lsof", ["-i", `:${port}`, "-t"], { stdio: "pipe" }).toString().trim().split(`
1030
+ return execFileSync("lsof", ["-i", `:${port}`, "-t"], { stdio: "pipe" }).toString().trim().split(`
3375
1031
  `).map((pid) => parseInt(pid, 10)).filter((pid) => !Number.isNaN(pid));
3376
1032
  } catch {
3377
1033
  return [];
@@ -3382,7 +1038,7 @@ function isPortInUse(port) {
3382
1038
  }
3383
1039
  function processCommand(pid) {
3384
1040
  try {
3385
- return execFileSync2("ps", ["-p", String(pid), "-o", "command="], { stdio: "pipe" }).toString().trim();
1041
+ return execFileSync("ps", ["-p", String(pid), "-o", "command="], { stdio: "pipe" }).toString().trim();
3386
1042
  } catch {
3387
1043
  return "";
3388
1044
  }
@@ -3447,7 +1103,7 @@ async function spawnBgBash(key, userId, topic, reservedPort) {
3447
1103
  `--topic=${topic}`,
3448
1104
  `--port=${port}`
3449
1105
  ];
3450
- const proc = spawn3("bun", serverArgs, {
1106
+ const proc = spawn("bun", serverArgs, {
3451
1107
  stdio: "ignore",
3452
1108
  detached: false,
3453
1109
  env: { ...process.env }
@@ -3523,14 +1179,14 @@ async function killAllBgBash() {
3523
1179
  instances.clear();
3524
1180
  usedPorts.clear();
3525
1181
  const deadline = Date.now() + 3000;
3526
- await Promise.all(entries.map(([, inst]) => new Promise((resolve3) => {
1182
+ await Promise.all(entries.map(([, inst]) => new Promise((resolve2) => {
3527
1183
  if (inst.process.exitCode !== null || inst.process.killed) {
3528
- resolve3();
1184
+ resolve2();
3529
1185
  return;
3530
1186
  }
3531
- inst.process.once("exit", () => resolve3());
3532
- inst.process.once("error", () => resolve3());
3533
- const t = setTimeout(resolve3, Math.max(0, deadline - Date.now()));
1187
+ inst.process.once("exit", () => resolve2());
1188
+ inst.process.once("error", () => resolve2());
1189
+ const t = setTimeout(resolve2, Math.max(0, deadline - Date.now()));
3534
1190
  t.unref?.();
3535
1191
  })));
3536
1192
  }
@@ -3550,29 +1206,29 @@ setInterval(() => {
3550
1206
  }, 30 * 60 * 1000).unref();
3551
1207
 
3552
1208
  // ../../packages/core/src/query/state.ts
3553
- import { mkdirSync as mkdirSync4, readdirSync as readdirSync2, renameSync as renameSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
3554
- import { join as join8 } from "path";
1209
+ import { mkdirSync as mkdirSync3, readdirSync as readdirSync2, renameSync as renameSync2, unlinkSync, writeFileSync as writeFileSync3 } from "fs";
1210
+ import { join as join6 } from "path";
3555
1211
  function queryStateDirPath(userId) {
3556
- return join8(USERS_LOG_DIR, String(userId), "active-queries");
1212
+ return join6(USERS_LOG_DIR, String(userId), "active-queries");
3557
1213
  }
3558
1214
  function queryStateFile(userId, topicName) {
3559
1215
  const name = `${topicName}.json`;
3560
- return join8(queryStateDirPath(userId), name);
1216
+ return join6(queryStateDirPath(userId), name);
3561
1217
  }
3562
1218
  function writeQueryState(userId, topicName, task) {
3563
1219
  const dir = queryStateDirPath(userId);
3564
- mkdirSync4(dir, { recursive: true });
1220
+ mkdirSync3(dir, { recursive: true });
3565
1221
  const state = { since: new Date().toISOString() };
3566
1222
  if (task)
3567
1223
  state.task = [...task.replace(/\n+/g, " ").trim()].slice(0, 100).join("");
3568
1224
  const target = queryStateFile(userId, topicName);
3569
1225
  const tmp = `${target}.${process.pid}.${Date.now()}.tmp`;
3570
- writeFileSync4(tmp, JSON.stringify(state));
3571
- renameSync3(tmp, target);
1226
+ writeFileSync3(tmp, JSON.stringify(state));
1227
+ renameSync2(tmp, target);
3572
1228
  }
3573
1229
  function clearQueryState(userId, topicName) {
3574
1230
  try {
3575
- unlinkSync2(queryStateFile(userId, topicName));
1231
+ unlinkSync(queryStateFile(userId, topicName));
3576
1232
  } catch (e) {
3577
1233
  if (e?.code !== "ENOENT") {
3578
1234
  logger.warn({ err: e, userId, topicName }, "Failed to clear query state file");
@@ -4119,7 +1775,7 @@ async function notifyParent(watch, prompt, noteKind) {
4119
1775
  if (!parent)
4120
1776
  return;
4121
1777
  const requestId = `subagent-${noteKind}-${watch.childTopicId}`;
4122
- const { triggerTopicAiTurn } = await import("./chunk-rbrxjvbj.js");
1778
+ const { triggerTopicAiTurn } = await import("./chunk-nmv41qx9.js");
4123
1779
  const queryId = parent.agent ? triggerTopicAiTurn(watch.parentTopicId, watch.userId, prompt, undefined, {
4124
1780
  origin: `subagent:${watch.name}`,
4125
1781
  requestId,
@@ -4127,7 +1783,7 @@ async function notifyParent(watch, prompt, noteKind) {
4127
1783
  }) : null;
4128
1784
  if (queryId)
4129
1785
  return;
4130
- const { interSessionQueue } = await import("./chunk-t9zcg8f1.js");
1786
+ const { interSessionQueue } = await import("./chunk-efyxk97v.js");
4131
1787
  if (parent.agent && interSessionQueue.hasRequest(watch.parentTopicId, requestId)) {
4132
1788
  return;
4133
1789
  }
@@ -4216,13 +1872,13 @@ async function spawnSubagent(ctx, input) {
4216
1872
  }
4217
1873
  }
4218
1874
  if (modelOverride) {
4219
- const { getRegistry: getRegistry2 } = await import("./chunk-5ahkew9c.js");
1875
+ const { getRegistry: getRegistry2 } = await import("./chunk-nqdp52aj.js");
4220
1876
  const targetAgent = agentOverride ?? ctx.agent;
4221
1877
  if (!getRegistry2(targetAgent).validateModel(modelOverride)) {
4222
1878
  return errorResult(`Error: model '${modelOverride}' is not valid for agent '${targetAgent}'.`);
4223
1879
  }
4224
1880
  }
4225
- const { createDerivedTopic, TopicTitleConflictError } = await import("./chunk-ghs2j1fr.js");
1881
+ const { createDerivedTopic, TopicTitleConflictError } = await import("./chunk-h8vmb9gf.js");
4226
1882
  let child;
4227
1883
  try {
4228
1884
  child = await createDerivedTopic(ctx.topicId, ctx.userId, false, {
@@ -4276,8 +1932,8 @@ async function spawnSubagent(ctx, input) {
4276
1932
 
4277
1933
  ${task}`;
4278
1934
  {
4279
- const { triggerTopicAiTurn } = await import("./chunk-rbrxjvbj.js");
4280
- const { getRoomQuery: getRoomQuery2 } = await import("./chunk-t9zcg8f1.js");
1935
+ const { triggerTopicAiTurn } = await import("./chunk-nmv41qx9.js");
1936
+ const { getRoomQuery: getRoomQuery2 } = await import("./chunk-efyxk97v.js");
4281
1937
  childQueryId = triggerTopicAiTurn(child.id, ctx.userId, childPrompt, agentOverride, {
4282
1938
  origin: `subagent-task:${parent.title}`,
4283
1939
  requestId: `subagent-task-${child.id}`,
@@ -4366,7 +2022,7 @@ function createSubagentManagementToolDefinitions(ctx) {
4366
2022
  return errorResult("Error: no owned direct subagent with that topic_id exists under this room.");
4367
2023
  }
4368
2024
  try {
4369
- const { deleteTopicCascade } = await import("./chunk-sm6fh5c5.js");
2025
+ const { deleteTopicCascade } = await import("./chunk-g7pvfj5e.js");
4370
2026
  await deleteTopicCascade(child, ctx.userId);
4371
2027
  return textResult(`Subagent deleted: ${child.title} (${child.id})`);
4372
2028
  } catch (err) {
@@ -4393,6 +2049,6 @@ function createSpawnSubagentToolDefinition(ctx) {
4393
2049
  };
4394
2050
  }
4395
2051
 
4396
- export { killOwnedCodexTreesForShutdown, isSensitivePath, VAULT_VALUE_MIN_BYTES, VAULT_VALUE_MAX_BYTES, VAULT_DESCRIPTION_MAX_LENGTH, normalizeVaultKey, validateVaultKey, vaultListWithValues, vaultSet, vaultHasKey, vaultDel, vaultList, registerCanonicalMcpBridgeEnvProvider, RUNTIME_MCP_KEY, RUNTIME_MCP_BASE_PATH, setRuntimeMcpPort, getRuntimeMcpPort, issueRuntimeMcpToken, resolveRuntimeMcpToken, buildRuntimeMcpSpec, registerPeerSessionBridgeIpcConfig, buildStdioMcpServer, setPlaywrightUnavailableNotifier, markPlaywrightUnavailable, consumePlaywrightUnavailable, OPTIONAL_FORUM_MCP_SERVERS, registerRuntimeMcpServer, setNodeMcpServers, getNodeMcpServers, getMcpServersForQuery, extractFileEvents, extractFileTagPaths, stripFileTags, errMsg, NEGOTIUM_VERSION, runAgent, textResult, errorResult, buildTopicSystemPrompt, buildChannelSystemPrompt, buildManagerSystemPrompt, buildMemoryPromptSection, getTopicBrief, resolveTopicBrief, deleteTopicBrief, getSharedWikiDir, wikiSummaryFilename, listActiveMemoryArchiverSessions, runArchiverTurn, archiveTopicMessages, deleteTopicArchiveState, cancelIdleArchiveForTopic, scheduleIdleArchiveForTopic, ensureBgBash, killBgBash, killAllBgBash, writeQueryState, clearQueryState, fileHooks, setFileHooks, isUploadFileId, resolveAttachmentByFileId, resolveUploadedFilePathByFileId, storeLocalFileAsUpload, deleteFilesForTopic, nextUsageAlert, clearQueryUsageAlert, VISUAL_MEDIA_URL_PLACEHOLDER, normalizeVisualTitle, topicHasVisualFileId, storeTopicVisual, storeTopicMermaidVisual, storeTopicMediaVisual, getActiveVisualForPrompt, deleteTopicVisuals, topicVisualUrl, enqueueRuntimeUserTurnRequest, claimNextRuntimeUserTurnRequest, markRuntimeUserTurnRunning, releaseRuntimeUserTurnClaim, completeRuntimeUserTurnRequest, cancelRuntimeUserTurnRequestsBeforeEpoch, cancelRuntimeUserTurnRequests, getRuntimeUserTurnRequest, takeSubagentWatch, cancelSubagentWatchForDeletedTopic, settleSubagentSuccess, settleSubagentFailure, sweepStaleSubagentCards, createSubagentManagementToolDefinitions, createSpawnSubagentToolDefinition };
2052
+ export { runAgent, textResult, errorResult, buildTopicSystemPrompt, buildChannelSystemPrompt, buildManagerSystemPrompt, buildMemoryPromptSection, getTopicBrief, resolveTopicBrief, deleteTopicBrief, getSharedWikiDir, wikiSummaryFilename, listActiveMemoryArchiverSessions, runArchiverTurn, archiveTopicMessages, deleteTopicArchiveState, cancelIdleArchiveForTopic, scheduleIdleArchiveForTopic, ensureBgBash, killBgBash, killAllBgBash, writeQueryState, clearQueryState, fileHooks, setFileHooks, isUploadFileId, resolveAttachmentByFileId, resolveUploadedFilePathByFileId, storeLocalFileAsUpload, deleteFilesForTopic, nextUsageAlert, clearQueryUsageAlert, VISUAL_MEDIA_URL_PLACEHOLDER, normalizeVisualTitle, topicHasVisualFileId, storeTopicVisual, storeTopicMermaidVisual, storeTopicMediaVisual, getActiveVisualForPrompt, deleteTopicVisuals, topicVisualUrl, enqueueRuntimeUserTurnRequest, claimNextRuntimeUserTurnRequest, markRuntimeUserTurnRunning, releaseRuntimeUserTurnClaim, completeRuntimeUserTurnRequest, cancelRuntimeUserTurnRequestsBeforeEpoch, cancelRuntimeUserTurnRequests, getRuntimeUserTurnRequest, takeSubagentWatch, cancelSubagentWatchForDeletedTopic, settleSubagentSuccess, settleSubagentFailure, sweepStaleSubagentCards, createSubagentManagementToolDefinitions, createSpawnSubagentToolDefinition };
4397
2053
 
4398
- //# debugId=C2F7B4AA8BBA82E564756E2164756E21
2054
+ //# debugId=7320042BFA69CF4F64756E2164756E21