volute 0.53.0 → 0.54.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 (147) hide show
  1. package/dist/auto-upgrade-HEVZKPTF.js +46 -0
  2. package/dist/{backup-WWXVIMYQ.js → backup-QRNX6RK7.js} +11 -11
  3. package/dist/channels-CKCY64TD.js +203 -0
  4. package/dist/{chat-WK2BLM7B.js → chat-ZUGNL5GS.js} +9 -9
  5. package/dist/{chunk-UTKUO2KY.js → chunk-3LHUXY3E.js} +2 -2
  6. package/dist/{chunk-PIZDPY4G.js → chunk-444RR3YF.js} +4 -4
  7. package/dist/{chunk-VDZ4I4ZP.js → chunk-56IT2ZXF.js} +3 -3
  8. package/dist/{chunk-OXWUEU5V.js → chunk-5U5GLZSD.js} +2 -2
  9. package/dist/{chunk-LN7XJ56R.js → chunk-7XKPG66D.js} +9 -9
  10. package/dist/{chunk-R772ET2M.js → chunk-AAPNU7GA.js} +4 -0
  11. package/dist/{chunk-7ARONCZW.js → chunk-ATZF2RNJ.js} +3 -3
  12. package/dist/{chunk-6NLW72HP.js → chunk-CKPAVQNK.js} +1 -1
  13. package/dist/{chunk-RMADJFA7.js → chunk-CLCHOXLR.js} +1 -1
  14. package/dist/{chunk-IQ7VNWH6.js → chunk-HKGYZVWN.js} +1 -1
  15. package/dist/{chunk-HASADR3E.js → chunk-LGXVGIWN.js} +66 -25
  16. package/dist/{chunk-P2TBUKLW.js → chunk-LKUGUQUW.js} +3 -3
  17. package/dist/{chunk-AP2KQMMW.js → chunk-OAYY3WF3.js} +11 -3
  18. package/dist/{chunk-UO4MZQNO.js → chunk-OGPVJ53N.js} +14 -6
  19. package/dist/{chunk-M77B73VV.js → chunk-OIOW3FPT.js} +7 -2
  20. package/dist/chunk-OT5SXF7L.js +904 -0
  21. package/dist/{chunk-EDR47UE2.js → chunk-OYF25B62.js} +1 -1
  22. package/dist/{chunk-TAWRJ24T.js → chunk-PPSN2MLC.js} +2 -2
  23. package/dist/{chunk-5DBGQBQR.js → chunk-RGHF3OK5.js} +21 -10
  24. package/dist/{chunk-RQRO6PC7.js → chunk-VDUCLVZI.js} +305 -31
  25. package/dist/{chunk-23UJGVYP.js → chunk-WHL6PKYP.js} +10 -10
  26. package/dist/{chunk-HTB7KDRU.js → chunk-WZZCA23D.js} +5 -4
  27. package/dist/{chunk-UDZLPR34.js → chunk-YSRYCS3N.js} +13 -8
  28. package/dist/{chunk-5N32EN4O.js → chunk-Z4Q6TQWL.js} +1 -1
  29. package/dist/{chunk-BDHKCLWK.js → chunk-ZNHL5KQJ.js} +1 -1
  30. package/dist/cli.js +19 -19
  31. package/dist/{clock-SH4NQC2F.js → clock-X7V3C7NL.js} +3 -3
  32. package/dist/{cloud-sync-MJVJ33LD.js → cloud-sync-52YNJRMR.js} +11 -11
  33. package/dist/{daemon-restart-BIGU4OKS.js → daemon-restart-QWHN5VMU.js} +4 -4
  34. package/dist/daemon.js +330 -746
  35. package/dist/{db-IPXRJUI7.js → db-I4EH5SLF.js} +3 -1
  36. package/dist/{delivery-manager-FDHCZ7PU.js → delivery-manager-7R2YIQ2W.js} +11 -7
  37. package/dist/{delivery-notices-65TB3CBX.js → delivery-notices-RZQ6GONP.js} +3 -3
  38. package/dist/{delivery-router-RHANNTAC.js → delivery-router-QS4NLJR6.js} +3 -1
  39. package/dist/{down-QSNEMTTX.js → down-UN2EDX2Y.js} +3 -3
  40. package/dist/{echo-text-KU6G4Z7V.js → echo-text-73CYQRL7.js} +14 -14
  41. package/dist/{exec-ABD3DYA2.js → exec-Z34TTUO3.js} +1 -1
  42. package/dist/{extensions-VACUKFA5.js → extensions-QT65JGV6.js} +21 -11
  43. package/dist/{isolation-GVV2IBAU.js → isolation-UQPDO34R.js} +1 -1
  44. package/dist/{message-delivery-TUXM3AAE.js → message-delivery-TRKT4T2P.js} +11 -11
  45. package/dist/migrate-name-placeholder-C7NCNLWF.js +37 -0
  46. package/dist/{mind-IX5AWUST.js → mind-CT6ANXK4.js} +12 -12
  47. package/dist/{mind-contacts-BKUOUJPV.js → mind-contacts-PEXCDRXA.js} +8 -8
  48. package/dist/{mind-list-KNU34SUD.js → mind-list-7KBLPLES.js} +2 -2
  49. package/dist/{mind-service-WHFBUKZE.js → mind-service-7EFIJ3ZA.js} +11 -11
  50. package/dist/{mind-sleep-D3DEYAWG.js → mind-sleep-24HV4RCA.js} +5 -5
  51. package/dist/{mind-status-BPTLQ5Z6.js → mind-status-4YW4PHMO.js} +6 -3
  52. package/dist/{package-BN5KMU54.js → package-F6S5NHY2.js} +4 -4
  53. package/dist/{prompts-HNKGCAEY.js → prompts-JECJIFPQ.js} +1 -1
  54. package/dist/{scheduler-5X5UIQOQ.js → scheduler-73Q4MQ7Y.js} +6 -6
  55. package/dist/{seed-LHZ33UB4.js → seed-DRKAP3XV.js} +1 -1
  56. package/dist/{seed-cmd-U4RVJ3UA.js → seed-cmd-6IMTO3LG.js} +2 -2
  57. package/dist/{seed-create-OQU4KNCB.js → seed-create-VNLDYRFK.js} +1 -1
  58. package/dist/{seed-readiness-WJYYRRCK.js → seed-readiness-H5TTPAQH.js} +2 -2
  59. package/dist/{seed-sprout-SFUYH2RG.js → seed-sprout-PPGYGK4G.js} +8 -8
  60. package/dist/{send-FWZAMDBN.js → send-UXUQKO4N.js} +9 -9
  61. package/dist/{service-XQPMZAGI.js → service-T4P5W6RS.js} +2 -2
  62. package/dist/{service-install-2EWD7KXD.js → service-install-R4SNYUVO.js} +3 -3
  63. package/dist/{setup-RVSGCIF3.js → setup-YUAQMDVZ.js} +4 -4
  64. package/dist/skills/dreaming/SKILL.md +2 -2
  65. package/dist/skills/dreaming/references/INSTALL.md +1 -1
  66. package/dist/skills/tending/SKILL.md +2 -0
  67. package/dist/skills/volute-mind/references/integrations.md +3 -2
  68. package/dist/skills/volute-mind/references/routing.md +18 -5
  69. package/dist/{skills-QRPU6BWY.js → skills-GMQ5UIIW.js} +2 -2
  70. package/dist/{sleep-manager-NWUCRBZB.js → sleep-manager-KHI65FEX.js} +11 -11
  71. package/dist/{spirit-I5ECLGRI.js → spirit-Q5VGFWUI.js} +5 -3
  72. package/dist/{spirit-availability-NIUUYKU2.js → spirit-availability-G5FMWKRH.js} +12 -12
  73. package/dist/{sprout-A2PMEF7Z.js → sprout-PMTRL7QN.js} +1 -1
  74. package/dist/{src-LCSOQY6Z.js → src-3FIW7DLH.js} +241 -87
  75. package/dist/src-H3UCOP6B.js +516 -0
  76. package/dist/{src-HGNE2XG4.js → src-LTTQVN2Z.js} +1 -1
  77. package/dist/{status-EYG4A3OK.js → status-LCKTTIWS.js} +5 -5
  78. package/dist/{system-events-YTOU66QI.js → system-events-Q7IVZ5T4.js} +1 -1
  79. package/dist/{systems-2DIJY4RF.js → systems-HL7SGSW7.js} +6 -6
  80. package/dist/{turn-tracker-NNHS6MAR.js → turn-tracker-HZDPMSCC.js} +2 -2
  81. package/dist/{up-UGBHH743.js → up-XZDSJVMU.js} +3 -3
  82. package/dist/{update-CIWI2JYA.js → update-ILCPVNDD.js} +2 -2
  83. package/dist/{upgrade-LIE46UBB.js → upgrade-XY2J7NWZ.js} +9 -1
  84. package/dist/{variant-cleanup-CZDLIY3F.js → variant-cleanup-QHGGY7ND.js} +6 -6
  85. package/dist/{version-notify-SJVAQLZI.js → version-notify-VNVFX6UP.js} +19 -4
  86. package/dist/web-assets/assets/{index-b7G7ZsVk.js → index-CwtxGwe_.js} +16 -16
  87. package/dist/web-assets/assets/index-DhM1yYRN.css +1 -0
  88. package/dist/web-assets/index.html +2 -2
  89. package/package.json +4 -4
  90. package/packages/extensions/intentions/dist/ui/assets/index-3FneLCKR.css +1 -0
  91. package/packages/extensions/intentions/dist/ui/assets/index-CQz4aceM.js +67 -0
  92. package/packages/extensions/{plan → intentions}/dist/ui/index.html +3 -3
  93. package/packages/extensions/intentions/skills/intention-review/SKILL.md +38 -0
  94. package/packages/extensions/intentions/skills/intentions/SKILL.md +49 -0
  95. package/packages/extensions/intentions/skills/intentions/scripts/intentions-hook.sh +45 -0
  96. package/packages/extensions/pages/skills/commons-gardening/SKILL.md +50 -0
  97. package/packages/extensions/pages/skills/pages/SKILL.md +18 -13
  98. package/templates/_base/.init/.config/prompts.json +2 -2
  99. package/templates/{pi → _base}/.init/.config/routes.json +1 -0
  100. package/templates/_base/.init/memory/dreams/.gitkeep +0 -0
  101. package/templates/_base/home/.config/routes.json +1 -1
  102. package/templates/_base/home/VOLUTE.md +1 -1
  103. package/templates/_base/src/lib/context-breakdown.ts +11 -3
  104. package/templates/_base/src/lib/routing.ts +2 -2
  105. package/templates/_base/src/lib/startup.ts +4 -2
  106. package/templates/claude/.init/CLAUDE.md +4 -0
  107. package/templates/claude/src/agent.ts +28 -12
  108. package/templates/claude/src/lib/message-channel.ts +43 -15
  109. package/templates/claude/src/lib/recover.ts +69 -0
  110. package/templates/claude/src/lib/stream-consumer.ts +23 -5
  111. package/templates/claude/volute-template.json +1 -1
  112. package/templates/codex/.init/AGENTS.md +4 -0
  113. package/templates/codex/volute-template.json +1 -1
  114. package/templates/pi/.init/MINDS.md +4 -0
  115. package/templates/pi/src/lib/mechanics-doc.ts +30 -0
  116. package/templates/pi/src/server.ts +3 -1
  117. package/templates/pi/volute-template.json +1 -1
  118. package/dist/channels-LW5ZLMSQ.js +0 -90
  119. package/dist/skills/plan-coordinator/SKILL.md +0 -60
  120. package/dist/src-UDMJZ6MH.js +0 -426
  121. package/dist/web-assets/assets/index-BHSU9r-4.css +0 -1
  122. package/packages/extensions/plan/dist/ui/assets/index-B8s8wxyt.js +0 -67
  123. package/packages/extensions/plan/dist/ui/assets/index-jIkrt-vI.css +0 -1
  124. package/packages/extensions/plan/skills/plan/SKILL.md +0 -43
  125. package/packages/extensions/plan/skills/plan/scripts/plan-hook.sh +0 -37
  126. package/templates/claude/.init/.config/routes.json +0 -11
  127. package/templates/codex/.init/.config/routes.json +0 -11
  128. package/dist/{accept-I4GVGWA3.js → accept-RSDIVJL6.js} +3 -3
  129. package/dist/{bridge-KU2IKSH3.js → bridge-YK7NTRU5.js} +3 -3
  130. package/dist/{create-C3XZACTV.js → create-M727VUDF.js} +3 -3
  131. package/dist/{env-BJGM6FBA.js → env-7AZFBPWX.js} +7 -7
  132. package/dist/{extension-B2W52WZY.js → extension-MRYJWSMV.js} +3 -3
  133. package/dist/{files-CHPNEQXN.js → files-PCKBQHAY.js} +3 -3
  134. package/dist/{list-R2GDWOEE.js → list-UQGIW7OF.js} +3 -3
  135. package/dist/{login-EZ4RRDQX.js → login-4NUS72Z5.js} +3 -3
  136. package/dist/{login-JDOAK465.js → login-57MMYILL.js} +4 -4
  137. package/dist/{logout-MWSIUOQU.js → logout-BYV433X6.js} +3 -3
  138. package/dist/{logout-K6ZGAJY5.js → logout-YFZAJRPY.js} +3 -3
  139. package/dist/{mind-history-RVF6KJ7K.js → mind-history-RZMMCHMX.js} +9 -9
  140. package/dist/{mind-wake-DCDFFQKX.js → mind-wake-LDGPRA42.js} +3 -3
  141. package/dist/{read-J5RM674N.js → read-RJ5NAW5J.js} +3 -3
  142. package/dist/{register-53LBGSDC.js → register-WNCSOTAU.js} +3 -3
  143. package/dist/{reject-K2A4UK2Y.js → reject-4NFLXKRN.js} +3 -3
  144. package/dist/{restart-R2WFNF5K.js → restart-B4GNMVF2.js} +7 -7
  145. package/dist/{skill-DXLLSUHB.js → skill-QYTBUDNQ.js} +7 -7
  146. package/dist/{start-XDOUAWN3.js → start-BDHJVOVX.js} +6 -6
  147. package/dist/{stop-O62RTEMD.js → stop-45QYSGYS.js} +7 -7
