brane 0.0.2 → 0.3.0

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 (338) hide show
  1. package/AGENTS.md +362 -0
  2. package/CHANGELOG.md +33 -0
  3. package/CONTRIBUTING.md +91 -0
  4. package/DESIGN.md +402 -0
  5. package/Dockerfile +61 -0
  6. package/LICENSE +656 -17
  7. package/README.md +174 -2
  8. package/SECURITY.md +205 -0
  9. package/dist/absorb/brane-id.js +11 -0
  10. package/dist/absorb/chatgpt.js +161 -0
  11. package/dist/absorb/claude-code.js +504 -0
  12. package/dist/absorb/index.js +543 -0
  13. package/dist/absorb/organelle.js +345 -0
  14. package/dist/absorb/run.js +520 -0
  15. package/dist/absorb/sources.js +168 -0
  16. package/dist/absorb/types.js +3 -0
  17. package/dist/agent-env.js +45 -0
  18. package/dist/agents.js +1168 -0
  19. package/dist/claude-live-session.js +124 -0
  20. package/dist/cli.js +50 -0
  21. package/dist/codex-tool-log.js +144 -0
  22. package/dist/commands/absorb.js +291 -0
  23. package/dist/commands/attach.js +31 -0
  24. package/dist/commands/certify.js +62 -0
  25. package/dist/commands/config.js +116 -0
  26. package/dist/commands/cost.js +126 -0
  27. package/dist/commands/daemon.js +350 -0
  28. package/dist/commands/doctor.js +262 -0
  29. package/dist/commands/fold.js +95 -0
  30. package/dist/commands/list.js +89 -0
  31. package/dist/commands/mcp.js +84 -0
  32. package/dist/commands/meld.js +332 -0
  33. package/dist/commands/memory.js +215 -0
  34. package/dist/commands/revive.js +74 -0
  35. package/dist/commands/run.js +85 -0
  36. package/dist/commands/spawn.js +1094 -0
  37. package/dist/commands/token.js +153 -0
  38. package/dist/copilot-tool-log.js +145 -0
  39. package/dist/cost/pricing.js +116 -0
  40. package/dist/cost/reprice.js +136 -0
  41. package/dist/crush-tool-log.js +293 -0
  42. package/dist/daemon/agent-scope.js +116 -0
  43. package/dist/daemon/awaiting.js +54 -0
  44. package/dist/daemon/cert-manager.js +349 -0
  45. package/dist/daemon/client.js +262 -0
  46. package/dist/daemon/gated-routes.js +112 -0
  47. package/dist/daemon/keep-awake.js +103 -0
  48. package/dist/daemon/local-socket.js +145 -0
  49. package/dist/daemon/presence-gate.js +206 -0
  50. package/dist/daemon/server.js +6349 -0
  51. package/dist/daemon/signal-source.js +35 -0
  52. package/dist/daemon/tailscale-rebind.js +58 -0
  53. package/dist/daemon/token-ring.js +263 -0
  54. package/dist/daemon/token-routes.js +190 -0
  55. package/dist/db/events.js +152 -0
  56. package/dist/db/mentions.js +179 -0
  57. package/dist/db/schema.js +633 -0
  58. package/dist/db/tokens.js +583 -0
  59. package/dist/db/tool-uses.js +647 -0
  60. package/dist/diagram/index.js +85 -0
  61. package/dist/diagram/label.js +455 -0
  62. package/dist/diagram/layout.js +476 -0
  63. package/dist/diagram/links.js +50 -0
  64. package/dist/diagram/metrics.js +152 -0
  65. package/dist/diagram/parse.js +487 -0
  66. package/dist/diagram/svg.js +389 -0
  67. package/dist/diagram/types.js +9 -0
  68. package/dist/export.js +629 -0
  69. package/dist/files/archive-types.js +2 -0
  70. package/dist/files/archive.js +182 -0
  71. package/dist/files/browse.js +327 -0
  72. package/dist/files/bytes.js +162 -0
  73. package/dist/files/codecs.js +33 -0
  74. package/dist/files/find.js +212 -0
  75. package/dist/files/tar.js +223 -0
  76. package/dist/files/zip.js +209 -0
  77. package/dist/folds.js +72 -0
  78. package/dist/grammars/ebnf.js +92 -0
  79. package/dist/highlight.js +250 -0
  80. package/dist/ids.js +24 -0
  81. package/dist/markdown-diagnostics.js +233 -0
  82. package/dist/math.js +168 -0
  83. package/dist/mcp/server.js +886 -0
  84. package/dist/memory/embedder.js +145 -0
  85. package/dist/memory/embedding-worker.js +71 -0
  86. package/dist/memory/embeddings.js +91 -0
  87. package/dist/memory/index.js +232 -0
  88. package/dist/memory/storage.js +587 -0
  89. package/dist/memory/wiki.js +688 -0
  90. package/dist/memory/wordpiece.js +247 -0
  91. package/dist/mentions-scan.js +40 -0
  92. package/dist/presence.js +259 -0
  93. package/dist/session-affinity.js +185 -0
  94. package/dist/shared/api-request.js +124 -0
  95. package/dist/shared/block-glyphs.js +114 -0
  96. package/dist/shared/diagram-css.js +17 -0
  97. package/dist/shared/export-selection.js +101 -0
  98. package/dist/shared/find-in-text.js +110 -0
  99. package/dist/shared/glow-hues.js +225 -0
  100. package/dist/shared/hex-dump.js +62 -0
  101. package/dist/shared/hex-pages.js +52 -0
  102. package/dist/shared/hit-drum.js +79 -0
  103. package/dist/shared/known-nids.js +58 -0
  104. package/dist/shared/list-scroll-keys.js +56 -0
  105. package/dist/shared/markdown-lists.js +72 -0
  106. package/dist/shared/markdown-math.js +485 -0
  107. package/dist/shared/markdown-mentions.js +79 -0
  108. package/dist/shared/markdown-nids.js +41 -0
  109. package/dist/shared/markdown-protocols.js +53 -0
  110. package/dist/shared/markdown-refs.js +123 -0
  111. package/dist/shared/membrane-invasion.js +342 -0
  112. package/dist/shared/membrane-motion.js +83 -0
  113. package/dist/shared/membrane-palette.js +253 -0
  114. package/dist/shared/membrane-terrain.js +212 -0
  115. package/dist/shared/message-selection.js +123 -0
  116. package/dist/shared/mo-glyph.js +57 -0
  117. package/dist/shared/narrative-format.js +237 -0
  118. package/dist/shared/narrative-menu.js +82 -0
  119. package/dist/shared/narrative-sort.js +47 -0
  120. package/dist/shared/narrative-status.js +52 -0
  121. package/dist/shared/no-pan.js +85 -0
  122. package/dist/shared/pane-layout.js +50 -0
  123. package/dist/shared/payload-inline.js +83 -0
  124. package/dist/shared/speaker-color.js +61 -0
  125. package/dist/shared/token-rotation.js +14 -0
  126. package/dist/shared/view-url.js +197 -0
  127. package/dist/shared/wiki-links.js +70 -0
  128. package/dist/shared/wiki-window.js +4 -0
  129. package/dist/tailscale.js +55 -0
  130. package/dist/test/absorb-claude.test.js +892 -0
  131. package/dist/test/agent-scope.test.js +193 -0
  132. package/dist/test/agents.test.js +55 -0
  133. package/dist/test/api-request.test.js +122 -0
  134. package/dist/test/awaiting-glow.test.js +54 -0
  135. package/dist/test/awaiting.test.js +122 -0
  136. package/dist/test/bind-token.test.js +88 -0
  137. package/dist/test/block-glyphs.test.js +68 -0
  138. package/dist/test/cert-manager.test.js +262 -0
  139. package/dist/test/claude-live-session.test.js +110 -0
  140. package/dist/test/codex-tool-log.test.js +568 -0
  141. package/dist/test/config.test.js +71 -0
  142. package/dist/test/copilot-tool-log.test.js +93 -0
  143. package/dist/test/cors-preflight.test.js +49 -0
  144. package/dist/test/cost-linger.test.js +31 -0
  145. package/dist/test/cost-pricing.test.js +72 -0
  146. package/dist/test/cost-reprice.test.js +139 -0
  147. package/dist/test/cost-sessions.test.js +301 -0
  148. package/dist/test/crush-tool-log.test.js +289 -0
  149. package/dist/test/daemon-client.test.js +109 -0
  150. package/dist/test/daemon-socket.test.js +293 -0
  151. package/dist/test/diagram-arrows.test.js +205 -0
  152. package/dist/test/diagram-export.test.js +210 -0
  153. package/dist/test/diagram-labels.test.js +313 -0
  154. package/dist/test/diagram-parse.test.js +191 -0
  155. package/dist/test/diagram-render.test.js +175 -0
  156. package/dist/test/diagram-review.test.js +288 -0
  157. package/dist/test/diagram-self-loop-labels.test.js +303 -0
  158. package/dist/test/embedder.test.js +84 -0
  159. package/dist/test/events.test.js +123 -0
  160. package/dist/test/export-selection.test.js +102 -0
  161. package/dist/test/export.test.js +505 -0
  162. package/dist/test/files-browse.test.js +393 -0
  163. package/dist/test/files-find.test.js +272 -0
  164. package/dist/test/find-in-text.test.js +86 -0
  165. package/dist/test/first-paint-viewport.test.js +59 -0
  166. package/dist/test/fixtures/embedding-golden.js +23 -0
  167. package/dist/test/fixtures/wordpiece-golden.js +167 -0
  168. package/dist/test/glow-hues.test.js +202 -0
  169. package/dist/test/harness-cost.test.js +423 -0
  170. package/dist/test/helpers.js +21 -0
  171. package/dist/test/hex-pages.test.js +68 -0
  172. package/dist/test/highlight.test.js +69 -0
  173. package/dist/test/hit-drum.test.js +71 -0
  174. package/dist/test/hook-config.test.js +262 -0
  175. package/dist/test/json-gzip.test.js +70 -0
  176. package/dist/test/key-routing.test.js +58 -0
  177. package/dist/test/known-nids.test.js +106 -0
  178. package/dist/test/link-preview.test.js +67 -0
  179. package/dist/test/list-scroll-keys.test.js +66 -0
  180. package/dist/test/listen-own-words.test.js +147 -0
  181. package/dist/test/listen-timeout.test.js +33 -0
  182. package/dist/test/markdown-diagnostics.test.js +121 -0
  183. package/dist/test/markdown-lists.test.js +182 -0
  184. package/dist/test/markdown-math.test.js +256 -0
  185. package/dist/test/markdown-nids.test.js +146 -0
  186. package/dist/test/markdown-protocols.test.js +139 -0
  187. package/dist/test/mcp-bridge-cancel.test.js +98 -0
  188. package/dist/test/mcp-bridge-reconnect.test.js +121 -0
  189. package/dist/test/mcp-narrative.test.js +193 -0
  190. package/dist/test/mcp.test.js +548 -0
  191. package/dist/test/meld.test.js +85 -0
  192. package/dist/test/membrane-invasion.test.js +482 -0
  193. package/dist/test/membrane-motion.test.js +101 -0
  194. package/dist/test/membrane-palette.test.js +166 -0
  195. package/dist/test/membrane-terrain.test.js +170 -0
  196. package/dist/test/memory-log.test.js +202 -0
  197. package/dist/test/mentions-e2e.test.js +261 -0
  198. package/dist/test/mentions-ledger.test.js +265 -0
  199. package/dist/test/mentions-listen.test.js +265 -0
  200. package/dist/test/mentions.test.js +238 -0
  201. package/dist/test/message-selection.test.js +114 -0
  202. package/dist/test/mo-glyph.test.js +22 -0
  203. package/dist/test/model-resolution.test.js +166 -0
  204. package/dist/test/narrate-validate.test.js +77 -0
  205. package/dist/test/narrate-wait.test.js +160 -0
  206. package/dist/test/narrative-format.test.js +219 -0
  207. package/dist/test/narrative-menu.test.js +137 -0
  208. package/dist/test/narrative-sort.test.js +75 -0
  209. package/dist/test/no-pan.test.js +60 -0
  210. package/dist/test/pane-layout.test.js +153 -0
  211. package/dist/test/paths.test.js +37 -0
  212. package/dist/test/payload-inline.test.js +91 -0
  213. package/dist/test/precache-fonts.test.js +72 -0
  214. package/dist/test/presence-gate.test.js +440 -0
  215. package/dist/test/presence.test.js +160 -0
  216. package/dist/test/read-events.test.js +220 -0
  217. package/dist/test/remote-meld.test.js +115 -0
  218. package/dist/test/revive-cli.test.js +93 -0
  219. package/dist/test/search-floor.test.js +206 -0
  220. package/dist/test/search-fusion.test.js +168 -0
  221. package/dist/test/search.test.js +205 -0
  222. package/dist/test/security.test.js +48 -0
  223. package/dist/test/selection-wiring.test.js +61 -0
  224. package/dist/test/session-affinity.test.js +295 -0
  225. package/dist/test/signal-source.test.js +32 -0
  226. package/dist/test/socket-account.test.js +268 -0
  227. package/dist/test/spawn-toml.test.js +72 -0
  228. package/dist/test/speaker-color.test.js +90 -0
  229. package/dist/test/tailscale-rebind.test.js +79 -0
  230. package/dist/test/terminal-cursor.test.js +61 -0
  231. package/dist/test/terminal-resync.test.js +23 -0
  232. package/dist/test/thin-cli.test.js +382 -0
  233. package/dist/test/token-bootstrap.test.js +272 -0
  234. package/dist/test/token-devices.test.js +587 -0
  235. package/dist/test/token-lapse.test.js +94 -0
  236. package/dist/test/token-ring.test.js +188 -0
  237. package/dist/test/token-rotate-cli.test.js +58 -0
  238. package/dist/test/token-routes.test.js +95 -0
  239. package/dist/test/tool-uses.test.js +1006 -0
  240. package/dist/test/usage-ledger.test.js +126 -0
  241. package/dist/test/view-url.test.js +581 -0
  242. package/dist/test/wiki-links.test.js +57 -0
  243. package/dist/test/wiki-sources.test.js +85 -0
  244. package/dist/test/wiki-window.test.js +31 -0
  245. package/dist/test/wiki.test.js +399 -0
  246. package/dist/test/wordpiece.test.js +68 -0
  247. package/dist/tls.js +148 -0
  248. package/dist/tmux.js +202 -0
  249. package/dist/user-agent.js +46 -0
  250. package/docker-entrypoint.sh +141 -0
  251. package/docs/README.md +3 -0
  252. package/docs/absorb.md +231 -0
  253. package/docs/cost-ledger.md +125 -0
  254. package/docs/daemon-api.md +189 -0
  255. package/docs/diagrams.md +163 -0
  256. package/docs/doctor.md +36 -0
  257. package/docs/export-scope.md +69 -0
  258. package/docs/files-view.md +133 -0
  259. package/docs/link-previews.md +38 -0
  260. package/docs/math.md +32 -0
  261. package/docs/mcp.md +69 -0
  262. package/docs/memory-search.md +75 -0
  263. package/docs/mentions.md +101 -0
  264. package/docs/multi-brane.md +14 -0
  265. package/docs/passkeys.md +165 -0
  266. package/docs/presence.md +202 -0
  267. package/docs/releasing.md +53 -0
  268. package/docs/rendering-warnings.md +22 -0
  269. package/docs/request-layer.md +72 -0
  270. package/docs/selection-store.md +50 -0
  271. package/docs/session-affinity.md +61 -0
  272. package/docs/terminal-mirroring.md +211 -0
  273. package/docs/token-rotation.md +413 -0
  274. package/docs/view-urls.md +114 -0
  275. package/docs/web-app.md +23 -0
  276. package/docs/wiki.md +43 -0
  277. package/expo/dist/_expo/static/js/web/entry-e56efc1291dca7258ea00b8108b033a4.js +1097 -0
  278. package/expo/dist/apple-touch-icon.png +0 -0
  279. package/expo/dist/favicon.svg +3 -0
  280. package/expo/dist/index.html +177 -0
  281. package/expo/dist/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  282. package/expo/dist/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  283. package/expo/dist/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  284. package/expo/dist/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  285. package/expo/dist/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  286. package/expo/dist/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
  287. package/expo/dist/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  288. package/expo/dist/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
  289. package/expo/dist/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
  290. package/expo/dist/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  291. package/expo/dist/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
  292. package/expo/dist/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  293. package/expo/dist/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  294. package/expo/dist/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  295. package/expo/dist/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
  296. package/expo/dist/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  297. package/expo/dist/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  298. package/expo/dist/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  299. package/expo/dist/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  300. package/expo/dist/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  301. package/expo/dist/katex/katex.min.css +1 -0
  302. package/expo/dist/manifest.json +16 -0
  303. package/expo/dist/metadata.json +1 -0
  304. package/expo/dist/og/membrane.v1.png +0 -0
  305. package/expo/dist/pdf.min.mjs +28 -0
  306. package/expo/dist/pdf.worker.min.mjs +28 -0
  307. package/expo/dist/sw.js +154 -0
  308. package/package.json +81 -23
  309. package/protocols/LIVE_loop.md +407 -0
  310. package/protocols/bebrief.md +41 -0
  311. package/protocols/digest.md +231 -0
  312. package/protocols/fly.md +266 -0
  313. package/protocols/idle.md +81 -0
  314. package/protocols/keeper.md +829 -0
  315. package/protocols/markdown.md +80 -0
  316. package/protocols/memory.md +419 -0
  317. package/protocols/mentions.md +93 -0
  318. package/protocols/payloads.md +53 -0
  319. package/protocols/prelude.md +91 -0
  320. package/protocols/ramify.md +105 -0
  321. package/protocols/review.md +356 -0
  322. package/protocols/rrr.md +232 -0
  323. package/protocols/scribe.md +429 -0
  324. package/protocols/summarize.md +203 -0
  325. package/protocols/twin.md +174 -0
  326. package/protocols/wiki.md +438 -0
  327. package/protocols/worker.md +38 -0
  328. package/wiki/shared/append-only-event-log.md +148 -0
  329. package/wiki/shared/index.md +48 -0
  330. package/wiki/shared/owning-your-narratives.md +161 -0
  331. package/wiki/shared/premature-ontologies.md +435 -0
  332. package/wiki/shared/sedimentary-superintelligence.md +128 -0
  333. package/wiki/shared/sequestering-the-narrative.md +143 -0
  334. package/wiki/shared/why-wikis-work.md +216 -0
  335. package/.npmignore +0 -2
  336. package/.travis.yml +0 -5
  337. package/index.js +0 -8
  338. package/index.js~ +0 -12
