negotium 0.1.7 → 0.1.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (256) hide show
  1. package/README.md +2 -2
  2. package/dist/chunk-0ay6cjdy.js +1331 -0
  3. package/dist/chunk-0ay6cjdy.js.map +16 -0
  4. package/dist/chunk-1k7t7bnx.js +97 -0
  5. package/dist/chunk-1k7t7bnx.js.map +10 -0
  6. package/dist/chunk-5ahkew9c.js +806 -0
  7. package/dist/chunk-5ahkew9c.js.map +17 -0
  8. package/dist/chunk-6cpn7ht2.js +287 -0
  9. package/dist/chunk-6cpn7ht2.js.map +13 -0
  10. package/dist/chunk-6kctn20e.js +3529 -0
  11. package/dist/chunk-6kctn20e.js.map +20 -0
  12. package/dist/chunk-7xqj7aex.js +77 -0
  13. package/dist/chunk-7xqj7aex.js.map +11 -0
  14. package/dist/chunk-9p0ww3tk.js +41 -0
  15. package/dist/chunk-9p0ww3tk.js.map +10 -0
  16. package/dist/chunk-a92a7nrk.js +252 -0
  17. package/dist/chunk-a92a7nrk.js.map +10 -0
  18. package/dist/chunk-c500xe9z.js +33 -0
  19. package/dist/chunk-c500xe9z.js.map +10 -0
  20. package/dist/chunk-cds1qxsf.js +1644 -0
  21. package/dist/chunk-cds1qxsf.js.map +17 -0
  22. package/dist/chunk-e4407g8x.js +274 -0
  23. package/dist/chunk-e4407g8x.js.map +10 -0
  24. package/dist/chunk-e5pbggsh.js +363 -0
  25. package/dist/chunk-e5pbggsh.js.map +11 -0
  26. package/dist/chunk-edb07sjp.js +567 -0
  27. package/dist/chunk-edb07sjp.js.map +14 -0
  28. package/dist/chunk-exd0v74a.js +3012 -0
  29. package/dist/chunk-exd0v74a.js.map +32 -0
  30. package/dist/chunk-fgrme9tr.js +4398 -0
  31. package/dist/chunk-fgrme9tr.js.map +47 -0
  32. package/dist/chunk-fka9eqe1.js +581 -0
  33. package/dist/chunk-fka9eqe1.js.map +11 -0
  34. package/dist/chunk-g82jygez.js +108 -0
  35. package/dist/chunk-g82jygez.js.map +10 -0
  36. package/dist/chunk-ghs2j1fr.js +312 -0
  37. package/dist/chunk-ghs2j1fr.js.map +10 -0
  38. package/dist/chunk-gmy11efb.js +125 -0
  39. package/dist/chunk-gmy11efb.js.map +10 -0
  40. package/dist/chunk-gxma4c70.js +110 -0
  41. package/dist/chunk-gxma4c70.js.map +10 -0
  42. package/dist/chunk-hkdqhm34.js +61 -0
  43. package/dist/chunk-hkdqhm34.js.map +10 -0
  44. package/dist/chunk-hpa0cpzh.js +134 -0
  45. package/dist/chunk-hpa0cpzh.js.map +10 -0
  46. package/dist/chunk-hwy3n31w.js +21 -0
  47. package/dist/chunk-hwy3n31w.js.map +9 -0
  48. package/dist/chunk-j2behpy7.js +214 -0
  49. package/dist/chunk-j2behpy7.js.map +10 -0
  50. package/dist/chunk-jc0at0tc.js +102 -0
  51. package/dist/chunk-jc0at0tc.js.map +10 -0
  52. package/dist/chunk-jkqq5ef6.js +243 -0
  53. package/dist/chunk-jkqq5ef6.js.map +11 -0
  54. package/dist/chunk-jzkw2f01.js +51 -0
  55. package/dist/chunk-jzkw2f01.js.map +10 -0
  56. package/dist/chunk-jzz91jwh.js +70 -0
  57. package/dist/chunk-jzz91jwh.js.map +10 -0
  58. package/dist/chunk-k9z47mab.js +247 -0
  59. package/dist/chunk-k9z47mab.js.map +12 -0
  60. package/dist/chunk-n17yxpa8.js +23 -0
  61. package/dist/chunk-n17yxpa8.js.map +10 -0
  62. package/dist/chunk-naa09ags.js +2143 -0
  63. package/dist/chunk-naa09ags.js.map +15 -0
  64. package/dist/chunk-ns3ke88n.js +142 -0
  65. package/dist/chunk-ns3ke88n.js.map +10 -0
  66. package/dist/chunk-p3cr2ejg.js +1626 -0
  67. package/dist/chunk-p3cr2ejg.js.map +15 -0
  68. package/dist/chunk-q5bcpf23.js +2925 -0
  69. package/dist/chunk-q5bcpf23.js.map +20 -0
  70. package/dist/chunk-rafc55bs.js +424 -0
  71. package/dist/chunk-rafc55bs.js.map +11 -0
  72. package/dist/chunk-rbrxjvbj.js +2830 -0
  73. package/dist/chunk-rbrxjvbj.js.map +23 -0
  74. package/dist/chunk-sa874j5c.js +9 -0
  75. package/dist/chunk-sa874j5c.js.map +10 -0
  76. package/dist/chunk-sgsz2fpq.js +289 -0
  77. package/dist/chunk-sgsz2fpq.js.map +10 -0
  78. package/dist/chunk-sm6fh5c5.js +569 -0
  79. package/dist/chunk-sm6fh5c5.js.map +14 -0
  80. package/dist/chunk-t9zcg8f1.js +499 -0
  81. package/dist/chunk-t9zcg8f1.js.map +13 -0
  82. package/dist/chunk-vp2db42d.js +121 -0
  83. package/dist/chunk-vp2db42d.js.map +10 -0
  84. package/dist/chunk-w13jjf4a.js +46 -0
  85. package/dist/chunk-w13jjf4a.js.map +10 -0
  86. package/dist/chunk-x1arr7ze.js +63 -0
  87. package/dist/chunk-x1arr7ze.js.map +10 -0
  88. package/dist/chunk-xfq68rch.js +583 -0
  89. package/dist/chunk-xfq68rch.js.map +11 -0
  90. package/dist/main.js +132 -20
  91. package/dist/main.js.map +4 -4
  92. package/dist/runtime/scripts/faster-whisper-wrapper.py +87 -0
  93. package/dist/runtime/scripts/mcp-patchright-http.mjs +349 -0
  94. package/dist/runtime/src/agents/api-topic-agent-switch.ts +178 -0
  95. package/dist/runtime/src/agents/archiver.ts +396 -0
  96. package/dist/runtime/src/agents/auth-check.ts +64 -0
  97. package/dist/runtime/src/agents/claude-provider.ts +737 -0
  98. package/dist/runtime/src/agents/claude-registry.ts +115 -0
  99. package/dist/runtime/src/agents/codex-native-multi-agent.ts +177 -0
  100. package/dist/runtime/src/agents/codex-provider.ts +785 -0
  101. package/dist/runtime/src/agents/codex-registry.ts +115 -0
  102. package/dist/runtime/src/agents/codex-tree-kill.ts +365 -0
  103. package/dist/runtime/src/agents/contracts.ts +69 -0
  104. package/dist/runtime/src/agents/deep-map.ts +13 -0
  105. package/dist/runtime/src/agents/execution-host.ts +91 -0
  106. package/dist/runtime/src/agents/fixtures/claude-attachments.jsonl +2 -0
  107. package/dist/runtime/src/agents/fixtures/codex-shell.jsonl +5 -0
  108. package/dist/runtime/src/agents/fork.ts +65 -0
  109. package/dist/runtime/src/agents/hosted-agent.ts +48 -0
  110. package/dist/runtime/src/agents/idle-archiver.ts +128 -0
  111. package/dist/runtime/src/agents/index.ts +205 -0
  112. package/dist/runtime/src/agents/maestro-provider.ts +206 -0
  113. package/dist/runtime/src/agents/maestro-registry.ts +31 -0
  114. package/dist/runtime/src/agents/mcp-tools/ask-user.ts +280 -0
  115. package/dist/runtime/src/agents/mcp-tools/common.ts +21 -0
  116. package/dist/runtime/src/agents/mcp-tools/self-config.ts +220 -0
  117. package/dist/runtime/src/agents/mcp-tools/spawn-subagent.ts +525 -0
  118. package/dist/runtime/src/agents/mcp-tools/visuals.ts +88 -0
  119. package/dist/runtime/src/agents/model-catalog.ts +151 -0
  120. package/dist/runtime/src/agents/registry.ts +24 -0
  121. package/dist/runtime/src/agents/rollout/claude.ts +331 -0
  122. package/dist/runtime/src/agents/rollout/codex.ts +569 -0
  123. package/dist/runtime/src/agents/rollout/shared.ts +205 -0
  124. package/dist/runtime/src/agents/self-config-core.ts +415 -0
  125. package/dist/runtime/src/agents/task-events.ts +44 -0
  126. package/dist/runtime/src/agents/tool-format.ts +352 -0
  127. package/dist/runtime/src/agents/topic-agent-switch.ts +193 -0
  128. package/dist/runtime/src/agents/topic-cleanup.ts +231 -0
  129. package/dist/runtime/src/agents/vault-tool-policy.ts +33 -0
  130. package/dist/runtime/src/application/execute-external-user-turn.ts +23 -0
  131. package/dist/runtime/src/application/submit-user-message.ts +47 -0
  132. package/dist/runtime/src/application/switch-topic-effort.ts +48 -0
  133. package/dist/runtime/src/application/switch-topic-model.ts +79 -0
  134. package/dist/runtime/src/application/topic-service.ts +139 -0
  135. package/dist/runtime/src/application/vault-command.ts +170 -0
  136. package/dist/runtime/src/bus.ts +319 -0
  137. package/dist/runtime/src/index.ts +333 -0
  138. package/dist/runtime/src/mcp/agent-health-server.ts +309 -0
  139. package/dist/runtime/src/mcp/background-bash-server.ts +346 -0
  140. package/dist/runtime/src/mcp/canonical-bridge-config.ts +84 -0
  141. package/dist/runtime/src/mcp/canonical-proxy-server.ts +171 -0
  142. package/dist/runtime/src/mcp/dm-ipc.ts +60 -0
  143. package/dist/runtime/src/mcp/mcp-helpers.ts +40 -0
  144. package/dist/runtime/src/mcp/paddleocr-server.ts +105 -0
  145. package/dist/runtime/src/mcp/peer-bridge.ts +114 -0
  146. package/dist/runtime/src/mcp/runtime-spec.ts +134 -0
  147. package/dist/runtime/src/mcp/session-comm/bridge-ipc-config.ts +27 -0
  148. package/dist/runtime/src/mcp/session-comm/peer-forward.ts +165 -0
  149. package/dist/runtime/src/mcp/session-comm/runtime.ts +115 -0
  150. package/dist/runtime/src/mcp/session-comm/server.ts +649 -0
  151. package/dist/runtime/src/mcp/session-comm/topic-config.ts +131 -0
  152. package/dist/runtime/src/mcp/session-comm/topics.ts +229 -0
  153. package/dist/runtime/src/mcp/stdio-protect.ts +84 -0
  154. package/dist/runtime/src/mcp/system-health-server.ts +130 -0
  155. package/dist/runtime/src/mcp/task-server.ts +173 -0
  156. package/dist/runtime/src/mcp/token-stats-server.ts +98 -0
  157. package/dist/runtime/src/mcp/vault-http.ts +219 -0
  158. package/dist/runtime/src/mcp/vault-run.ts +153 -0
  159. package/dist/runtime/src/mcp/vault-server.ts +104 -0
  160. package/dist/runtime/src/mcp/wiki-server.ts +755 -0
  161. package/dist/runtime/src/media/file-events.ts +33 -0
  162. package/dist/runtime/src/media/text-extractor.ts +312 -0
  163. package/dist/runtime/src/media/video.ts +92 -0
  164. package/dist/runtime/src/outbox/file-ops.ts +210 -0
  165. package/dist/runtime/src/outbox/index.ts +8 -0
  166. package/dist/runtime/src/outbox/utils.ts +51 -0
  167. package/dist/runtime/src/platform/background-bash/manager.ts +279 -0
  168. package/dist/runtime/src/platform/config.ts +371 -0
  169. package/dist/runtime/src/platform/constants.ts +18 -0
  170. package/dist/runtime/src/platform/debug-users.ts +14 -0
  171. package/dist/runtime/src/platform/delay.ts +1 -0
  172. package/dist/runtime/src/platform/error.ts +9 -0
  173. package/dist/runtime/src/platform/file-utils.ts +16 -0
  174. package/dist/runtime/src/platform/jsonl.ts +202 -0
  175. package/dist/runtime/src/platform/lifecycle.ts +174 -0
  176. package/dist/runtime/src/platform/logger.ts +31 -0
  177. package/dist/runtime/src/platform/maestro-bootstrap-env.ts +3 -0
  178. package/dist/runtime/src/platform/mcp-config.ts +865 -0
  179. package/dist/runtime/src/platform/modules.ts +145 -0
  180. package/dist/runtime/src/platform/node-plugins.ts +46 -0
  181. package/dist/runtime/src/platform/playwright/manager.ts +1233 -0
  182. package/dist/runtime/src/prompts/agents/wiki-archiver.md +261 -0
  183. package/dist/runtime/src/prompts/builders.ts +357 -0
  184. package/dist/runtime/src/prompts/sessions/channel-system.md +48 -0
  185. package/dist/runtime/src/prompts/sessions/manager-system.md +37 -0
  186. package/dist/runtime/src/prompts/sessions/topic-system.md +47 -0
  187. package/dist/runtime/src/prompts/sessions/visual-design.md +86 -0
  188. package/dist/runtime/src/query/active-rooms.ts +532 -0
  189. package/dist/runtime/src/query/control.ts +313 -0
  190. package/dist/runtime/src/query/index.ts +24 -0
  191. package/dist/runtime/src/query/session-inbox-cleanup.ts +44 -0
  192. package/dist/runtime/src/query/session-inbox-path.ts +42 -0
  193. package/dist/runtime/src/query/state.ts +94 -0
  194. package/dist/runtime/src/query/types.ts +83 -0
  195. package/dist/runtime/src/runtime/ask-callbacks.ts +153 -0
  196. package/dist/runtime/src/runtime/attachments.ts +151 -0
  197. package/dist/runtime/src/runtime/background-sessions.ts +83 -0
  198. package/dist/runtime/src/runtime/channel-context.ts +107 -0
  199. package/dist/runtime/src/runtime/errors.ts +69 -0
  200. package/dist/runtime/src/runtime/event-heartbeat.ts +77 -0
  201. package/dist/runtime/src/runtime/file-hooks.ts +81 -0
  202. package/dist/runtime/src/runtime/footer.ts +23 -0
  203. package/dist/runtime/src/runtime/inbox.ts +946 -0
  204. package/dist/runtime/src/runtime/tasks.ts +56 -0
  205. package/dist/runtime/src/runtime/topic-config.ts +29 -0
  206. package/dist/runtime/src/runtime/turn-runner.ts +2286 -0
  207. package/dist/runtime/src/runtime/usage-alert.ts +84 -0
  208. package/dist/runtime/src/runtime/visual-html.ts +109 -0
  209. package/dist/runtime/src/runtime/visual-store.ts +430 -0
  210. package/dist/runtime/src/runtime/visuals.ts +171 -0
  211. package/dist/runtime/src/security/sanitize.ts +27 -0
  212. package/dist/runtime/src/security/sensitive-path.ts +34 -0
  213. package/dist/runtime/src/storage/activity-log.ts +152 -0
  214. package/dist/runtime/src/storage/api-messages.ts +617 -0
  215. package/dist/runtime/src/storage/api-topic-brief.ts +127 -0
  216. package/dist/runtime/src/storage/api-topic-config.ts +192 -0
  217. package/dist/runtime/src/storage/api-topics.ts +844 -0
  218. package/dist/runtime/src/storage/app-settings.ts +44 -0
  219. package/dist/runtime/src/storage/browser-profiles.ts +133 -0
  220. package/dist/runtime/src/storage/conversations.ts +268 -0
  221. package/dist/runtime/src/storage/forum/agent-settings.ts +20 -0
  222. package/dist/runtime/src/storage/forum/index.ts +4 -0
  223. package/dist/runtime/src/storage/forum/repository.ts +283 -0
  224. package/dist/runtime/src/storage/forum/schema.ts +300 -0
  225. package/dist/runtime/src/storage/forum-db.ts +23 -0
  226. package/dist/runtime/src/storage/runtime-events.ts +130 -0
  227. package/dist/runtime/src/storage/runtime-leases.ts +189 -0
  228. package/dist/runtime/src/storage/runtime-process-leases.ts +216 -0
  229. package/dist/runtime/src/storage/runtime-topic-state.ts +148 -0
  230. package/dist/runtime/src/storage/runtime-turn-requests.ts +300 -0
  231. package/dist/runtime/src/storage/self-schedules.ts +298 -0
  232. package/dist/runtime/src/storage/session-asks.ts +347 -0
  233. package/dist/runtime/src/storage/sqlite.ts +129 -0
  234. package/dist/runtime/src/storage/tasks.ts +179 -0
  235. package/dist/runtime/src/storage/token-stats.ts +144 -0
  236. package/dist/runtime/src/storage/topic-archive-state.ts +59 -0
  237. package/dist/runtime/src/storage/topic-archive.ts +74 -0
  238. package/dist/runtime/src/storage/topic-settings.ts +67 -0
  239. package/dist/runtime/src/storage/topic-transcript.ts +98 -0
  240. package/dist/runtime/src/storage/vault-crypto.ts +65 -0
  241. package/dist/runtime/src/storage/vault.ts +225 -0
  242. package/dist/runtime/src/storage/wiki-summary-names.ts +33 -0
  243. package/dist/runtime/src/storage/wiki.ts +85 -0
  244. package/dist/runtime/src/topics/create.ts +123 -0
  245. package/dist/runtime/src/topics/derive.ts +398 -0
  246. package/dist/runtime/src/topics/lifecycle.ts +311 -0
  247. package/dist/runtime/src/topics/links.ts +7 -0
  248. package/dist/runtime/src/topics/personal-general.ts +65 -0
  249. package/dist/runtime/src/topics/session.ts +381 -0
  250. package/dist/runtime/src/types/api.ts +497 -0
  251. package/dist/runtime/src/types.ts +255 -0
  252. package/dist/runtime/src/version.ts +1 -0
  253. package/dist/runtime/tsconfig.json +13 -0
  254. package/package.json +16 -3
  255. package/dist/main.d.ts +0 -3
  256. package/dist/main.d.ts.map +0 -1