@@ -1,29 +1,29 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  spiritDir
4
- } from "./chunk-5DBGQBQR.js";
4
+ } from "./chunk-RGHF3OK5.js";
5
5
  import {
6
6
  getScheduler
7
- } from "./chunk-PIZDPY4G.js";
7
+ } from "./chunk-444RR3YF.js";
8
8
  import {
9
9
  getDeliveryManager
10
- } from "./chunk-RQRO6PC7.js";
10
+ } from "./chunk-VDUCLVZI.js";
11
11
  import {
12
12
  getMindManager,
13
13
  resolveMindToken
14
- } from "./chunk-23UJGVYP.js";
14
+ } from "./chunk-WHL6PKYP.js";
15
15
  import {
16
16
  extractTextContent,
17
17
  getRoutingConfig,
18
18
  resolveRoute,
19
19
  shouldGate
20
- } from "./chunk-UDZLPR34.js";
20
+ } from "./chunk-YSRYCS3N.js";
21
21
  import {
22
22
  hashSkillDir,
23
23
  importSkillFromDir,
24
24
  removeSharedSkill,
25
25
  sharedSkillsDir
26
- } from "./chunk-TAWRJ24T.js";
26
+ } from "./chunk-PPSN2MLC.js";
27
27
  import {
28
28
  getOrCreateMindUser,
29
29
  getOrCreateSystemUser,
@@ -38,13 +38,13 @@ import {
38
38
  pendingEventCount,
39
39
  pendingEventsLine,
40
40
  recordNotice
41
- } from "./chunk-IQ7VNWH6.js";
41
+ } from "./chunk-HKGYZVWN.js";
42
42
  import {
43
43
  publish as publish2
44
44
  } from "./chunk-LNNI3MS4.js";