@@ -0,0 +1,345 @@
1
+ /** Organelle absorb — imports narratives from organelle's search.db + niche filesystem */
2
+ import { randomBase36 } from "../ids.js";
3
+ import { copyFileSync, existsSync, readdirSync, readFileSync, statSync, } from "node:fs";
4
+ import { join, extname } from "node:path";
5
+ import BetterSqlite3 from "better-sqlite3";
6
+ import { getPayloadsDir } from "../db/schema.js";
7
+ import { spawnedContent } from "../shared/narrative-format.js";
8
+ /**
9
+ * Default search.db location. Assumes a single organelle installation
10
+ * per machine at ~/.organelle/ (the only layout organelle ever used).
11
+ */
12
+ const SEARCH_DB_PATH = join(process.env.HOME, ".organelle", "index", "search.db");
13
+ /** Find organelle search.db, returns path or undefined */
14
+ export function findSearchDb() {
15
+ return existsSync(SEARCH_DB_PATH) ? SEARCH_DB_PATH : undefined;
16
+ }
17
+ /** Scan a niche's .organelle/narratives/ for narrative directories */
18
+ function scanNicheNarratives(nichePath) {
19
+ const narrativesDir = join(nichePath, ".organelle", "narratives");
20
+ if (!existsSync(narrativesDir))
21
+ return [];
22
+ return readdirSync(narrativesDir).filter((name) => {
23
+ if (name.startsWith("."))
24
+ return false;
25
+ try {
26
+ return statSync(join(narrativesDir, name)).isDirectory();
27
+ }
28
+ catch {
29
+ return false;
30
+ }
31
+ });
32
+ }
33
+ /** Read meta.json for a narrative */
34
+ function readNarrativeMeta(nichePath, nid) {
35
+ const metaPath = join(nichePath, ".organelle", "narratives", nid, "meta.json");
36
+ if (!existsSync(metaPath))
37
+ return undefined;
38
+ try {
39
+ return JSON.parse(readFileSync(metaPath, "utf-8"));
40
+ }
41
+ catch {
42
+ return undefined;
43
+ }
44
+ }
45
+ /** Map organelle event_type → brane event type */
46
+ function mapEventType(eventType, sender, knownNarrativeIds) {
47
+ switch (eventType) {
48
+ case "signal_received":
49
+ // Agent-to-agent signals: sender is a known narrative ID
50
+ if (sender && knownNarrativeIds.has(sender))
51
+ return "agent_signal";
52
+ return "human_signal";
53
+ case "narration":
54
+ return "narration";
55
+ case "started":
56
+ return "started";
57
+ case "stopped":
58
+ return "stopped";
59
+ default:
60
+ return "narration";
61
+ }
62
+ }
63
+ /** Event types worth importing (skip terminal_frame, restarted) */
64
+ const IMPORTABLE_TYPES = new Set([
65
+ "narration",
66
+ "signal_received",
67
+ "started",
68
+ "stopped",
69
+ ]);
70
+ /** Utility agent types to skip */
71
+ const UTILITY_ROLES = new Set(["utility"]);
72
+ /** Describe a niche for display before import */
73
+ export function describeOrganelleNiche(nichePath) {
74
+ const searchDbPath = findSearchDb();
75
+ const narrativeIds = scanNicheNarratives(nichePath);
76
+ const agentTypes = {};
77
+ let narrativesWithEvents = 0;
78
+ let totalEvents = 0;
79
+ let skippedUtility = 0;
80
+ if (searchDbPath) {
81
+ const orgDb = new BetterSqlite3(searchDbPath, { readonly: true });
82
+ const countEvents = orgDb.prepare("SELECT COUNT(*) as cnt FROM events WHERE narrative_id = ? AND event_type IN ('narration', 'signal_received', 'started', 'stopped')");
83
+ for (const nid of narrativeIds) {
84
+ const meta = readNarrativeMeta(nichePath, nid);
85
+ // Skip utility narratives
86
+ if (meta && UTILITY_ROLES.has(meta.role)) {
87
+ skippedUtility++;
88
+ continue;
89
+ }
90
+ const { cnt } = countEvents.get(nid);
91
+ if (cnt > 0) {
92
+ narrativesWithEvents++;
93
+ totalEvents += cnt;
94
+ const type = meta?.agent_type || nid.replace(/-[a-f0-9]+$/, "");
95
+ agentTypes[type] = (agentTypes[type] || 0) + 1;
96
+ }
97
+ }
98
+ orgDb.close();
99
+ }
100
+ return {
101
+ nichePath,
102
+ narrativeCount: narrativeIds.length - skippedUtility,
103
+ narrativesWithEvents,
104
+ agentTypes,
105
+ searchDbPath,
106
+ totalEvents,
107
+ };
108
+ }
109
+ /** Read all niches from ~/.organelle/niches.json (single installation assumed) */
110
+ export function readNichesRegistry() {
111
+ const nichesPath = join(process.env.HOME, ".organelle", "niches.json");
112
+ if (!existsSync(nichesPath))
113
+ return {};
114
+ try {
115
+ const data = JSON.parse(readFileSync(nichesPath, "utf-8"));
116
+ return data.niches || {};
117
+ }
118
+ catch {
119
+ return {};
120
+ }
121
+ }
122
+ /** Copy attachment files from organelle receptor to brane payloads */
123
+ function copyAttachments(nichePath, orgNid, evt, braneNid, seq, summary, dryRun) {
124
+ if (!evt.extra)
125
+ return undefined;
126
+ let extra;
127
+ try {
128
+ extra = JSON.parse(evt.extra);
129
+ }
130
+ catch {
131
+ return undefined;
132
+ }
133
+ if (!extra.attachments || extra.attachments.length === 0)
134
+ return undefined;
135
+ const narrativeDir = join(nichePath, ".organelle", "narratives", orgNid);
136
+ const payloadPaths = [];
137
+ for (let i = 0; i < extra.attachments.length; i++) {
138
+ const attachName = extra.attachments[i];
139
+ // Attachments may be in receptor/ (old format) or attachments/ (new format)
140
+ // The attachName itself may include the subdirectory prefix (e.g. "attachments/...")
141
+ const candidates = [
142
+ join(narrativeDir, "receptor", attachName),
143
+ join(narrativeDir, attachName),
144
+ ];
145
+ const srcPath = candidates.find((p) => existsSync(p));
146
+ if (!srcPath) {
147
+ summary.unresolvedAssets++;
148
+ continue;
149
+ }
150
+ try {
151
+ const size = statSync(srcPath).size;
152
+ summary.assets++;
153
+ summary.assetsBytes += size;
154
+ if (!dryRun) {
155
+ const dir = getPayloadsDir(braneNid);
156
+ const ext = extname(srcPath) || ".bin";
157
+ const storedName = `${evt.timestamp_ms}_${seq}_${i}${ext}`;
158
+ copyFileSync(srcPath, join(dir, storedName));
159
+ payloadPaths.push(`${braneNid}/${storedName}`);
160
+ }
161
+ }
162
+ catch {
163
+ summary.unresolvedAssets++;
164
+ }
165
+ }
166
+ return payloadPaths.length > 0 ? payloadPaths : undefined;
167
+ }
168
+ /**
169
+ * Absorb organelle narratives from one or more niche directories.
170
+ *
171
+ * Reads events from ~/.organelle/index/search.db, metadata from
172
+ * {niche}/.organelle/narratives/{nid}/meta.json, and copies
173
+ * attachments from receptor directories.
174
+ */
175
+ export async function absorbOrganelle(db, niches, braneId, identity, onProgress, dryRun) {
176
+ const summary = {
177
+ provider: "organelle",
178
+ conversations: 0,
179
+ messages: 0,
180
+ oldest: null,
181
+ newest: null,
182
+ models: {},
183
+ skipped: 0,
184
+ appended: 0,
185
+ assets: 0,
186
+ assetsBytes: 0,
187
+ unresolvedAssets: 0,
188
+ };
189
+ const searchDbPath = findSearchDb();
190
+ if (!searchDbPath) {
191
+ throw new Error("Cannot find organelle search database at ~/.organelle/index/search.db");
192
+ }
193
+ const organelleDb = new BetterSqlite3(searchDbPath, { readonly: true });
194
+ // Prepared statements for brane DB
195
+ const insertNarrative = dryRun
196
+ ? undefined
197
+ : db.prepare(`INSERT INTO narratives (id, brane_id, fold_path, agent_type, role, created_at, status, title, archived, agent_session_id)
198
+ VALUES (?, ?, ?, ?, ?, ?, 'ended', ?, 1, ?)`);
199
+ const insertEvent = dryRun
200
+ ? undefined
201
+ : db.prepare(`INSERT INTO events (narrative_id, seq, time, type, content, content_type, source, re, payloads, meta)
202
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
203
+ // Dedup by agent_session_id (unique across organelle imports)
204
+ const checkExisting = db.prepare(`SELECT id FROM narratives WHERE agent_session_id = ?`);
205
+ const getMaxSeq = db.prepare("SELECT MAX(seq) as max_seq FROM events WHERE narrative_id = ?");
206
+ const getExistingEventCount = db.prepare("SELECT COUNT(*) as cnt FROM events WHERE narrative_id = ?");
207
+ // Query organelle events (excluding terminal_frame and restarted)
208
+ const queryEvents = organelleDb.prepare(`SELECT * FROM events WHERE narrative_id = ?
209
+ AND event_type IN ('narration', 'signal_received', 'started', 'stopped')
210
+ ORDER BY seq ASC`);
211
+ // Build set of all known narrative IDs for agent signal detection
212
+ const allNarrativeIds = new Set(organelleDb
213
+ .prepare("SELECT DISTINCT narrative_id FROM events")
214
+ .all().map((r) => r.narrative_id));
215
+ const source = identity
216
+ ? `organelle:${identity.label}`
217
+ : "organelle:operator";
218
+ // Insert a single narrative (transaction-wrapped)
219
+ const insertOne = dryRun
220
+ ? undefined
221
+ : db.transaction((orgNid, meta, events, nicheFoldPath, nicheDir) => {
222
+ const uniqueId = randomBase36(8);
223
+ const braneNid = `organelle-${uniqueId}`;
224
+ const agentType = meta?.agent_type || orgNid.replace(/-[a-f0-9]+$/, "");
225
+ const role = "absorbed";
226
+ const createdAt = meta?.started_at
227
+ ? new Date(meta.started_at).getTime()
228
+ : events[0]?.timestamp_ms || Date.now();
229
+ const externalId = `organelle:${orgNid}`;
230
+ // Extract title from first narration content (truncated)
231
+ const firstNarration = events.find((e) => e.event_type === "narration" && e.content);
232
+ const title = firstNarration?.content
233
+ ? firstNarration.content.slice(0, 100)
234
+ : null;
235
+ insertNarrative.run(braneNid, braneId, nicheFoldPath, agentType, role, createdAt, title, externalId);
236
+ for (let i = 0; i < events.length; i++) {
237
+ const evt = events[i];
238
+ const seq = i + 1;
239
+ const type = mapEventType(evt.event_type, evt.sender, allNarrativeIds);
240
+ const msgSource = evt.event_type === "signal_received"
241
+ ? evt.sender || source
242
+ : evt.speaker || orgNid;
243
+ // Build content for started/stopped from extra
244
+ let content = evt.content;
245
+ if (evt.event_type === "started" && !content && evt.extra) {
246
+ try {
247
+ const ex = JSON.parse(evt.extra);
248
+ content = spawnedContent(ex.agent_type || agentType, ex.role || role);
249
+ }
250
+ catch {
251
+ content = spawnedContent(agentType, role);
252
+ }
253
+ }
254
+ const payloads = copyAttachments(nicheDir, orgNid, evt, braneNid, seq, summary, false);
255
+ insertEvent.run(braneNid, seq, evt.timestamp_ms, type, content, null, // content_type
256
+ msgSource, null, // re
257
+ payloads ? JSON.stringify(payloads) : null, null);
258
+ }
259
+ return braneNid;
260
+ });
261
+ let processed = 0;
262
+ for (const { nichePath, foldPath } of niches) {
263
+ const narrativeIds = scanNicheNarratives(nichePath);
264
+ for (const orgNid of narrativeIds) {
265
+ // Every narrative's work below is synchronous SQLite and file copies.
266
+ // Yield between them so the daemon that runs this keeps answering —
267
+ // and so the progress stream actually reaches the client mid-run.
268
+ await new Promise((r) => setImmediate(r));
269
+ const meta = readNarrativeMeta(nichePath, orgNid);
270
+ // Skip utility narratives (caffeinate, htop, lazygit, zsh)
271
+ if (meta && UTILITY_ROLES.has(meta.role)) {
272
+ processed++;
273
+ onProgress?.(processed, orgNid);
274
+ continue;
275
+ }
276
+ const events = queryEvents.all(orgNid);
277
+ // Skip narratives with no importable events
278
+ if (events.length === 0) {
279
+ summary.skipped++;
280
+ processed++;
281
+ onProgress?.(processed, orgNid);
282
+ continue;
283
+ }
284
+ // Update summary timestamps
285
+ const firstTime = events[0].timestamp_ms;
286
+ const lastTime = events[events.length - 1].timestamp_ms;
287
+ if (!summary.oldest || firstTime < summary.oldest.time) {
288
+ summary.oldest = { time: firstTime, title: orgNid };
289
+ }
290
+ if (!summary.newest || lastTime > summary.newest.time) {
291
+ summary.newest = { time: lastTime, title: orgNid };
292
+ }
293
+ // Track agent types in models field
294
+ const agentType = meta?.agent_type || orgNid.replace(/-[a-f0-9]+$/, "");
295
+ summary.models[agentType] = (summary.models[agentType] || 0) + 1;
296
+ // Dedup check
297
+ const externalId = `organelle:${orgNid}`;
298
+ const existing = checkExisting.get(externalId);
299
+ if (existing) {
300
+ const { cnt } = getExistingEventCount.get(existing.id);
301
+ if (events.length <= cnt) {
302
+ summary.skipped++;
303
+ }
304
+ else {
305
+ if (!dryRun) {
306
+ // Append new events
307
+ const { max_seq } = getMaxSeq.get(existing.id);
308
+ let nextSeq = max_seq + 1;
309
+ for (let i = cnt; i < events.length; i++) {
310
+ const evt = events[i];
311
+ const type = mapEventType(evt.event_type, evt.sender, allNarrativeIds);
312
+ const msgSource = evt.event_type === "signal_received"
313
+ ? evt.sender || source
314
+ : evt.speaker || orgNid;
315
+ const payloads = copyAttachments(nichePath, orgNid, evt, existing.id, nextSeq, summary, false);
316
+ insertEvent.run(existing.id, nextSeq++, evt.timestamp_ms, type, evt.content, null, msgSource, null, payloads ? JSON.stringify(payloads) : null, null);
317
+ }
318
+ }
319
+ summary.appended++;
320
+ }
321
+ }
322
+ else {
323
+ // Count content events (narration + signal) for messages total
324
+ const contentEvents = events.filter((e) => e.event_type === "narration" ||
325
+ e.event_type === "signal_received");
326
+ summary.messages += contentEvents.length;
327
+ if (!dryRun) {
328
+ insertOne(orgNid, meta, events, foldPath, nichePath);
329
+ }
330
+ else {
331
+ // Dry-run: count assets
332
+ for (const evt of events) {
333
+ copyAttachments(nichePath, orgNid, evt, "dry-run", 0, summary, true);
334
+ }
335
+ }
336
+ summary.conversations++;
337
+ }
338
+ processed++;
339
+ onProgress?.(processed, orgNid);
340
+ }
341
+ }
342
+ organelleDb.close();
343
+ return summary;
344
+ }
345
+ //# sourceMappingURL=organelle.js.map