@@ -0,0 +1,77 @@
1
+ import {
2
+ SESSIONS_DB
3
+ } from "./chunk-k9z47mab.js";
4
+ import {
5
+ __require,
6
+ __toESM
7
+ } from "./chunk-hwy3n31w.js";
8
+
9
+ // ../../packages/core/src/storage/forum-db.ts
10
+ import { mkdirSync } from "fs";
11
+ import { dirname } from "path";
12
+
13
+ // ../../packages/core/src/storage/sqlite.ts
14
+ var isBun = typeof process.versions.bun === "string";
15
+ var Database;
16
+ if (isBun) {
17
+ ({ Database } = await import("bun:sqlite"));
18
+ } else {
19
+ const nodeSqliteSpecifier = ["node", "sqlite"].join(":");
20
+ const { DatabaseSync } = await import(nodeSqliteSpecifier);
21
+
22
+ class NodeDatabase {
23
+ #db;
24
+ constructor(path, options = {}) {
25
+ this.#db = options.readonly ? new DatabaseSync(path, { readOnly: true }) : new DatabaseSync(path);
26
+ }
27
+ query(sql) {
28
+ return this.#db.prepare(sql);
29
+ }
30
+ prepare(sql) {
31
+ return this.#db.prepare(sql);
32
+ }
33
+ exec(sql) {
34
+ this.#db.exec(sql);
35
+ }
36
+ run(sql, ...params) {
37
+ return this.#db.prepare(sql).run(...params);
38
+ }
39
+ transaction(fn) {
40
+ const run = (begin) => (...args) => {
41
+ this.#db.exec(begin);
42
+ try {
43
+ const result = fn(...args);
44
+ this.#db.exec("COMMIT");
45
+ return result;
46
+ } catch (err) {
47
+ this.#db.exec("ROLLBACK");
48
+ throw err;
49
+ }
50
+ };
51
+ const tx = run("BEGIN");
52
+ tx.deferred = run("BEGIN DEFERRED");
53
+ tx.immediate = run("BEGIN IMMEDIATE");
54
+ tx.exclusive = run("BEGIN EXCLUSIVE");
55
+ return tx;
56
+ }
57
+ close() {
58
+ this.#db.close();
59
+ }
60
+ }
61
+ Database = NodeDatabase;
62
+ }
63
+
64
+ // ../../packages/core/src/storage/forum-db.ts
65
+ mkdirSync(dirname(SESSIONS_DB), { recursive: true });
66
+ var db = new Database(SESSIONS_DB, { create: true });
67
+ db.exec("PRAGMA journal_mode = WAL");
68
+ db.exec("PRAGMA foreign_keys = ON");
69
+ db.exec("PRAGMA busy_timeout = 5000");
70
+ db.exec("PRAGMA wal_autocheckpoint = 1000");
71
+ try {
72
+ db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
73
+ } catch {}
74
+
75
+ export { Database, db };
76
+
77
+ //# debugId=6EEF6536830551AA64756E2164756E21
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../packages/core/src/storage/forum-db.ts", "../../../packages/core/src/storage/sqlite.ts"],
4
+ "sourcesContent": [
5
+ "import { mkdirSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { SESSIONS_DB } from \"#platform/config\";\nimport { Database } from \"#storage/sqlite\";\n\nmkdirSync(dirname(SESSIONS_DB), { recursive: true });\n\nexport const db = new Database(SESSIONS_DB, { create: true });\ndb.exec(\"PRAGMA journal_mode = WAL\");\ndb.exec(\"PRAGMA foreign_keys = ON\");\ndb.exec(\"PRAGMA busy_timeout = 5000\");\n// Auto-checkpoint every ~1000 WAL pages (~4 MB at 4KB page size) to keep the\n// WAL file from growing unbounded during long-running pm2 sessions. The\n// default is 1000 but we set it explicitly so the policy is auditable.\ndb.exec(\"PRAGMA wal_autocheckpoint = 1000\");\n// Also run a TRUNCATE checkpoint on startup to reclaim any WAL space\n// accumulated while the bot was stopped (checkpointer doesn't run when\n// no connections are open).\ntry {\n db.exec(\"PRAGMA wal_checkpoint(TRUNCATE)\");\n} catch {\n // Non-fatal; a concurrent writer may hold the WAL briefly.\n}\n",
6
+ "// Runtime-adaptive SQLite façade.\n//\n// Why this exists: Otium normally runs on bun and uses `bun:sqlite` (native,\n// fast, zero-build). But the `codex` agent's MCP servers must run on pure node\n// — codex 0.135's rmcp stdio client can't handshake with bun-spawned servers\n// (see serverLaunch in platform/mcp-config.ts). `bun:sqlite` doesn't exist\n// under node, so any MCP server that touches the DB would crash on import.\n//\n// This module picks the backend at runtime and re-exports it as `Database`:\n// - under bun → the real `bun:sqlite` Database (unchanged behavior)\n// - under node → a thin shim over node:sqlite's DatabaseSync exposing the\n// exact subset Otium uses: new Database(path, {readonly?,create?}),\n// .query()/.prepare() → statement, .exec(), .run(), .transaction(fn),\n// .close(); statements support positional `?` params via .run()/.get()/\n// .all(). (No named-object params / .values()/.iterate()/.as() are used in\n// this codebase, so the shim intentionally omits them.)\n//\n// The TYPE of the exported `Database` is bun:sqlite's own class type (imported\n// type-only, so it's erased at runtime and never resolved under node). That\n// keeps every existing call site — including generic `.query<Row, Params>()`\n// usages — type-checking exactly as before with zero changes.\n//\n// The bun branch uses a dynamic import so node never resolves the `bun:sqlite`\n// specifier (dead code under node), and the node branch's `node:sqlite` import\n// never runs under bun.\n\nimport type { Database as BunDatabase } from \"bun:sqlite\";\n\n// `mock.module()` can replace the Bun global while a test graph is being\n// evaluated. The runtime version flag is stable and is also the documented\n// cross-runtime discriminator.\nconst isBun = typeof process.versions.bun === \"string\";\n\ntype DatabaseCtor = typeof BunDatabase;\n\nlet Database: DatabaseCtor;\n\nif (isBun) {\n ({ Database } = await import(\"bun:sqlite\"));\n} else {\n // node:sqlite has no type declarations under our tsconfig (`types: bun-types`\n // only), and `bun-types` doesn't ship them — so type the dynamic import\n // locally and use a `string` specifier to keep tsc from trying to resolve it.\n type NodeStatement = {\n run(...params: unknown[]): unknown;\n get(...params: unknown[]): unknown;\n all(...params: unknown[]): unknown[];\n };\n type NodeDatabaseSync = {\n prepare(sql: string): NodeStatement;\n exec(sql: string): void;\n close(): void;\n };\n type NodeSqliteModule = {\n DatabaseSync: new (path: string, options?: { readOnly?: boolean }) => NodeDatabaseSync;\n };\n // Keep Bun's resolver from eagerly resolving the Node-only builtin even\n // though this branch is dead there (notably under `bun test` + mock.module).\n const nodeSqliteSpecifier = [\"node\", \"sqlite\"].join(\":\");\n const { DatabaseSync } = (await import(nodeSqliteSpecifier)) as NodeSqliteModule;\n\n type DbOptions = { readonly?: boolean; create?: boolean };\n\n class NodeDatabase {\n #db: NodeDatabaseSync;\n\n constructor(path: string, options: DbOptions = {}) {\n // node:sqlite opens read-write and creates-if-missing by default, which\n // matches bun's `{create:true}`. For readonly we must not create.\n this.#db = options.readonly\n ? new DatabaseSync(path, { readOnly: true })\n : new DatabaseSync(path);\n }\n\n query(sql: string) {\n return this.#db.prepare(sql);\n }\n\n prepare(sql: string) {\n return this.#db.prepare(sql);\n }\n\n exec(sql: string): void {\n this.#db.exec(sql);\n }\n\n run(sql: string, ...params: unknown[]) {\n return this.#db.prepare(sql).run(...params);\n }\n\n transaction<Args extends unknown[], R>(fn: (...args: Args) => R): (...args: Args) => R {\n // bun's db.transaction(fn) returns a function that runs fn inside a\n // transaction when called. node:sqlite has no helper, so emulate with\n // BEGIN/COMMIT/ROLLBACK. It also exposes deferred/immediate/exclusive\n // variants; mirror those so Node-launched MCP servers can share call\n // sites with Bun.\n const run =\n (begin: \"BEGIN\" | \"BEGIN DEFERRED\" | \"BEGIN IMMEDIATE\" | \"BEGIN EXCLUSIVE\") =>\n (...args: Args): R => {\n this.#db.exec(begin);\n try {\n const result = fn(...args);\n this.#db.exec(\"COMMIT\");\n return result;\n } catch (err) {\n this.#db.exec(\"ROLLBACK\");\n throw err;\n }\n };\n const tx = run(\"BEGIN\") as ((...args: Args) => R) & {\n deferred: (...args: Args) => R;\n immediate: (...args: Args) => R;\n exclusive: (...args: Args) => R;\n };\n tx.deferred = run(\"BEGIN DEFERRED\");\n tx.immediate = run(\"BEGIN IMMEDIATE\");\n tx.exclusive = run(\"BEGIN EXCLUSIVE\");\n return tx;\n }\n\n close(): void {\n this.#db.close();\n }\n }\n\n Database = NodeDatabase as unknown as DatabaseCtor;\n}\n\nexport { Database };\n"
7
+ ],
8
+ "mappings": ";;;;;;;;;AAAA;AACA;;;AC8BA,IAAM,QAAQ,OAAO,QAAQ,SAAS,QAAQ;AAI9C,IAAI;AAEJ,IAAI,OAAO;AAAA,GACR,EAAE,SAAS,IAAI,MAAa;AAC/B,EAAO;AAAA,EAmBL,MAAM,sBAAsB,CAAC,QAAQ,QAAQ,EAAE,KAAK,GAAG;AAAA,EACvD,QAAQ,iBAAkB,MAAa;AAAA;AAAA,EAIvC,MAAM,aAAa;AAAA,IACjB;AAAA,IAEA,WAAW,CAAC,MAAc,UAAqB,CAAC,GAAG;AAAA,MAGjD,KAAK,MAAM,QAAQ,WACf,IAAI,aAAa,MAAM,EAAE,UAAU,KAAK,CAAC,IACzC,IAAI,aAAa,IAAI;AAAA;AAAA,IAG3B,KAAK,CAAC,KAAa;AAAA,MACjB,OAAO,KAAK,IAAI,QAAQ,GAAG;AAAA;AAAA,IAG7B,OAAO,CAAC,KAAa;AAAA,MACnB,OAAO,KAAK,IAAI,QAAQ,GAAG;AAAA;AAAA,IAG7B,IAAI,CAAC,KAAmB;AAAA,MACtB,KAAK,IAAI,KAAK,GAAG;AAAA;AAAA,IAGnB,GAAG,CAAC,QAAgB,QAAmB;AAAA,MACrC,OAAO,KAAK,IAAI,QAAQ,GAAG,EAAE,IAAI,GAAG,MAAM;AAAA;AAAA,IAG5C,WAAsC,CAAC,IAAgD;AAAA,MAMrF,MAAM,MACJ,CAAC,UACD,IAAI,SAAkB;AAAA,QACpB,KAAK,IAAI,KAAK,KAAK;AAAA,QACnB,IAAI;AAAA,UACF,MAAM,SAAS,GAAG,GAAG,IAAI;AAAA,UACzB,KAAK,IAAI,KAAK,QAAQ;AAAA,UACtB,OAAO;AAAA,UACP,OAAO,KAAK;AAAA,UACZ,KAAK,IAAI,KAAK,UAAU;AAAA,UACxB,MAAM;AAAA;AAAA;AAAA,MAGZ,MAAM,KAAK,IAAI,OAAO;AAAA,MAKtB,GAAG,WAAW,IAAI,gBAAgB;AAAA,MAClC,GAAG,YAAY,IAAI,iBAAiB;AAAA,MACpC,GAAG,YAAY,IAAI,iBAAiB;AAAA,MACpC,OAAO;AAAA;AAAA,IAGT,KAAK,GAAS;AAAA,MACZ,KAAK,IAAI,MAAM;AAAA;AAAA,EAEnB;AAAA,EAEA,WAAW;AAAA;;;ADxHb,UAAU,QAAQ,WAAW,GAAG,EAAE,WAAW,KAAK,CAAC;AAE5C,IAAM,KAAK,IAAI,SAAS,aAAa,EAAE,QAAQ,KAAK,CAAC;AAC5D,GAAG,KAAK,2BAA2B;AACnC,GAAG,KAAK,0BAA0B;AAClC,GAAG,KAAK,4BAA4B;AAIpC,GAAG,KAAK,kCAAkC;AAI1C,IAAI;AAAA,EACF,GAAG,KAAK,iCAAiC;AAAA,EACzC,MAAM;",
9
+ "debugId": "6EEF6536830551AA64756E2164756E21",
10
+ "names": []
11
+ }
@@ -0,0 +1,41 @@
1
+ // @bun
2
+ import {
3
+ getRegistry
4
+ } from "./chunk-5ahkew9c.js";
5
+ import"./chunk-6cpn7ht2.js";
6
+ import {
7
+ logger
8
+ } from "./chunk-k9z47mab.js";
9
+ import"./chunk-hwy3n31w.js";
10
+
11
+ // ../../packages/core/src/agents/fork.ts
12
+ import { existsSync, unlinkSync } from "fs";
13
+ async function forkAgentSession(opts) {
14
+ const { forkId, rolloutPath } = await getRegistry(opts.agent).forkSession({
15
+ parentSessionId: opts.parentSessionId,
16
+ cwd: opts.cwd,
17
+ userId: opts.userId,
18
+ topicName: opts.topicName,
19
+ ...opts.title ? { title: opts.title } : {},
20
+ ...opts.model ? { model: opts.model } : {},
21
+ ...opts.effort ? { effort: opts.effort } : {}
22
+ });
23
+ return { agent: opts.agent, forkId, rolloutPath };
24
+ }
25
+ function cleanupAgentFork(handle) {
26
+ try {
27
+ if (existsSync(handle.rolloutPath)) {
28
+ unlinkSync(handle.rolloutPath);
29
+ }
30
+ } catch (err) {
31
+ logger.warn({ err, agent: handle.agent, forkId: handle.forkId, rolloutPath: handle.rolloutPath }, "cleanupAgentFork: failed to remove rollout");
32
+ }
33
+ }
34
+ export {
35
+ forkAgentSession,
36
+ cleanupAgentFork
37
+ };
38
+
39
+ export { forkAgentSession, cleanupAgentFork };
40
+
41
+ //# debugId=F6D753EECB8A5F7C64756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../packages/core/src/agents/fork.ts"],
4
+ "sourcesContent": [
5
+ "import { existsSync, unlinkSync } from \"node:fs\";\nimport { getRegistry } from \"#agents/registry\";\nimport { logger } from \"#platform/logger\";\nimport type { AgentKind, EffortLevel } from \"#types\";\n\n/**\n * Agent-aware fork primitives.\n *\n * Strategy lives in each agent's registry (`*-registry.ts:forkSession`).\n * Claude uses the SDK's native `forkSession` (byte-equivalent rollout copy);\n * Codex synthesizes a rollout from the provider-agnostic conversation log\n * because the SDK has no fork API. Both surfaces converge on `ForkHandle`.\n */\n\nexport interface ForkAgentSessionOptions {\n agent: AgentKind;\n /** Parent session/thread id to clone from. */\n parentSessionId: string;\n /** Workspace directory the resumed fork will report. Claude also writes\n * the rollout file under this directory. */\n cwd: string;\n /** Used to locate the conversation log for codex synthesis. */\n userId: number | string;\n /** Used to locate the conversation log for codex synthesis. */\n topicName: string;\n /** Optional title forwarded to Claude SDK forkSession; ignored by Codex. */\n title?: string;\n /** Effective model/effort the fork will use when it resumes. */\n model?: string;\n effort?: EffortLevel;\n}\n\nexport interface ForkHandle {\n agent: AgentKind;\n forkId: string;\n /** Absolute path of the rollout file to remove on cleanup. */\n rolloutPath: string;\n}\n\nexport async function forkAgentSession(opts: ForkAgentSessionOptions): Promise<ForkHandle> {\n const { forkId, rolloutPath } = await getRegistry(opts.agent).forkSession({\n parentSessionId: opts.parentSessionId,\n cwd: opts.cwd,\n userId: opts.userId,\n topicName: opts.topicName,\n ...(opts.title ? { title: opts.title } : {}),\n ...(opts.model ? { model: opts.model } : {}),\n ...(opts.effort ? { effort: opts.effort } : {}),\n });\n return { agent: opts.agent, forkId, rolloutPath };\n}\n\n/** Best-effort cleanup of a fork's rollout file. Errors are logged, never thrown. */\nexport function cleanupAgentFork(handle: ForkHandle): void {\n try {\n if (existsSync(handle.rolloutPath)) {\n unlinkSync(handle.rolloutPath);\n }\n } catch (err) {\n logger.warn(\n { err, agent: handle.agent, forkId: handle.forkId, rolloutPath: handle.rolloutPath },\n \"cleanupAgentFork: failed to remove rollout\",\n );\n }\n}\n"
6
+ ],
7
+ "mappings": ";;;;;;;;;;;AAAA;AAuCA,eAAsB,gBAAgB,CAAC,MAAoD;AAAA,EACzF,QAAQ,QAAQ,gBAAgB,MAAM,YAAY,KAAK,KAAK,EAAE,YAAY;AAAA,IACxE,iBAAiB,KAAK;AAAA,IACtB,KAAK,KAAK;AAAA,IACV,QAAQ,KAAK;AAAA,IACb,WAAW,KAAK;AAAA,OACZ,KAAK,QAAQ,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;AAAA,OACtC,KAAK,QAAQ,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;AAAA,OACtC,KAAK,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;AAAA,EAC/C,CAAC;AAAA,EACD,OAAO,EAAE,OAAO,KAAK,OAAO,QAAQ,YAAY;AAAA;AAI3C,SAAS,gBAAgB,CAAC,QAA0B;AAAA,EACzD,IAAI;AAAA,IACF,IAAI,WAAW,OAAO,WAAW,GAAG;AAAA,MAClC,WAAW,OAAO,WAAW;AAAA,IAC/B;AAAA,IACA,OAAO,KAAK;AAAA,IACZ,OAAO,KACL,EAAE,KAAK,OAAO,OAAO,OAAO,QAAQ,OAAO,QAAQ,aAAa,OAAO,YAAY,GACnF,4CACF;AAAA;AAAA;",
8
+ "debugId": "F6D753EECB8A5F7C64756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,252 @@
1
+ import {
2
+ assertSecureCentralUrl,
3
+ assertSecureRelayUrl,
4
+ isJoinPersisted,
5
+ joinCredentialDigest,
6
+ saveJoinWhileLocked,
7
+ withJoinCredentialLock
8
+ } from "./chunk-e5pbggsh.js";
9
+ import {
10
+ DATA_DIR
11
+ } from "./chunk-k9z47mab.js";
12
+
13
+ // ../../adapters/otium/src/enrollment.ts
14
+ import {
15
+ createDecipheriv,
16
+ createPrivateKey,
17
+ createPublicKey,
18
+ diffieHellman,
19
+ generateKeyPairSync,
20
+ hkdfSync,
21
+ randomUUID
22
+ } from "crypto";
23
+ import {
24
+ chmodSync,
25
+ closeSync,
26
+ existsSync,
27
+ fsyncSync,
28
+ linkSync,
29
+ mkdirSync,
30
+ openSync,
31
+ readFileSync,
32
+ renameSync,
33
+ unlinkSync,
34
+ writeFileSync
35
+ } from "fs";
36
+ import { dirname, resolve } from "path";
37
+ var INFO = Buffer.from("otium-node-enrollment-v1", "utf8");
38
+ function parseEnrollmentInvite(code) {
39
+ let parsed;
40
+ try {
41
+ parsed = JSON.parse(Buffer.from(code.trim(), "base64url").toString("utf8"));
42
+ } catch {
43
+ throw new Error("production invite must be base64url JSON");
44
+ }
45
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
46
+ throw new Error("production invite must decode to an object");
47
+ }
48
+ const raw = parsed;
49
+ const central = typeof raw.central === "string" ? raw.central.trim().replace(/\/+$/, "") : "";
50
+ const token = typeof raw.token === "string" ? raw.token.trim() : "";
51
+ if (raw.v !== 2 || !central || !token.startsWith("nei_")) {
52
+ throw new Error("production invite requires {v:2, central:http(s), token:nei_\u2026}");
53
+ }
54
+ assertSecureCentralUrl(central);
55
+ return { v: 2, central, token };
56
+ }
57
+ function pendingEnrollmentPath() {
58
+ return resolve(DATA_DIR, "otium-enrollment-pending.json");
59
+ }
60
+ function isEnrollmentPending(invite) {
61
+ const path = pendingEnrollmentPath();
62
+ if (!existsSync(path))
63
+ return false;
64
+ try {
65
+ const saved = JSON.parse(readFileSync(path, "utf8"));
66
+ return saved.central === invite.central && saved.token === invite.token;
67
+ } catch {
68
+ return false;
69
+ }
70
+ }
71
+ function loadOrCreatePending(invite, nodeName) {
72
+ const path = pendingEnrollmentPath();
73
+ if (existsSync(path)) {
74
+ const saved = JSON.parse(readFileSync(path, "utf8"));
75
+ if (saved.central === invite.central && saved.token === invite.token)
76
+ return saved;
77
+ throw new Error(`another Otium enrollment is pending at ${path}`);
78
+ }
79
+ const pair = generateKeyPairSync("x25519");
80
+ const pending = {
81
+ ...invite,
82
+ idempotencyKey: randomUUID(),
83
+ privateKey: pair.privateKey.export({ format: "der", type: "pkcs8" }).toString("base64url"),
84
+ publicKey: pair.publicKey.export({ format: "der", type: "spki" }).toString("base64url"),
85
+ ...nodeName ? { nodeName } : {}
86
+ };
87
+ mkdirSync(dirname(path), { recursive: true });
88
+ const temporaryPath = resolve(dirname(path), `.otium-enrollment-pending.json.${process.pid}.${randomUUID()}.tmp`);
89
+ let fd;
90
+ try {
91
+ fd = openSync(temporaryPath, "wx", 384);
92
+ writeFileSync(fd, `${JSON.stringify(pending, null, 2)}
93
+ `, "utf8");
94
+ fsyncSync(fd);
95
+ closeSync(fd);
96
+ fd = undefined;
97
+ linkSync(temporaryPath, path);
98
+ unlinkSync(temporaryPath);
99
+ chmodSync(path, 384);
100
+ const directoryFd = openSync(dirname(path), "r");
101
+ try {
102
+ fsyncSync(directoryFd);
103
+ } finally {
104
+ closeSync(directoryFd);
105
+ }
106
+ } catch (error) {
107
+ if (fd !== undefined)
108
+ closeSync(fd);
109
+ if (existsSync(temporaryPath))
110
+ unlinkSync(temporaryPath);
111
+ if (error.code === "EEXIST" && existsSync(path)) {
112
+ const saved = JSON.parse(readFileSync(path, "utf8"));
113
+ if (saved.central === invite.central && saved.token === invite.token)
114
+ return saved;
115
+ }
116
+ throw error;
117
+ }
118
+ return pending;
119
+ }
120
+ function replacePendingEnrollment(pending) {
121
+ const path = pendingEnrollmentPath();
122
+ const directory = dirname(path);
123
+ const temporaryPath = resolve(directory, `.otium-enrollment-pending.json.${process.pid}.${randomUUID()}.tmp`);
124
+ let fd;
125
+ try {
126
+ fd = openSync(temporaryPath, "wx", 384);
127
+ writeFileSync(fd, `${JSON.stringify(pending, null, 2)}
128
+ `, "utf8");
129
+ fsyncSync(fd);
130
+ closeSync(fd);
131
+ fd = undefined;
132
+ renameSync(temporaryPath, path);
133
+ const directoryFd = openSync(directory, "r");
134
+ try {
135
+ fsyncSync(directoryFd);
136
+ } finally {
137
+ closeSync(directoryFd);
138
+ }
139
+ } catch (error) {
140
+ if (fd !== undefined)
141
+ closeSync(fd);
142
+ if (existsSync(temporaryPath))
143
+ unlinkSync(temporaryPath);
144
+ throw error;
145
+ }
146
+ }
147
+ function recordClaimedCredential(pending, join) {
148
+ withJoinCredentialLock(() => {
149
+ const current = JSON.parse(readFileSync(pendingEnrollmentPath(), "utf8"));
150
+ if (current.central !== pending.central || current.token !== pending.token || current.idempotencyKey !== pending.idempotencyKey || current.publicKey !== pending.publicKey) {
151
+ throw new Error("pending Otium enrollment changed while its claim was in flight");
152
+ }
153
+ const claimed = { digest: joinCredentialDigest(join), cellId: join.cellId };
154
+ if (current.claimed && (current.claimed.digest !== claimed.digest || current.claimed.cellId !== claimed.cellId)) {
155
+ throw new Error("central returned different credentials for an idempotent enrollment claim");
156
+ }
157
+ replacePendingEnrollment({ ...current, claimed });
158
+ });
159
+ }
160
+ function openCredential(envelope, privateKeyDer) {
161
+ if (envelope.v !== 1 || envelope.algorithm !== "X25519-HKDF-SHA256-AES-256-GCM") {
162
+ throw new Error("unsupported enrollment credential envelope");
163
+ }
164
+ const privateKey = createPrivateKey({
165
+ key: Buffer.from(privateKeyDer, "base64url"),
166
+ format: "der",
167
+ type: "pkcs8"
168
+ });
169
+ const ephemeral = createPublicKey({
170
+ key: Buffer.from(envelope.ephemeralPublicKey, "base64url"),
171
+ format: "der",
172
+ type: "spki"
173
+ });
174
+ const shared = diffieHellman({ privateKey, publicKey: ephemeral });
175
+ const key = Buffer.from(hkdfSync("sha256", shared, Buffer.from(envelope.salt, "base64url"), INFO, 32));
176
+ const decipher = createDecipheriv("aes-256-gcm", key, Buffer.from(envelope.nonce, "base64url"));
177
+ decipher.setAAD(INFO);
178
+ decipher.setAuthTag(Buffer.from(envelope.tag, "base64url"));
179
+ return Buffer.concat([
180
+ decipher.update(Buffer.from(envelope.ciphertext, "base64url")),
181
+ decipher.final()
182
+ ]).toString("utf8");
183
+ }
184
+ async function postJson(url, body, headers) {
185
+ const response = await fetch(url, {
186
+ method: "POST",
187
+ headers: { "content-type": "application/json", ...headers },
188
+ body: JSON.stringify(body)
189
+ });
190
+ const result = await response.json();
191
+ if (!response.ok)
192
+ throw new Error(String(result.error || `request failed (${response.status})`));
193
+ return result;
194
+ }
195
+ async function previewEnrollment(invite) {
196
+ return postJson(`${invite.central}/api/v1/node-enrollments/preview`, {
197
+ token: invite.token
198
+ });
199
+ }
200
+ async function claimEnrollment(invite, nodeName) {
201
+ const pending = loadOrCreatePending(invite, nodeName);
202
+ const response = await postJson(`${invite.central}/api/v1/node-enrollments/claim`, {
203
+ token: invite.token,
204
+ credentialPublicKey: pending.publicKey,
205
+ ...pending.nodeName ? { nodeName: pending.nodeName } : {}
206
+ }, { "idempotency-key": pending.idempotencyKey });
207
+ const credential = response.credential;
208
+ const secret = openCredential(credential, pending.privateKey);
209
+ if (!secret.startsWith("rcs_"))
210
+ throw new Error("central returned an invalid runtime credential");
211
+ const join = {
212
+ v: 2,
213
+ central: invite.central,
214
+ relay: String(response.relayUrl),
215
+ cellId: String(response.cell?.id),
216
+ secret
217
+ };
218
+ if (!join.relay || !join.cellId || join.cellId === "undefined") {
219
+ throw new Error("central returned an incomplete enrollment response");
220
+ }
221
+ assertSecureRelayUrl(join.relay);
222
+ recordClaimedCredential(pending, join);
223
+ return join;
224
+ }
225
+ function commitEnrollment(join, options = {}) {
226
+ return withJoinCredentialLock(() => {
227
+ const pendingPath = pendingEnrollmentPath();
228
+ const pending = existsSync(pendingPath) ? JSON.parse(readFileSync(pendingPath, "utf8")) : null;
229
+ const digest = joinCredentialDigest(join);
230
+ if (pending && (!pending.claimed || pending.claimed.digest !== digest || pending.claimed.cellId !== join.cellId)) {
231
+ throw new Error(`pending Otium enrollment at ${pendingPath} does not match these credentials`);
232
+ }
233
+ const path = saveJoinWhileLocked(join, options);
234
+ if (!isJoinPersisted(join)) {
235
+ throw new Error("Otium join credentials were not durably persisted");
236
+ }
237
+ if (!pending)
238
+ return path;
239
+ unlinkSync(pendingPath);
240
+ const directoryFd = openSync(dirname(pendingPath), "r");
241
+ try {
242
+ fsyncSync(directoryFd);
243
+ } finally {
244
+ closeSync(directoryFd);
245
+ }
246
+ return path;
247
+ });
248
+ }
249
+
250
+ export { parseEnrollmentInvite, pendingEnrollmentPath, isEnrollmentPending, previewEnrollment, claimEnrollment, commitEnrollment };
251
+
252
+ //# debugId=3E67B5DF4109FD5964756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../adapters/otium/src/enrollment.ts"],
4
+ "sourcesContent": [
5
+ "import {\n createDecipheriv,\n createPrivateKey,\n createPublicKey,\n diffieHellman,\n generateKeyPairSync,\n hkdfSync,\n randomUUID,\n} from \"node:crypto\";\nimport {\n chmodSync,\n closeSync,\n existsSync,\n fsyncSync,\n linkSync,\n mkdirSync,\n openSync,\n readFileSync,\n renameSync,\n unlinkSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { DATA_DIR } from \"@negotium/core\";\nimport {\n isJoinPersisted,\n joinCredentialDigest,\n type OtiumJoin,\n type SaveJoinOptions,\n saveJoinWhileLocked,\n withJoinCredentialLock,\n} from \"@/join\";\nimport { assertSecureCentralUrl, assertSecureRelayUrl } from \"@/secure-transport\";\n\nconst INFO = Buffer.from(\"otium-node-enrollment-v1\", \"utf8\");\n\nexport interface EnrollmentInvite {\n v: 2;\n central: string;\n token: string;\n}\n\ninterface PendingEnrollment extends EnrollmentInvite {\n idempotencyKey: string;\n privateKey: string;\n publicKey: string;\n /** Frozen with the idempotency key so retries replay the same claim body. */\n nodeName?: string;\n claimed?: {\n digest: string;\n cellId: string;\n };\n}\n\nexport interface EnrollmentCredentialEnvelope {\n v: 1;\n algorithm: \"X25519-HKDF-SHA256-AES-256-GCM\";\n ephemeralPublicKey: string;\n salt: string;\n nonce: string;\n ciphertext: string;\n tag: string;\n}\n\nexport interface EnrollmentPreviewResponse {\n ok: true;\n preview: {\n workspace: { id: string; slug: string; name: string };\n suggestedNodeName: string | null;\n transport: \"relay\";\n relayUrl: string;\n expiresAt: string;\n status: string;\n topics: string;\n };\n}\n\ninterface EnrollmentClaimResponse {\n ok: true;\n relayUrl: string;\n cell: { id: string; baseUrl: string };\n credential: EnrollmentCredentialEnvelope;\n}\n\nexport function parseEnrollmentInvite(code: string): EnrollmentInvite {\n let parsed: unknown;\n try {\n parsed = JSON.parse(Buffer.from(code.trim(), \"base64url\").toString(\"utf8\"));\n } catch {\n throw new Error(\"production invite must be base64url JSON\");\n }\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n throw new Error(\"production invite must decode to an object\");\n }\n const raw = parsed as Record<string, unknown>;\n const central = typeof raw.central === \"string\" ? raw.central.trim().replace(/\\/+$/, \"\") : \"\";\n const token = typeof raw.token === \"string\" ? raw.token.trim() : \"\";\n if (raw.v !== 2 || !central || !token.startsWith(\"nei_\")) {\n throw new Error(\"production invite requires {v:2, central:http(s), token:nei_…}\");\n }\n assertSecureCentralUrl(central);\n return { v: 2, central, token };\n}\n\nexport function pendingEnrollmentPath(): string {\n return resolve(DATA_DIR, \"otium-enrollment-pending.json\");\n}\n\nexport function isEnrollmentPending(invite: EnrollmentInvite): boolean {\n const path = pendingEnrollmentPath();\n if (!existsSync(path)) return false;\n try {\n const saved = JSON.parse(readFileSync(path, \"utf8\")) as PendingEnrollment;\n return saved.central === invite.central && saved.token === invite.token;\n } catch {\n return false;\n }\n}\n\nfunction loadOrCreatePending(invite: EnrollmentInvite, nodeName?: string): PendingEnrollment {\n const path = pendingEnrollmentPath();\n if (existsSync(path)) {\n const saved = JSON.parse(readFileSync(path, \"utf8\")) as PendingEnrollment;\n if (saved.central === invite.central && saved.token === invite.token) return saved;\n throw new Error(`another Otium enrollment is pending at ${path}`);\n }\n const pair = generateKeyPairSync(\"x25519\");\n const pending: PendingEnrollment = {\n ...invite,\n idempotencyKey: randomUUID(),\n privateKey: pair.privateKey.export({ format: \"der\", type: \"pkcs8\" }).toString(\"base64url\"),\n publicKey: pair.publicKey.export({ format: \"der\", type: \"spki\" }).toString(\"base64url\"),\n ...(nodeName ? { nodeName } : {}),\n };\n mkdirSync(dirname(path), { recursive: true });\n const temporaryPath = resolve(\n dirname(path),\n `.otium-enrollment-pending.json.${process.pid}.${randomUUID()}.tmp`,\n );\n let fd: number | undefined;\n try {\n fd = openSync(temporaryPath, \"wx\", 0o600);\n writeFileSync(fd, `${JSON.stringify(pending, null, 2)}\\n`, \"utf8\");\n fsyncSync(fd);\n closeSync(fd);\n fd = undefined;\n // Publish only the completely written key material and never overwrite a\n // concurrent enrollment created by another process.\n linkSync(temporaryPath, path);\n unlinkSync(temporaryPath);\n chmodSync(path, 0o600);\n const directoryFd = openSync(dirname(path), \"r\");\n try {\n fsyncSync(directoryFd);\n } finally {\n closeSync(directoryFd);\n }\n } catch (error) {\n if (fd !== undefined) closeSync(fd);\n if (existsSync(temporaryPath)) unlinkSync(temporaryPath);\n if ((error as NodeJS.ErrnoException).code === \"EEXIST\" && existsSync(path)) {\n const saved = JSON.parse(readFileSync(path, \"utf8\")) as PendingEnrollment;\n if (saved.central === invite.central && saved.token === invite.token) return saved;\n }\n throw error;\n }\n return pending;\n}\n\nfunction replacePendingEnrollment(pending: PendingEnrollment): void {\n const path = pendingEnrollmentPath();\n const directory = dirname(path);\n const temporaryPath = resolve(\n directory,\n `.otium-enrollment-pending.json.${process.pid}.${randomUUID()}.tmp`,\n );\n let fd: number | undefined;\n try {\n fd = openSync(temporaryPath, \"wx\", 0o600);\n writeFileSync(fd, `${JSON.stringify(pending, null, 2)}\\n`, \"utf8\");\n fsyncSync(fd);\n closeSync(fd);\n fd = undefined;\n renameSync(temporaryPath, path);\n const directoryFd = openSync(directory, \"r\");\n try {\n fsyncSync(directoryFd);\n } finally {\n closeSync(directoryFd);\n }\n } catch (error) {\n if (fd !== undefined) closeSync(fd);\n if (existsSync(temporaryPath)) unlinkSync(temporaryPath);\n throw error;\n }\n}\n\nfunction recordClaimedCredential(pending: PendingEnrollment, join: OtiumJoin): void {\n withJoinCredentialLock(() => {\n const current = JSON.parse(readFileSync(pendingEnrollmentPath(), \"utf8\")) as PendingEnrollment;\n if (\n current.central !== pending.central ||\n current.token !== pending.token ||\n current.idempotencyKey !== pending.idempotencyKey ||\n current.publicKey !== pending.publicKey\n ) {\n throw new Error(\"pending Otium enrollment changed while its claim was in flight\");\n }\n const claimed = { digest: joinCredentialDigest(join), cellId: join.cellId };\n if (\n current.claimed &&\n (current.claimed.digest !== claimed.digest || current.claimed.cellId !== claimed.cellId)\n ) {\n throw new Error(\"central returned different credentials for an idempotent enrollment claim\");\n }\n replacePendingEnrollment({ ...current, claimed });\n });\n}\n\nfunction openCredential(envelope: EnrollmentCredentialEnvelope, privateKeyDer: string): string {\n if (envelope.v !== 1 || envelope.algorithm !== \"X25519-HKDF-SHA256-AES-256-GCM\") {\n throw new Error(\"unsupported enrollment credential envelope\");\n }\n const privateKey = createPrivateKey({\n key: Buffer.from(privateKeyDer, \"base64url\"),\n format: \"der\",\n type: \"pkcs8\",\n });\n const ephemeral = createPublicKey({\n key: Buffer.from(envelope.ephemeralPublicKey, \"base64url\"),\n format: \"der\",\n type: \"spki\",\n });\n const shared = diffieHellman({ privateKey, publicKey: ephemeral });\n const key = Buffer.from(\n hkdfSync(\"sha256\", shared, Buffer.from(envelope.salt, \"base64url\"), INFO, 32),\n );\n const decipher = createDecipheriv(\"aes-256-gcm\", key, Buffer.from(envelope.nonce, \"base64url\"));\n decipher.setAAD(INFO);\n decipher.setAuthTag(Buffer.from(envelope.tag, \"base64url\"));\n return Buffer.concat([\n decipher.update(Buffer.from(envelope.ciphertext, \"base64url\")),\n decipher.final(),\n ]).toString(\"utf8\");\n}\n\nasync function postJson<T>(\n url: string,\n body: unknown,\n headers?: Record<string, string>,\n): Promise<T> {\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\", ...headers },\n body: JSON.stringify(body),\n });\n const result = (await response.json()) as Record<string, unknown>;\n if (!response.ok) throw new Error(String(result.error || `request failed (${response.status})`));\n return result as T;\n}\n\nexport async function previewEnrollment(\n invite: EnrollmentInvite,\n): Promise<EnrollmentPreviewResponse> {\n return postJson<EnrollmentPreviewResponse>(`${invite.central}/api/v1/node-enrollments/preview`, {\n token: invite.token,\n });\n}\n\nexport async function claimEnrollment(\n invite: EnrollmentInvite,\n nodeName?: string,\n): Promise<OtiumJoin> {\n const pending = loadOrCreatePending(invite, nodeName);\n const response = await postJson<EnrollmentClaimResponse>(\n `${invite.central}/api/v1/node-enrollments/claim`,\n {\n token: invite.token,\n credentialPublicKey: pending.publicKey,\n ...(pending.nodeName ? { nodeName: pending.nodeName } : {}),\n },\n { \"idempotency-key\": pending.idempotencyKey },\n );\n const credential = response.credential as EnrollmentCredentialEnvelope;\n const secret = openCredential(credential, pending.privateKey);\n if (!secret.startsWith(\"rcs_\")) throw new Error(\"central returned an invalid runtime credential\");\n const join: OtiumJoin = {\n v: 2,\n central: invite.central,\n relay: String(response.relayUrl),\n cellId: String(response.cell?.id),\n secret,\n };\n if (!join.relay || !join.cellId || join.cellId === \"undefined\") {\n throw new Error(\"central returned an incomplete enrollment response\");\n }\n assertSecureRelayUrl(join.relay);\n recordClaimedCredential(pending, join);\n return join;\n}\n\n/**\n * Durably commit a claimed enrollment. The retry key is deliberately removed\n * only after the atomic join-file save is fsynced and verified. A crash before\n * that point leaves enough key material to replay the idempotent claim.\n */\nexport function commitEnrollment(join: OtiumJoin, options: SaveJoinOptions = {}): string {\n return withJoinCredentialLock(() => {\n const pendingPath = pendingEnrollmentPath();\n const pending = existsSync(pendingPath)\n ? (JSON.parse(readFileSync(pendingPath, \"utf8\")) as PendingEnrollment)\n : null;\n const digest = joinCredentialDigest(join);\n if (\n pending &&\n (!pending.claimed ||\n pending.claimed.digest !== digest ||\n pending.claimed.cellId !== join.cellId)\n ) {\n throw new Error(\n `pending Otium enrollment at ${pendingPath} does not match these credentials`,\n );\n }\n\n const path = saveJoinWhileLocked(join, options);\n if (!isJoinPersisted(join)) {\n throw new Error(\"Otium join credentials were not durably persisted\");\n }\n if (!pending) return path;\n\n // The same lock protects save→verification→pending deletion from a\n // concurrent explicit replacement through saveJoin().\n unlinkSync(pendingPath);\n const directoryFd = openSync(dirname(pendingPath), \"r\");\n try {\n fsyncSync(directoryFd);\n } finally {\n closeSync(directoryFd);\n }\n return path;\n });\n}\n"
6
+ ],
7
+ "mappings": "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",
8
+ "debugId": "3E67B5DF4109FD5964756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,33 @@
1
+ // ../../adapters/otium/src/protocol.ts
2
+ var PEER_PROTOCOL_VERSION = 1;
3
+ var MAX_PEER_MESSAGE_LENGTH = 1e4;
4
+ var MAX_PEER_INPUT_FILE_BYTES = 2 * 1024 * 1024 * 1024;
5
+ var MAX_PEER_INPUT_REQUEST_BYTES = MAX_PEER_INPUT_FILE_BYTES + 8 * 1024 * 1024;
6
+ function str(body, field) {
7
+ const value = body[field];
8
+ return typeof value === "string" && value.trim() ? value : null;
9
+ }
10
+ function parseExecutionSpec(value) {
11
+ if (!value || typeof value !== "object" || Array.isArray(value))
12
+ return null;
13
+ const raw = value;
14
+ const agent = str(raw, "agent");
15
+ const model = str(raw, "model");
16
+ const effort = str(raw, "effort");
17
+ const rawMcp = raw.mcp;
18
+ if (!agent || !model || !effort || !Array.isArray(rawMcp) || !rawMcp.every((entry) => typeof entry === "string" && entry.trim().length > 0) || typeof raw.canSpawnSubagents !== "boolean") {
19
+ return null;
20
+ }
21
+ return {
22
+ agent,
23
+ model,
24
+ effort,
25
+ ...str(raw, "description") ? { description: str(raw, "description") } : {},
26
+ mcp: [...new Set(rawMcp.map((entry) => entry.trim()))],
27
+ canSpawnSubagents: raw.canSpawnSubagents
28
+ };
29
+ }
30
+
31
+ export { PEER_PROTOCOL_VERSION, MAX_PEER_MESSAGE_LENGTH, MAX_PEER_INPUT_FILE_BYTES, MAX_PEER_INPUT_REQUEST_BYTES, parseExecutionSpec };
32
+
33
+ //# debugId=536F85318EA03AF964756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../adapters/otium/src/protocol.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * otium peer protocol — payload shapes exchanged between workspace runtime\n * nodes. Field-for-field copy of otium's\n * `apps/runtime-api/src/peer/protocol.ts` (PEER_PROTOCOL_VERSION 1): the hub\n * trusts these shapes without schema validation, so this file must track the\n * otium side exactly.\n */\n\nexport const PEER_PROTOCOL_VERSION = 1;\n\nexport interface PeerFeatureCapabilities {\n remoteAsk: boolean;\n inputFiles: boolean;\n outputFiles: boolean;\n visualBridge: boolean;\n}\n\nexport const MAX_PEER_MESSAGE_LENGTH = 10_000;\nexport const MAX_PEER_INPUT_FILE_BYTES = 2 * 1024 * 1024 * 1024;\nexport const MAX_PEER_INPUT_REQUEST_BYTES = MAX_PEER_INPUT_FILE_BYTES + 8 * 1024 * 1024;\n\nexport interface PeerSessionEntry {\n topicId: string;\n name: string;\n agent: string | null;\n hasSession: boolean;\n description?: string;\n}\n\n/** Fully-resolved room configuration pinned by the hub for one worker turn.\n * The worker must not re-resolve these values from its hidden mirror topic. */\nexport interface PlacedTopicExecutionSpec {\n agent: string;\n model: string;\n effort: string;\n description?: string;\n /** Optional MCP whitelist after hub-side topic override resolution. */\n mcp: string[];\n /** Preserves the host room's real top-level delegation capability. */\n canSpawnSubagents: boolean;\n}\n\n/** Hub → worker: run one turn of a hub-hosted agent room. */\nexport interface PeerTurnRequest {\n v: number;\n requestId: string;\n userId: string;\n /** The hub's canonical topic id — the worker keys its hidden topic on it. */\n hostTopicId: string;\n /** Room title, mirrored onto the hidden topic so tell/ask by title works. */\n topicTitle: string;\n execution?: PlacedTopicExecutionSpec;\n /** Legacy additive fields accepted during rolling upgrades. */\n agent?: string;\n model?: string;\n effort?: string;\n /** File ids already copied into the worker hidden topic's upload store. */\n attachments?: string[];\n message: string;\n}\n\n/** Hub → worker: create/update the hidden mirror before the first turn. */\nexport interface PeerProvisionRequest {\n v: number;\n userId: string;\n hostTopicId: string;\n topicTitle: string;\n execution: PlacedTopicExecutionSpec;\n}\n\n/** Worker → hub: one coarse turn event, ordered by seq per requestId. */\nexport interface PeerEventRequest {\n v: number;\n requestId: string;\n seq: number;\n event: Record<string, unknown>;\n}\n\nfunction str(body: Record<string, unknown>, field: string): string | null {\n const value = body[field];\n return typeof value === \"string\" && value.trim() ? value : null;\n}\n\n/** Parse and normalize an inbound execution spec (mirrors otium routes.ts). */\nexport function parseExecutionSpec(value: unknown): PlacedTopicExecutionSpec | null {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return null;\n const raw = value as Record<string, unknown>;\n const agent = str(raw, \"agent\");\n const model = str(raw, \"model\");\n const effort = str(raw, \"effort\");\n const rawMcp = raw.mcp;\n if (\n !agent ||\n !model ||\n !effort ||\n !Array.isArray(rawMcp) ||\n !rawMcp.every((entry) => typeof entry === \"string\" && entry.trim().length > 0) ||\n typeof raw.canSpawnSubagents !== \"boolean\"\n ) {\n return null;\n }\n return {\n agent,\n model,\n effort,\n ...(str(raw, \"description\") ? { description: str(raw, \"description\") as string } : {}),\n mcp: [...new Set(rawMcp.map((entry) => entry.trim()))],\n canSpawnSubagents: raw.canSpawnSubagents,\n };\n}\n"
6
+ ],
7
+ "mappings": ";AAQO,IAAM,wBAAwB;AAS9B,IAAM,0BAA0B;AAChC,IAAM,4BAA4B,IAAI,OAAO,OAAO;AACpD,IAAM,+BAA+B,4BAA4B,IAAI,OAAO;AA2DnF,SAAS,GAAG,CAAC,MAA+B,OAA8B;AAAA,EACxE,MAAM,QAAQ,KAAK;AAAA,EACnB,OAAO,OAAO,UAAU,YAAY,MAAM,KAAK,IAAI,QAAQ;AAAA;AAItD,SAAS,kBAAkB,CAAC,OAAiD;AAAA,EAClF,KAAK,SAAS,OAAO,UAAU,YAAY,MAAM,QAAQ,KAAK;AAAA,IAAG,OAAO;AAAA,EACxE,MAAM,MAAM;AAAA,EACZ,MAAM,QAAQ,IAAI,KAAK,OAAO;AAAA,EAC9B,MAAM,QAAQ,IAAI,KAAK,OAAO;AAAA,EAC9B,MAAM,SAAS,IAAI,KAAK,QAAQ;AAAA,EAChC,MAAM,SAAS,IAAI;AAAA,EACnB,KACG,UACA,UACA,WACA,MAAM,QAAQ,MAAM,MACpB,OAAO,MAAM,CAAC,UAAU,OAAO,UAAU,YAAY,MAAM,KAAK,EAAE,SAAS,CAAC,KAC7E,OAAO,IAAI,sBAAsB,WACjC;AAAA,IACA,OAAO;AAAA,EACT;AAAA,EACA,OAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,OACI,IAAI,KAAK,aAAa,IAAI,EAAE,aAAa,IAAI,KAAK,aAAa,EAAY,IAAI,CAAC;AAAA,IACpF,KAAK,CAAC,GAAG,IAAI,IAAI,OAAO,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC,CAAC,CAAC;AAAA,IACrD,mBAAmB,IAAI;AAAA,EACzB;AAAA;",
8
+ "debugId": "536F85318EA03AF964756E2164756E21",
9
+ "names": []
10
+ }