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
@@ -180,6 +180,8 @@ export interface AgentQueryOptions {
180
180
  wikiTopicId?: string;
181
181
  /** Whether self-config MCP may enqueue an automatic continue turn after set_* changes. */
182
182
  autoContinue?: boolean;
183
+ /** Expose Otium-only visual panel tools for this turn. Default-deny. */
184
+ visualTools?: boolean;
183
185
  abortController?: AbortController;
184
186
  model?: string;
185
187
  /** Provider-side hard budget when the selected SDK supports one. */
@@ -1 +1 @@
1
- export const NEGOTIUM_VERSION = "0.1.8";
1
+ export const NEGOTIUM_VERSION = "0.1.9";
package/package.json CHANGED
@@ -1,12 +1,24 @@
1
1
  {
2
2
  "name": "negotium",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "type": "module",
5
5
  "description": "Install the Negotium multi-agent runtime and CLI with one package",
6
6
  "license": "Apache-2.0",
7
7
  "bin": {
8
8
  "negotium": "dist/main.js"
9
9
  },
10
+ "exports": {
11
+ "./hosted-agent": {
12
+ "types": "./dist/runtime/src/agents/hosted-agent.ts",
13
+ "import": "./dist/hosted-agent.js",
14
+ "default": "./dist/hosted-agent.js"
15
+ },
16
+ "./canonical-mcp-bridge": {
17
+ "types": "./dist/runtime/src/mcp/canonical-bridge-config.ts",
18
+ "import": "./dist/canonical-mcp-bridge.js",
19
+ "default": "./dist/canonical-mcp-bridge.js"
20
+ }
21
+ },
10
22
  "files": [
11
23
  "dist",
12
24
  "README.md",
@@ -1,287 +0,0 @@
1
- // @bun
2
- import {
3
- DATA_DIR,
4
- logger
5
- } from "./chunk-k9z47mab.js";
6
- import"./chunk-hwy3n31w.js";
7
-
8
- // ../../packages/core/src/storage/conversations.ts
9
- import {
10
- existsSync,
11
- mkdirSync as mkdirSync2,
12
- readFileSync as readFileSync2,
13
- renameSync as renameSync2,
14
- unlinkSync as unlinkSync3,
15
- writeFileSync as writeFileSync2
16
- } from "fs";
17
- import { dirname as dirname2, join } from "path";
18
-
19
- // ../../packages/core/src/platform/jsonl.ts
20
- import {
21
- appendFileSync,
22
- closeSync,
23
- fsyncSync,
24
- mkdirSync,
25
- openSync,
26
- readFileSync,
27
- renameSync,
28
- statSync,
29
- unlinkSync as unlinkSync2,
30
- writeFileSync
31
- } from "fs";
32
- import { dirname } from "path";
33
-
34
- // ../../packages/core/src/platform/file-utils.ts
35
- import { unlinkSync } from "fs";
36
- function safeUnlink(path, warnLabel) {
37
- try {
38
- unlinkSync(path);
39
- } catch (e) {
40
- if (e?.code === "ENOENT")
41
- return;
42
- if (warnLabel)
43
- logger.warn({ err: e, path }, warnLabel);
44
- }
45
- }
46
-
47
- // ../../packages/core/src/platform/jsonl.ts
48
- function readJsonlLines(filePath) {
49
- return readFileSync(filePath, "utf-8").trim().split(`
50
- `).filter(Boolean);
51
- }
52
- function parseJsonlText(raw) {
53
- return raw.trim().split(`
54
- `).filter(Boolean).map((line) => JSON.parse(line));
55
- }
56
- var LOCK_SUFFIX = ".lock";
57
- var LOCK_RETRY_MS = 5;
58
- var LOCK_TIMEOUT_MS = 1500;
59
- var LOCK_STALE_MS = 5000;
60
- var LOCK_SLEEP = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
61
- function sleepForAppendLock(ms) {
62
- Atomics.wait(LOCK_SLEEP, 0, 0, ms);
63
- }
64
- function tryAcquireAppendLock(lockPath) {
65
- try {
66
- closeSync(openSync(lockPath, "wx"));
67
- return true;
68
- } catch (e) {
69
- if (e.code !== "EEXIST")
70
- throw e;
71
- return false;
72
- }
73
- }
74
- function isStaleLock(lockPath) {
75
- try {
76
- return Date.now() - statSync(lockPath).mtimeMs > LOCK_STALE_MS;
77
- } catch {
78
- return false;
79
- }
80
- }
81
- function appendJsonlEntry(filePath, entry) {
82
- appendJsonlLine(filePath, `${JSON.stringify(entry)}
83
- `);
84
- }
85
- function appendJsonlLine(filePath, line) {
86
- mkdirSync(dirname(filePath), { recursive: true });
87
- const lockPath = `${filePath}${LOCK_SUFFIX}`;
88
- const payload = line.endsWith(`
89
- `) ? line : `${line}
90
- `;
91
- let acquired = tryAcquireAppendLock(lockPath);
92
- if (!acquired && isStaleLock(lockPath)) {
93
- safeUnlink(lockPath);
94
- acquired = tryAcquireAppendLock(lockPath);
95
- }
96
- if (!acquired) {
97
- const start = Date.now();
98
- while (!acquired && Date.now() - start < LOCK_TIMEOUT_MS) {
99
- sleepForAppendLock(LOCK_RETRY_MS);
100
- acquired = tryAcquireAppendLock(lockPath);
101
- if (!acquired && isStaleLock(lockPath)) {
102
- safeUnlink(lockPath);
103
- acquired = tryAcquireAppendLock(lockPath);
104
- }
105
- }
106
- }
107
- if (!acquired) {
108
- appendFileSync(filePath, payload);
109
- return;
110
- }
111
- try {
112
- appendFileSync(filePath, payload);
113
- } finally {
114
- safeUnlink(lockPath);
115
- }
116
- }
117
- function writeJsonlFile(filePath, entries) {
118
- const dir = dirname(filePath);
119
- mkdirSync(dir, { recursive: true });
120
- const tmpPath = `${filePath}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
121
- const payload = `${entries.map((e) => JSON.stringify(e)).join(`
122
- `)}
123
- `;
124
- let fd = null;
125
- try {
126
- fd = openSync(tmpPath, "w");
127
- writeFileSync(fd, payload);
128
- fsyncSync(fd);
129
- closeSync(fd);
130
- fd = null;
131
- renameSync(tmpPath, filePath);
132
- fsyncDirectoryBestEffort(dir);
133
- } catch (err) {
134
- if (fd !== null) {
135
- try {
136
- closeSync(fd);
137
- } catch {}
138
- }
139
- try {
140
- unlinkSync2(tmpPath);
141
- } catch {}
142
- throw err;
143
- }
144
- }
145
- function fsyncDirectoryBestEffort(dir) {
146
- let fd = null;
147
- try {
148
- fd = openSync(dir, "r");
149
- fsyncSync(fd);
150
- } catch {} finally {
151
- if (fd !== null) {
152
- try {
153
- closeSync(fd);
154
- } catch {}
155
- }
156
- }
157
- }
158
-
159
- // ../../packages/core/src/security/sanitize.ts
160
- function sanitizeTopicName(name, lowercase = false) {
161
- const safe = name.replace(/[^a-zA-Z0-9\uAC00-\uD7A3_-]/g, "_") || "_";
162
- return lowercase ? safe.toLowerCase() : safe;
163
- }
164
- function sanitizeFileName(name) {
165
- const safe = name.replace(/[^a-zA-Z0-9._-]/g, "_") || "_";
166
- if (safe === "." || safe === "..")
167
- return "_";
168
- return safe;
169
- }
170
-
171
- // ../../packages/core/src/storage/conversations.ts
172
- function safeUserIdComponent(userId) {
173
- const str = String(userId);
174
- if (!str || /[/\\]|\.\./.test(str)) {
175
- throw new Error(`conversations: refusing unsafe userId path component: ${str}`);
176
- }
177
- return str;
178
- }
179
- function conversationDir(userId) {
180
- return join(DATA_DIR, "conversations", safeUserIdComponent(userId));
181
- }
182
- function topicFilename(topicName) {
183
- const t = sanitizeTopicName(topicName, true);
184
- return `${t}.jsonl`;
185
- }
186
- function getConversationPath(userId, topicName) {
187
- return join(conversationDir(userId), topicFilename(topicName));
188
- }
189
- function appendConversationEvent(userId, topicName, agent, event) {
190
- try {
191
- appendConversationEventStrict(userId, topicName, agent, event);
192
- } catch (err) {
193
- logger.warn({ err, userId, topicName, eventType: event.type }, "appendConversationEvent: write failed");
194
- }
195
- }
196
- function appendConversationEventStrict(userId, topicName, agent, event) {
197
- const path = getConversationPath(userId, topicName);
198
- const entry = {
199
- ts: new Date().toISOString(),
200
- agent,
201
- event
202
- };
203
- mkdirSync2(dirname2(path), { recursive: true });
204
- appendJsonlLine(path, JSON.stringify(entry));
205
- }
206
- function readConversation(userId, topicName) {
207
- const path = getConversationPath(userId, topicName);
208
- const out = [];
209
- if (!existsSync(path))
210
- return out;
211
- let raw;
212
- try {
213
- raw = readFileSync2(path, "utf8");
214
- } catch (err) {
215
- logger.warn({ err, path }, "readConversation: read failed");
216
- return out;
217
- }
218
- for (const line of raw.split(`
219
- `)) {
220
- if (!line.trim())
221
- continue;
222
- try {
223
- out.push(JSON.parse(line));
224
- } catch (err) {
225
- logger.warn({ err, line: line.slice(0, 200) }, "readConversation: malformed JSONL line skipped");
226
- }
227
- }
228
- return out;
229
- }
230
- function replaceConversationStrict(userId, topicName, entries) {
231
- const path = getConversationPath(userId, topicName);
232
- const tempPath = `${path}.${process.pid}.${Date.now()}.tmp`;
233
- mkdirSync2(dirname2(path), { recursive: true });
234
- try {
235
- writeFileSync2(tempPath, entries.length > 0 ? `${entries.map((entry) => JSON.stringify(entry)).join(`
236
- `)}
237
- ` : "", { flag: "wx" });
238
- renameSync2(tempPath, path);
239
- } catch (error) {
240
- try {
241
- unlinkSync3(tempPath);
242
- } catch {}
243
- throw error;
244
- }
245
- }
246
- function cloneConversationLog(opts) {
247
- const { userId, srcTopic, dstTopic } = opts;
248
- const dstPath = getConversationPath(userId, dstTopic);
249
- if (existsSync(dstPath) && readFileSync2(dstPath, "utf8").trim().length > 0) {
250
- logger.warn({ userId, srcTopic, dstTopic, dstPath }, "cloneConversationLog: dst already non-empty \u2014 refusing to overwrite");
251
- return { copied: false, entries: 0 };
252
- }
253
- const entries = readConversation(userId, srcTopic);
254
- if (entries.length === 0) {
255
- return { copied: false, entries: 0 };
256
- }
257
- const body = `${entries.map((e) => JSON.stringify(e)).join(`
258
- `)}
259
- `;
260
- mkdirSync2(dirname2(dstPath), { recursive: true });
261
- writeFileSync2(dstPath, body);
262
- return { copied: true, entries: entries.length };
263
- }
264
- function findLastSessionIdForAgent(entries, agent) {
265
- for (let i = entries.length - 1;i >= 0; i--) {
266
- const entry = entries[i];
267
- if (entry.agent !== agent)
268
- continue;
269
- if (entry.event.type === "session") {
270
- return entry.event.sessionId;
271
- }
272
- }
273
- return null;
274
- }
275
- export {
276
- replaceConversationStrict,
277
- readConversation,
278
- getConversationPath,
279
- findLastSessionIdForAgent,
280
- cloneConversationLog,
281
- appendConversationEventStrict,
282
- appendConversationEvent
283
- };
284
-
285
- export { readJsonlLines, parseJsonlText, appendJsonlEntry, writeJsonlFile, sanitizeTopicName, sanitizeFileName, getConversationPath, appendConversationEvent, appendConversationEventStrict, readConversation, replaceConversationStrict, cloneConversationLog, findLastSessionIdForAgent };
286
-
287
- //# debugId=6EF8231950893DB664756E2164756E21
@@ -1,13 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../packages/core/src/storage/conversations.ts", "../../../packages/core/src/platform/jsonl.ts", "../../../packages/core/src/platform/file-utils.ts", "../../../packages/core/src/security/sanitize.ts"],
4
- "sourcesContent": [
5
- "import {\n existsSync,\n mkdirSync,\n readFileSync,\n renameSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { DATA_DIR } from \"#platform/config\";\nimport { appendJsonlLine } from \"#platform/jsonl\";\nimport { logger } from \"#platform/logger\";\nimport { sanitizeTopicName } from \"#security/sanitize\";\nimport type { AgentKind, UnifiedEvent } from \"#types\";\n\n/**\n * Per-topic conversation log (UnifiedEvent stream) used as the\n * **provider-agnostic source of truth** for cross-agent portability.\n *\n * Storage layout:\n * {DATA_DIR}/conversations/{userId}/{sanitizedTopicName}.jsonl\n *\n * Each line is a `ConversationEntry` JSON object. Append-only: every yielded\n * UnifiedEvent during a `runAgent()` turn is captured here by the recording\n * wrapper, so on agent switch we can rebuild a synthetic native rollout for\n * the target SDK and preserve the conversation across providers.\n *\n * The Claude/Codex SDK rollouts (~/.claude/projects/, ~/.codex/sessions/) are\n * intentionally treated as opaque side-effects of the SDKs only this file\n * is canonical.\n */\nexport interface ConversationEntry {\n ts: string;\n /**\n * Agent that produced this event, frozen at write time. A topic that has\n * been switched (via `set_agent`) will have a mixed-agent log: earlier\n * entries keep the agent that originally generated them. Replay code\n * (rollout-codec `extractChatPairs`) intentionally ignores this field —\n * the cross-agent rollout's whole point is to feed past dialogue, no\n * matter who produced it, into the *new* SDK as if it were native.\n */\n agent: AgentKind;\n event: UnifiedEvent;\n}\n\n/**\n * Validate that a userId stringifies to a safe path component. Rejects empties,\n * slashes, and traversal sequences before they touch the filesystem. The\n * conversation log is local-only so we keep this strict but simple if a\n * caller sends something unexpected it is their bug to fix, not ours to coerce.\n */\nfunction safeUserIdComponent(userId: number | string): string {\n const str = String(userId);\n if (!str || /[/\\\\]|\\.\\./.test(str)) {\n throw new Error(`conversations: refusing unsafe userId path component: ${str}`);\n }\n return str;\n}\n\nfunction conversationDir(userId: number | string): string {\n return join(DATA_DIR, \"conversations\", safeUserIdComponent(userId));\n}\n\nfunction topicFilename(topicName: string): string {\n const t = sanitizeTopicName(topicName, true);\n return `${t}.jsonl`;\n}\n\n/** Compute the absolute path for a given user/topic conversation log. */\nexport function getConversationPath(userId: number | string, topicName: string): string {\n return join(conversationDir(userId), topicFilename(topicName));\n}\n\n/**\n * Append a single UnifiedEvent for the given topic. Creates the parent\n * directory and the file as needed. Best-effort: I/O failures are logged but\n * never throw, since recording must not break the live stream to Telegram.\n *\n * **Concurrency note (review item M2, revised):** within the bot process the\n * single-threaded event loop already serializes writes — but this module is\n * NOT single-process: the self-config MCP server (a separate stdio process,\n * via `topic-agent-switch`) and provider bridge helpers append to the same\n * topic logs.\n * Cross-process interleaving on macOS is real for lines beyond PIPE_BUF\n * (512B), and a torn line is silently dropped by `readConversation` —\n * corrupting the canonical source for cross-agent rollout reconstruction.\n * Writes therefore go through `appendJsonlLine` (sidecar `.lock` via O_EXCL,\n * stale-lock reclaim, unlocked-append fallback on timeout — interleave is\n * accepted over dropping the entry).\n *\n * The append stays SYNCHRONOUS on purpose: a previous attempt at a\n * Promise-chained per-topic queue made writes async (durability gap before a\n * synchronous `readConversation`) and broke `set_agent` in the bridge tests.\n */\nexport function appendConversationEvent(\n userId: number | string,\n topicName: string,\n agent: AgentKind,\n event: UnifiedEvent,\n): void {\n try {\n appendConversationEventStrict(userId, topicName, agent, event);\n } catch (err) {\n logger.warn(\n { err, userId, topicName, eventType: event.type },\n \"appendConversationEvent: write failed\",\n );\n }\n}\n\n/**\n * Strict variant for state transitions where the conversation log is a manifest\n * rather than telemetry. Throws on I/O failure so callers can avoid committing\n * DB state that points at an unmanifested SDK session.\n */\nexport function appendConversationEventStrict(\n userId: number | string,\n topicName: string,\n agent: AgentKind,\n event: UnifiedEvent,\n): void {\n const path = getConversationPath(userId, topicName);\n const entry: ConversationEntry = {\n ts: new Date().toISOString(),\n agent,\n event,\n };\n mkdirSync(dirname(path), { recursive: true });\n appendJsonlLine(path, JSON.stringify(entry));\n}\n\n/**\n * Read all entries for a topic in chronological order. Returns an empty array\n * if the file does not exist. Malformed lines are skipped with a warning so a\n * single corrupted entry does not poison the whole conversation.\n *\n * NOTE(perf): reads the whole file each call. Topics in the kilobyte range\n * are fine; if a single topic ever grows into multi-megabyte territory,\n * consider a streaming reader (`readline`/`Bun.file().stream()`) and a\n * size-bounded tail.\n */\nexport function readConversation(userId: number | string, topicName: string): ConversationEntry[] {\n const path = getConversationPath(userId, topicName);\n const out: ConversationEntry[] = [];\n if (!existsSync(path)) return out;\n let raw: string;\n try {\n raw = readFileSync(path, \"utf8\");\n } catch (err) {\n logger.warn({ err, path }, \"readConversation: read failed\");\n return out;\n }\n for (const line of raw.split(\"\\n\")) {\n if (!line.trim()) continue;\n try {\n out.push(JSON.parse(line) as ConversationEntry);\n } catch (err) {\n logger.warn(\n { err, line: line.slice(0, 200) },\n \"readConversation: malformed JSONL line skipped\",\n );\n }\n }\n return out;\n}\n\n/** Atomically replace a provider-neutral topic log with an explicit event set. */\nexport function replaceConversationStrict(\n userId: number | string,\n topicName: string,\n entries: ConversationEntry[],\n): void {\n const path = getConversationPath(userId, topicName);\n const tempPath = `${path}.${process.pid}.${Date.now()}.tmp`;\n mkdirSync(dirname(path), { recursive: true });\n try {\n writeFileSync(\n tempPath,\n entries.length > 0 ? `${entries.map((entry) => JSON.stringify(entry)).join(\"\\n\")}\\n` : \"\",\n { flag: \"wx\" },\n );\n renameSync(tempPath, path);\n } catch (error) {\n try {\n unlinkSync(tempPath);\n } catch {}\n throw error;\n }\n}\n\n/**\n * Copy the unified conversation log of `srcTopic` into the file path that\n * `dstTopic` will read from. Used by `/fork` so a forked child topic inherits\n * the parent's full cross-agent history, not just the agent's native SDK\n * rollout. `/spawn` intentionally does not call this; it starts with no\n * conversation history. Without this copy, a fork followed immediately by\n * `/agent <other>` would feed the empty child log to `switchTopicAgent` and\n * the new agent would start from zero — see the bug report from 2026-05-24.\n *\n * Semantics:\n * - Reads the parent via `readConversation`, matching the history an in-place\n * `set_agent` on the parent would have seen.\n * - Writes a single file at the child's canonical path.\n * - Refuses to overwrite a non-empty destination (returns `{copied:false}`).\n * `/fork`'s only caller runs this immediately after topic creation when\n * the dst file is guaranteed empty, so a non-empty dst means a programmer\n * error somewhere upstream — fail loud rather than silently merging.\n * - On any I/O error: throws. The caller (`createChildTopic`) already has\n * a rollback path that wraps this call.\n */\nexport function cloneConversationLog(opts: {\n userId: number | string;\n srcTopic: string;\n dstTopic: string;\n}): { copied: boolean; entries: number } {\n const { userId, srcTopic, dstTopic } = opts;\n const dstPath = getConversationPath(userId, dstTopic);\n if (existsSync(dstPath) && readFileSync(dstPath, \"utf8\").trim().length > 0) {\n logger.warn(\n { userId, srcTopic, dstTopic, dstPath },\n \"cloneConversationLog: dst already non-empty — refusing to overwrite\",\n );\n return { copied: false, entries: 0 };\n }\n const entries = readConversation(userId, srcTopic);\n if (entries.length === 0) {\n return { copied: false, entries: 0 };\n }\n const body = `${entries.map((e) => JSON.stringify(e)).join(\"\\n\")}\\n`;\n mkdirSync(dirname(dstPath), { recursive: true });\n writeFileSync(dstPath, body);\n return { copied: true, entries: entries.length };\n}\n\n/**\n * Walk the unified log backwards and return the most recent SDK-emitted\n * session id for the given agent — or null if that agent has never run on\n * this topic.\n *\n * Used by `set_agent` to round-trip the SAME native rollout file across\n * agent switches: when the user does claude → codex → claude, we reuse\n * the original claude sessionId so the synthetic rollout lands at the\n * same path the SDK already manages, preserving prompt-cache continuity\n * and avoiding orphan `~/.claude/projects/<dir>/<id>.jsonl` files.\n *\n * Behavior:\n * - First-ever switch to an agent (no prior session events) → null,\n * caller falls back to a fresh randomUUID/uuidv7. Same as before.\n * - Roundtrip switch (prior session exists) → that sessionId.\n *\n * `session` events are emitted on every turn by claude-/codex-provider and\n * captured into the unified log by runAgent's append wrapper. The most\n * recent one for the target agent is by definition the current SDK-side\n * resume key for that agent.\n */\nexport function findLastSessionIdForAgent(\n entries: ConversationEntry[],\n agent: AgentKind,\n): string | null {\n for (let i = entries.length - 1; i >= 0; i--) {\n const entry = entries[i];\n if (entry.agent !== agent) continue;\n if (entry.event.type === \"session\") {\n return entry.event.sessionId;\n }\n }\n return null;\n}\n",
6
- "import {\n appendFileSync,\n closeSync,\n fsyncSync,\n mkdirSync,\n openSync,\n readFileSync,\n renameSync,\n statSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { safeUnlink } from \"#platform/file-utils\";\n\nexport function readJsonlLines(filePath: string): string[] {\n return readFileSync(filePath, \"utf-8\").trim().split(\"\\n\").filter(Boolean);\n}\n\n/**\n * Parse a JSONL string (one JSON value per line) into an array of `T`.\n * Throws on malformed lines — use when a corrupt fixture/log should fail\n * loudly rather than silently drop entries (paired with `readJsonlLines`'\n * resilient counterpart for live data streams).\n */\nexport function parseJsonlText<T = unknown>(raw: string): T[] {\n return raw\n .trim()\n .split(\"\\n\")\n .filter(Boolean)\n .map((line) => JSON.parse(line) as T);\n}\n\n/**\n * Read and JSON-parse a file. Returns null if the file is missing or contains\n * invalid JSON. Use when a missing/corrupt file should fall through to a\n * default rather than throw.\n */\nexport function readJsonFile<T = unknown>(filePath: string): T | null {\n try {\n return JSON.parse(readFileSync(filePath, \"utf-8\")) as T;\n } catch {\n return null;\n }\n}\n\n// --- Cross-process append lock ---\n// POSIX `O_APPEND` is only atomic when the write payload fits in PIPE_BUF\n// (Linux 4096, macOS 512). MCP servers and the bot run as separate\n// processes that all `appendFileSync` to shared inbox/outbox files; a long\n// `tell_session` prompt or large `ask_session` reply is comfortably above\n// PIPE_BUF, and a concurrent append from another process can interleave at\n// the byte level — the result fails JSON parse and the entire entry is\n// silently dropped by `parseOutboxLine`.\n//\n// Mitigation: a sidecar `<filePath>.lock` created with `O_EXCL` serializes\n// every append at the OS level. Lock is held for the duration of one\n// `appendFileSync`, so the contention window is microseconds. Stale locks\n// (process crashed mid-write) are detected via mtime and forcibly removed\n// after `LOCK_STALE_MS`. As a last resort, after `LOCK_TIMEOUT_MS` of\n// retries, the lock is bypassed — the entry survives but may interleave;\n// preferable to dropping it entirely.\nconst LOCK_SUFFIX = \".lock\";\nconst LOCK_RETRY_MS = 5;\nconst LOCK_TIMEOUT_MS = 1500;\nconst LOCK_STALE_MS = 5000;\nconst LOCK_SLEEP = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));\n\n/** Synchronous, non-spinning sleep supported by both Bun and Node MCP workers. */\nfunction sleepForAppendLock(ms: number): void {\n Atomics.wait(LOCK_SLEEP, 0, 0, ms);\n}\n\nfunction tryAcquireAppendLock(lockPath: string): boolean {\n try {\n closeSync(openSync(lockPath, \"wx\")); // O_CREAT | O_EXCL — atomic\n return true;\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code !== \"EEXIST\") throw e;\n return false;\n }\n}\n\nfunction isStaleLock(lockPath: string): boolean {\n try {\n return Date.now() - statSync(lockPath).mtimeMs > LOCK_STALE_MS;\n } catch {\n return false;\n }\n}\n\n/**\n * Append a JSON-encoded entry + newline to a JSONL file. Ensures the parent\n * directory exists and serializes concurrent multi-process appends through a\n * sidecar lock so payloads larger than PIPE_BUF do not interleave.\n *\n * The drainer side (`outbox/file-ops.ts`) claims the file via atomic rename\n * and handles recovery of leftover `.processing` files from crashes.\n */\nexport function appendJsonlEntry(filePath: string, entry: unknown): void {\n appendJsonlLine(filePath, `${JSON.stringify(entry)}\\n`);\n}\n\n/**\n * Append an already-serialized JSONL line (must end with \"\\n\") under the\n * same cross-process append lock as `appendJsonlEntry`. Use when you\n * want to write back a raw line **exactly** — e.g. flusher tail-writeback\n * preserving lines that current parser can't decode, so a future parser\n * release still sees them.\n */\nexport function appendJsonlLine(filePath: string, line: string): void {\n mkdirSync(dirname(filePath), { recursive: true });\n const lockPath = `${filePath}${LOCK_SUFFIX}`;\n const payload = line.endsWith(\"\\n\") ? line : `${line}\\n`;\n\n let acquired = tryAcquireAppendLock(lockPath);\n // Pre-check: if the existing lock is already stale (writer crashed before\n // unlinking), reclaim it before sleeping. Saves the entire retry budget\n // for the common stale-leftover case.\n if (!acquired && isStaleLock(lockPath)) {\n safeUnlink(lockPath);\n acquired = tryAcquireAppendLock(lockPath);\n }\n if (!acquired) {\n const start = Date.now();\n while (!acquired && Date.now() - start < LOCK_TIMEOUT_MS) {\n // Codex launches built-in stdio MCPs under Node+tsx while the runtime\n // itself uses Bun. Atomics.wait gives both processes a blocking sleep\n // without a CPU-spinning fallback or a Bun-only global.\n sleepForAppendLock(LOCK_RETRY_MS);\n acquired = tryAcquireAppendLock(lockPath);\n // Recheck staleness inside the loop so a writer that crashes mid-wait\n // doesn't leave us spinning the whole timeout.\n if (!acquired && isStaleLock(lockPath)) {\n safeUnlink(lockPath);\n acquired = tryAcquireAppendLock(lockPath);\n }\n }\n }\n\n if (!acquired) {\n // Best-effort fallback: append unlocked. Worst case the line interleaves\n // with another writer and one of them is dropped at parse time — still\n // better than dropping the entry on the floor here.\n appendFileSync(filePath, payload);\n return;\n }\n\n try {\n appendFileSync(filePath, payload);\n } finally {\n safeUnlink(lockPath);\n }\n}\n\n/** Overwrite a JSONL file with the given entries. Trailing newline included. */\nexport function writeJsonlFile(filePath: string, entries: readonly unknown[]): void {\n const dir = dirname(filePath);\n mkdirSync(dir, { recursive: true });\n const tmpPath = `${filePath}.tmp-${process.pid}-${Date.now()}-${Math.random()\n .toString(16)\n .slice(2)}`;\n const payload = `${entries.map((e) => JSON.stringify(e)).join(\"\\n\")}\\n`;\n let fd: number | null = null;\n try {\n fd = openSync(tmpPath, \"w\");\n writeFileSync(fd, payload);\n fsyncSync(fd);\n closeSync(fd);\n fd = null;\n renameSync(tmpPath, filePath);\n fsyncDirectoryBestEffort(dir);\n } catch (err) {\n if (fd !== null) {\n try {\n closeSync(fd);\n } catch {}\n }\n try {\n unlinkSync(tmpPath);\n } catch {}\n throw err;\n }\n}\n\nfunction fsyncDirectoryBestEffort(dir: string): void {\n let fd: number | null = null;\n try {\n fd = openSync(dir, \"r\");\n fsyncSync(fd);\n } catch {\n // Directory fsync is not portable across every runtime/filesystem. The\n // file fsync + rename above is the important integrity boundary; this is\n // best-effort durability for the directory entry.\n } finally {\n if (fd !== null) {\n try {\n closeSync(fd);\n } catch {}\n }\n }\n}\n",
7
- "import { unlinkSync } from \"node:fs\";\nimport { logger } from \"#platform/logger\";\n\n/**\n * Best-effort unlink. ENOENT is always swallowed (the file is already gone).\n * Other errors are silent unless `warnLabel` is provided, in which case they\n * are logged at warn level with `{ err, path }` context.\n */\nexport function safeUnlink(path: string, warnLabel?: string): void {\n try {\n unlinkSync(path);\n } catch (e) {\n if ((e as NodeJS.ErrnoException)?.code === \"ENOENT\") return;\n if (warnLabel) logger.warn({ err: e, path }, warnLabel);\n }\n}\n",
8
- "/** Sanitize a topic/session name for use in file paths.\n * Replaces non-alphanumeric characters (excluding Korean) with underscores.\n * Empty input (or input consisting only of disallowed chars) returns \"_\" so\n * callers always get a non-empty single path component.\n * @param name The raw topic or session name.\n * @param lowercase Whether to lowercase the result (default: false).\n * Use true for wiki paths; leave false for log file paths.\n */\nexport function sanitizeTopicName(name: string, lowercase = false): string {\n const safe = name.replace(/[^a-zA-Z0-9가-힣_-]/g, \"_\") || \"_\";\n return lowercase ? safe.toLowerCase() : safe;\n}\n\n/** Sanitize a file name for use in file paths. Allows dots.\n * Pure-dot results (\".\", \"..\") are replaced with \"_\" to block path-traversal\n * when the sanitized value is used as a standalone path component.\n */\nexport function sanitizeFileName(name: string): string {\n const safe = name.replace(/[^a-zA-Z0-9._-]/g, \"_\") || \"_\";\n if (safe === \".\" || safe === \"..\") return \"_\";\n return safe;\n}\n\n/** Sanitize an arbitrary ID (no Korean, no dots) for use in file paths. */\nexport function sanitizeId(id: string): string {\n return id.replace(/[^a-zA-Z0-9_-]/g, \"_\") || \"_\";\n}\n"
9
- ],
10
- "mappings": "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",
11
- "debugId": "6EF8231950893DB664756E2164756E21",
12
- "names": []
13
- }