45
45
  import {
46
46
  getPrompt
47
- } from "./chunk-AP2KQMMW.js";
47
+ } from "./chunk-OAYY3WF3.js";
48
48
  import {
49
49
  addMessage,
50
50
  createChannel,
@@ -70,7 +70,7 @@ import {
70
70
  import {
71
71
  isIsolationEnabled,
72
72
  mindUserName
73
- } from "./chunk-M77B73VV.js";
73
+ } from "./chunk-OIOW3FPT.js";
74
74
  import {
75
75
  missingCredentialWarning
76
76
  } from "./chunk-EGV6R6UF.js";
@@ -86,6 +86,7 @@ import {
86
86
  mindDir,
87
87
  readGlobalConfig,
88
88
  readRegistry,
89
+ resolveMindDir,
89
90
  stateDir,
90
91
  voluteHome,
91
92
  voluteSystemDir,
@@ -931,7 +932,7 @@ async function startMindFull(name) {
931
932
  }
932
933
  }
933
934
  async function recordMissingCredentialsNotice(mind, template) {
934
- const { hasUndeliveredEvent, recordNotice: recordNotice2, MIND_LEVEL_THREAD: MIND_LEVEL_THREAD2 } = await import("./system-events-YTOU66QI.js");
935
+ const { hasUndeliveredEvent, recordNotice: recordNotice2, MIND_LEVEL_THREAD: MIND_LEVEL_THREAD2 } = await import("./system-events-Q7IVZ5T4.js");
935
936
  const warning = await missingCredentialWarning(template, readMindModel(mind, template), mind);
936
937
  if (!warning) return;
937
938
  if (await hasUndeliveredEvent(mind, "no_credentials")) return;
@@ -975,7 +976,7 @@ async function wakeMind(name) {
975
976
  async function startSpiritFull(name) {
976
977
  const entry = await findMind(name);
977
978
  if (entry?.dir) {
978
- const { registerMindDir } = await import("./delivery-router-RHANNTAC.js");
979
+ const { registerMindDir } = await import("./delivery-router-QS4NLJR6.js");
979
980
  registerMindDir(name, entry.dir);
980
981
  }
981
982
  await getMindManager().startMind(name);
@@ -983,6 +984,11 @@ async function startSpiritFull(name) {
983
984
  (err) => logger_default.error(`failed to join #system for ${name}`, logger_default.errorData(err))
984
985
  );
985
986
  getScheduler().loadSchedules(name, entry?.dir ?? spiritDir());
987
+ try {
988
+ notifyExtensionsMindStart(name);
989
+ } catch (err) {
990
+ logger_default.error(`failed to notify extensions of spirit start for ${name}`, logger_default.errorData(err));
991
+ }
986
992
  publish({
987
993
  type: "mind_started",
988
994
  mind: name,
@@ -990,6 +996,7 @@ async function startSpiritFull(name) {
990
996
  }).catch((err) => logger_default.error("failed to publish spirit_started activity", logger_default.errorData(err)));
991
997
  }
992
998
  async function stopSpiritFull(name) {
999
+ notifyExtensionsMindStop(name);
993
1000
  markIdle(name);
994
1001
  getScheduler().unloadSchedules(name);
995
1002
  await getMindManager().stopMind(name);
@@ -1452,7 +1459,7 @@ var SleepManager = class {
1452
1459
  * the real delivery stack. Returns whether the batch was handed off to the mind.
1453
1460
  */
1454
1461
  async deliverQueuedBatch(name, payloads) {
1455
- const { deliverBatch: deliverBatch2 } = await import("./message-delivery-TUXM3AAE.js");
1462
+ const { deliverBatch: deliverBatch2 } = await import("./message-delivery-TRKT4T2P.js");
1456
1463
  return deliverBatch2(name, payloads);
1457
1464
  }
1458
1465
  // --- Wake failure handling (#419) ---
@@ -2066,7 +2073,7 @@ async function deliverBatch(mindName, payloads) {
2066
2073
  const res = await fetch(`http://127.0.0.1:${entry.port}/message`, {
2067
2074
  method: "POST",
2068
2075
  headers: { "Content-Type": "application/json" },
2069
- body: JSON.stringify({ session, batch: { channels } })
2076
+ body: JSON.stringify({ session, batch: { channels }, interrupt: false })
2070
2077
  });
2071
2078
  return res.ok;
2072
2079
  } catch (err) {
@@ -2286,7 +2293,7 @@ async function openExtensionDb(_id, dataDir) {
2286
2293
  const Database = await getLibsqlDatabase();
2287
2294
  return new Database(dbPath);
2288
2295
  }
2289
- async function buildContext(manifest, dataDir, authMw) {
2296
+ async function buildExtensionContext(manifest, dataDir, authMw) {
2290
2297
  let db = null;
2291
2298
  if (manifest.initDb) {
2292
2299
  const realDb = await openExtensionDb(manifest.id, dataDir);
@@ -2319,9 +2326,11 @@ async function buildContext(manifest, dataDir, authMw) {
2319
2326
  (err) => logger_default.error(`extension ${manifest.id}: failed to publish activity`, logger_default.errorData(err))
2320
2327
  );
2321
2328
  },
2322
- getMindDir: (name) => {
2329
+ // Registry-backed, not path-convention: the spirit lives under the system dir
2330
+ // and variants live in worktrees, so mindDir(name) alone resolves neither.
2331
+ getMindDir: async (name) => {
2323
2332
  try {
2324
- const dir = mindDir(name);
2333
+ const dir = await resolveMindDir(name);
2325
2334
  return existsSync5(dir) ? dir : null;
2326
2335
  } catch (err) {
2327
2336
  logger_default.warn(
@@ -2335,8 +2344,7 @@ async function buildContext(manifest, dataDir, authMw) {
2335
2344
  announceToSystem: (text) => announceToSystem(text),
2336
2345
  recordNotice: async (mindName, text) => {
2337
2346
  try {
2338
- const user = await getUserByUsername(mindName);
2339
- if (user?.user_type !== "mind") return;
2347
+ if (!await findMind(mindName)) return;
2340
2348
  await recordNotice({
2341
2349
  mind: mindName,
2342
2350
  thread: MIND_LEVEL_THREAD,
@@ -2353,6 +2361,10 @@ async function buildContext(manifest, dataDir, authMw) {
2353
2361
  },
2354
2362
  isIsolationEnabled,
2355
2363
  getMindUser: mindUserName,
2364
+ // Delegate to the daemon's single source of truth, which falls back to "volute"
2365
+ // on installs that predate spirit naming. Reading setup.spiritName directly
2366
+ // returned null on those systems, so extension spirit paths no-opped forever.
2367
+ getSpiritName: () => getSpiritName(),
2356
2368
  dataDir
2357
2369
  };
2358
2370
  }
@@ -2363,7 +2375,7 @@ async function loadExtension(manifest, app, authMw) {
2363
2375
  }
2364
2376
  const dataDir = extensionDataDir(manifest.id);
2365
2377
  mkdirSync3(dataDir, { recursive: true });
2366
- const context = await buildContext(manifest, dataDir, authMw);
2378
+ const context = await buildExtensionContext(manifest, dataDir, authMw);
2367
2379
  const routesApp = manifest.routes(context);
2368
2380
  const extApiPath = `/api/ext/${manifest.id}`;
2369
2381
  app.use(extApiPath, authMw);
@@ -2529,9 +2541,9 @@ function resolveSkillsDir(manifest) {
2529
2541
  }
2530
2542
  async function discoverBuiltinExtensions(disabledIds) {
2531
2543
  const builtins = [
2532
- { id: "notes", load: async () => (await import("./src-HGNE2XG4.js")).default },
2533
- { id: "pages", load: async () => (await import("./src-LCSOQY6Z.js")).default },
2534
- { id: "plan", load: async () => (await import("./src-UDMJZ6MH.js")).default }
2544
+ { id: "notes", load: async () => (await import("./src-LTTQVN2Z.js")).default },
2545
+ { id: "pages", load: async () => (await import("./src-3FIW7DLH.js")).default },
2546
+ { id: "intentions", load: async () => (await import("./src-H3UCOP6B.js")).default }
2535
2547
  ];
2536
2548
  const results = [];
2537
2549
  for (const { id, load } of builtins) {
@@ -2780,7 +2792,7 @@ async function installNpmExtension(pkg) {
2780
2792
  throw new Error(`Extension "${pkg}" is already installed`);
2781
2793
  }
2782
2794
  const dir = ensureExtensionsNpmDir();
2783
- const { exec } = await import("./exec-ABD3DYA2.js");
2795
+ const { exec } = await import("./exec-Z34TTUO3.js");
2784
2796
  try {
2785
2797
  await exec("npm", ["install", "--ignore-scripts", pkg], { cwd: dir });
2786
2798
  } catch (err) {
@@ -2801,7 +2813,7 @@ async function uninstallNpmExtension(pkg) {
2801
2813
  packages.splice(idx, 1);
2802
2814
  writeExtensionsConfig(packages);
2803
2815
  try {
2804
- const { exec } = await import("./exec-ABD3DYA2.js");
2816
+ const { exec } = await import("./exec-Z34TTUO3.js");
2805
2817
  await exec("npm", ["uninstall", "--ignore-scripts", pkg], { cwd: extensionsNpmDir() });
2806
2818
  } catch (err) {
2807
2819
  logger_default.warn(
@@ -2839,9 +2851,10 @@ function getExtensionStandardSkills() {
2839
2851
  if (!manifest.standardSkill) continue;
2840
2852
  const dir = resolveSkillsDir(manifest);
2841
2853
  if (!dir) continue;
2854
+ const spiritSet = new Set(manifest.spiritSkills ?? []);
2842
2855
  try {
2843
2856
  for (const entry of readdirSync2(dir, { withFileTypes: true })) {
2844
- if (entry.isDirectory()) skills.push(entry.name);
2857
+ if (entry.isDirectory() && !spiritSet.has(entry.name)) skills.push(entry.name);
2845
2858
  }
2846
2859
  } catch (err) {
2847
2860
  logger_default.warn(`failed to read skills dir for extension ${manifest.id}`, logger_default.errorData(err));
@@ -2849,6 +2862,13 @@ function getExtensionStandardSkills() {
2849
2862
  }
2850
2863
  return skills;
2851
2864
  }
2865
+ function getExtensionSpiritSkills() {
2866
+ const skills = [];
2867
+ for (const { manifest } of loaded) {
2868
+ for (const name of manifest.spiritSkills ?? []) skills.push(name);
2869
+ }
2870
+ return skills;
2871
+ }
2852
2872
  function notifyExtensionsDaemonStart() {
2853
2873
  for (const { manifest, context } of loaded) {
2854
2874
  try {
@@ -2858,6 +2878,16 @@ function notifyExtensionsDaemonStart() {
2858
2878
  }
2859
2879
  }
2860
2880
  }
2881
+ async function notifyExtensionsSpiritReady() {
2882
+ for (const { manifest, context } of loaded) {
2883
+ if (!manifest.onSpiritReady) continue;
2884
+ try {
2885
+ await manifest.onSpiritReady(context);
2886
+ } catch (err) {
2887
+ logger_default.error(`extension ${manifest.id}: onSpiritReady failed`, logger_default.errorData(err));
2888
+ }
2889
+ }
2890
+ }
2861
2891
  function notifyExtensionsDaemonStop() {
2862
2892
  for (const { manifest, context } of loaded) {
2863
2893
  try {
@@ -2893,6 +2923,12 @@ function notifyExtensionsMindStop(mindName) {
2893
2923
  }
2894
2924
  }
2895
2925
  }
2926
+ function _registerExtensionForTest(manifest) {
2927
+ loaded.push({ manifest, context: {} });
2928
+ }
2929
+ function _clearLoadedExtensionsForTest() {
2930
+ loaded.length = 0;
2931
+ }
2896
2932
 
2897
2933
  export {
2898
2934
  issueApiToken,
@@ -2914,6 +2950,7 @@ export {
2914
2950
  deleteSystemsConfig,
2915
2951
  enrichActivityMetadata,
2916
2952
  parseCommandArgs,
2953
+ buildExtensionContext,
2917
2954
  loadAllExtensions,
2918
2955
  getLoadedExtensions,
2919
2956
  getAllDiscoveredExtensions,
@@ -2922,10 +2959,14 @@ export {
2922
2959
  installNpmExtension,
2923
2960
  uninstallNpmExtension,
2924
2961
  getExtensionStandardSkills,
2962
+ getExtensionSpiritSkills,
2925
2963
  notifyExtensionsDaemonStart,
2964
+ notifyExtensionsSpiritReady,
2926
2965
  notifyExtensionsDaemonStop,
2927
2966
  notifyExtensionsMindStart,
2928
2967
  notifyExtensionsMindStop,
2968
+ _registerExtensionForTest,
2969
+ _clearLoadedExtensionsForTest,
2929
2970
  initMailPoller,
2930
2971
  initTokenBudget,
2931
2972
  getTokenBudget,
@@ -2,14 +2,14 @@
2
2
  import {
3
3
  getSleepManagerIfReady,
4
4
  startSpiritFull
5
- } from "./chunk-HASADR3E.js";
5
+ } from "./chunk-LGXVGIWN.js";
6
6
  import {
7
7
  spiritCreationInProgress
8
- } from "./chunk-5DBGQBQR.js";
8
+ } from "./chunk-RGHF3OK5.js";
9
9
  import {
10
10
  MindStartupError,
11
11
  tryGetMindManager
12
- } from "./chunk-23UJGVYP.js";
12
+ } from "./chunk-WHL6PKYP.js";
13
13
  import {
14
14
  logger_default
15
15
  } from "./chunk-QUSMYGNQ.js";
@@ -120,7 +120,10 @@ Your MEMORY.md is currently \${memory_size}. It is loaded into every request, so
120
120
  category: "mind"
121
121
  },
122
122
  reply_instructions: {
123
- content: 'To reply to this message, use: volute chat send ${channel} "your message"',
123
+ // Quoted for the same reason as channel_invite: `${channel}` is `#garden` for any
124
+ // Volute channel, and unquoted that is a shell comment. This hint is injected on the
125
+ // first message of every session, so it is the command minds copy most often.
126
+ content: 'To reply to this message, use: volute chat send "${channel}" "your message"',
124
127
  description: "First-message reply hint injected via hook",
125
128
  variables: ["channel"],
126
129
  category: "mind"
@@ -132,13 +135,18 @@ Your MEMORY.md is currently \${memory_size}. It is loaded into every request, so
132
135
  category: "mind"
133
136
  },
134
137
  channel_invite: {
138
+ // Channel slugs are quoted in every command below because they routinely contain
139
+ // characters a shell eats — `#` starts a comment, so an unquoted `#garden` is stripped
140
+ // before the CLI ever sees it and the command fails with "Missing required argument".
141
+ // A mind pastes these verbatim, so they have to work verbatim.
135
142
  content: `[New channel: \${channel}]
136
143
  \${heldLine}
137
144
  Sender: \${sender}
138
145
  \${details}Preview: \${preview}
139
146
 
140
- To start hearing this channel, add a routing rule for "\${channel}" to .config/routes.json \u2014 only the \${limit} most recent held messages are replayed; older ones stay in the channel history (volute chat read \${channel}).
141
- To stop hearing about it, decline the channel: volute chat channels decline \${channel}`,
147
+ To read what's being held: volute chat channels peek "\${channel}"
148
+ To start hearing this channel: volute chat channels accept "\${channel}" \u2014 routes it and delivers the \${limit} most recent held messages; older ones stay readable via peek.
149
+ To stop hearing about it: volute chat channels decline "\${channel}"`,
142
150
  description: "Notification sent to a mind when a message arrives on an unrouted (gated) channel",
143
151
  variables: ["channel", "heldLine", "sender", "details", "preview", "limit"],
144
152
  category: "mind"
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  getMindManager
4
- } from "./chunk-23UJGVYP.js";
4
+ } from "./chunk-WHL6PKYP.js";
5
5
  import {
6
6
  chownMindDir,
7
7
  gitExec
8
- } from "./chunk-M77B73VV.js";
8
+ } from "./chunk-OIOW3FPT.js";
9
9
  import {
10
10
  logger_default
11
11
  } from "./chunk-QUSMYGNQ.js";
@@ -27,9 +27,12 @@ async function cleanupVariant(variantName, baseName, projectRoot, variantPath, o
27
27
  logger_default.warn(`failed to stop variant ${variantName}`, logger_default.errorData(err));
28
28
  }
29
29
  }
30
- const { findMind } = await import("./registry-TRC2HJXR.js");
31
- const variantEntry = await findMind(variantName);
32
- const branchName = variantEntry?.branch ?? variantName;
30
+ let branchName = opts?.branch;
31
+ if (!branchName) {
32
+ const { findMind } = await import("./registry-TRC2HJXR.js");
33
+ const variantEntry = await findMind(variantName);
34
+ branchName = variantEntry?.branch ?? variantName;
35
+ }
33
36
  if (existsSync(variantPath)) {
34
37
  try {
35
38
  await gitExec(["worktree", "remove", "--force", variantPath], { cwd: projectRoot });
@@ -45,7 +48,12 @@ async function cleanupVariant(variantName, baseName, projectRoot, variantPath, o
45
48
  try {
46
49
  await gitExec(["branch", "-D", branchName], { cwd: projectRoot });
47
50
  } catch (err) {
48
- logger_default.warn(`failed to delete branch ${branchName} for ${variantName}`, logger_default.errorData(err));
51
+ const stderr = err?.stderr ?? "";
52
+ if (stderr.includes("not found")) {
53
+ logger_default.info(`branch ${branchName} already deleted for ${variantName}`);
54
+ } else {
55
+ logger_default.warn(`failed to delete branch ${branchName} for ${variantName}`, logger_default.errorData(err));
56
+ }
49
57
  }
50
58
  try {
51
59
  await removeMind(variantName);
@@ -15,10 +15,10 @@ import { execFile as execFileCb, execFileSync, spawn } from "child_process";
15
15
  async function exec(cmd, args, options) {
16
16
  const [wrappedCmd, wrappedArgs] = options?.mindName ? await wrapForIsolation(cmd, args, options.mindName) : [cmd, args];
17
17
  return new Promise((resolve2, reject) => {
18
- execFileCb(
18
+ const child = execFileCb(
19
19
  wrappedCmd,
20
20
  wrappedArgs,
21
- { cwd: options?.cwd, env: options?.env },
21
+ { cwd: options?.cwd, env: options?.env, maxBuffer: options?.maxBuffer },
22
22
  (err, stdout, stderr) => {
23
23
  if (err) {
24
24
  err.stderr = stderr;
@@ -29,6 +29,11 @@ async function exec(cmd, args, options) {
29
29
  }
30
30
  }
31
31
  );
32
+ if (options?.stdin !== void 0 && child.stdin) {
33
+ child.stdin.on("error", () => {
34
+ });
35
+ child.stdin.end(options.stdin);
36
+ }
32
37
  });
33
38
  }
34
39
  function gitExec(args, options) {