volute 0.39.0 → 0.40.1

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 (171) hide show
  1. package/dist/{accept-AHAOUFBK.js → accept-IVHOHXQS.js} +1 -1
  2. package/dist/{activity-events-CR4RIHJT.js → activity-events-DS4QRWBY.js} +4 -4
  3. package/dist/{ai-service-PL7GKFQC.js → ai-service-AUWIPWLP.js} +9 -5
  4. package/dist/{api-client-LC5YRA32.js → api-client-NFM6GX2N.js} +1 -1
  5. package/dist/{archive-UEWIFRAR.js → archive-UDHFZN4G.js} +4 -4
  6. package/dist/{auth-OJ2YW3NR.js → auth-X6ZITZ7D.js} +5 -5
  7. package/dist/{bridge-F3ZJEKDN.js → bridge-MSZC4HUT.js} +1 -1
  8. package/dist/{chat-RQ5NV5EA.js → chat-JD3TJINV.js} +9 -9
  9. package/dist/{chunk-CJ26DXZL.js → chunk-2HZRJ35V.js} +17 -1
  10. package/dist/{chunk-QDWPM73D.js → chunk-3Y7UDHO7.js} +3 -3
  11. package/dist/{chunk-6V2U3GFQ.js → chunk-4UMXOMZ6.js} +2 -2
  12. package/dist/{chunk-7OS7QFDF.js → chunk-5EVW4KOD.js} +3 -3
  13. package/dist/{chunk-6FSSMAXU.js → chunk-6SYHHEQ7.js} +1 -1
  14. package/dist/{chunk-OEXOFBW5.js → chunk-AEAZRTEV.js} +1 -1
  15. package/dist/{chunk-DZ4LYU6J.js → chunk-ANSTTB55.js} +1 -1
  16. package/dist/{chunk-QDWP435B.js → chunk-B4WPXYPI.js} +1 -1
  17. package/dist/{chunk-CHCISTH7.js → chunk-BLAXARBZ.js} +511 -335
  18. package/dist/{chunk-TCNCRDB5.js → chunk-DNU6XLE2.js} +1 -1
  19. package/dist/{chunk-7W3UWCQD.js → chunk-E25SO3QW.js} +3 -3
  20. package/dist/{chunk-IA2SZSLB.js → chunk-I57LLNAQ.js} +1 -1
  21. package/dist/{chunk-WUH54G5S.js → chunk-ITH5ZLZJ.js} +1 -1
  22. package/dist/{chunk-QJ4GU5XT.js → chunk-JYQQDI3M.js} +2 -2
  23. package/dist/{chunk-EVKGKQ2P.js → chunk-KMHC36CA.js} +1 -7
  24. package/dist/chunk-LMMQX4CK.js +49 -0
  25. package/dist/{chunk-4BO4I7UF.js → chunk-LZ6CQYRO.js} +7 -7
  26. package/dist/{chunk-RAVQXKAG.js → chunk-MNUXL4XN.js} +67 -3
  27. package/dist/{chunk-OS5MRIEP.js → chunk-NERYUSER.js} +1 -1
  28. package/dist/{chunk-XOSAY7PY.js → chunk-PKGA23XM.js} +1 -1
  29. package/dist/{chunk-IBOU6Q3Y.js → chunk-QV7P3TYO.js} +5 -5
  30. package/dist/{chunk-7GZB3ZJP.js → chunk-QYI65HCL.js} +2 -2
  31. package/dist/{chunk-B7MWXFAH.js → chunk-S4SXBQ7F.js} +15 -1
  32. package/dist/{chunk-X7GM2OPM.js → chunk-SVXOBKQY.js} +2 -2
  33. package/dist/{chunk-Y5UC6G3O.js → chunk-TOANT6FD.js} +2 -2
  34. package/dist/{chunk-TT4WT6Y7.js → chunk-YHWGN4FX.js} +1 -1
  35. package/dist/{chunk-Y4W3OS7Q.js → chunk-YTNBO7S6.js} +8 -8
  36. package/dist/cli.js +24 -24
  37. package/dist/{clock-BMLJ2TR6.js → clock-YLDJUKBW.js} +3 -3
  38. package/dist/{cloud-sync-NQCWJLYI.js → cloud-sync-MAQCJEFJ.js} +17 -17
  39. package/dist/{config-QB7W3Z7P.js → config-PO4EZLIK.js} +4 -4
  40. package/dist/connectors/discord-bridge.js +1 -1
  41. package/dist/connectors/slack-bridge.js +1 -1
  42. package/dist/connectors/telegram-bridge.js +1 -1
  43. package/dist/{conversations-EUXHBWJD.js → conversations-AYLXPOGC.js} +5 -5
  44. package/dist/{create-3QH7DVLC.js → create-T4UJ64WB.js} +4 -4
  45. package/dist/{create-C3BBFYV7.js → create-ZMTGINV3.js} +1 -1
  46. package/dist/{daemon-client-2MIPKY3E.js → daemon-client-H6MEKK2W.js} +1 -1
  47. package/dist/{daemon-restart-UEB3PLKB.js → daemon-restart-LP2DR24J.js} +9 -9
  48. package/dist/daemon.js +424 -294
  49. package/dist/{db-IJL6B26S.js → db-FSYOTHSN.js} +1 -1
  50. package/dist/db-ZEPKEJJD.js +9 -0
  51. package/dist/{delete-NLXES2C7.js → delete-TXVOH2OC.js} +3 -3
  52. package/dist/delivery-manager-VNHLQTTO.js +30 -0
  53. package/dist/{delivery-router-EUW4RIWG.js → delivery-router-T6CYTS3M.js} +4 -4
  54. package/dist/down-6DYO2QV7.js +17 -0
  55. package/dist/echo-text-QFCH3H6G.js +31 -0
  56. package/dist/{env-IQ6Q2333.js → env-77IOWG4U.js} +1 -1
  57. package/dist/exec-5DGKQ5V3.js +17 -0
  58. package/dist/{export-JDAK3THX.js → export-TZKOCSCE.js} +6 -6
  59. package/dist/{extension-LZYHBNLV.js → extension-35NB6GHW.js} +1 -1
  60. package/dist/{extensions-V7TXKAKM.js → extensions-LQX2N7TI.js} +17 -17
  61. package/dist/{files-K5XV23H6.js → files-WNNVJWJB.js} +4 -4
  62. package/dist/{import-GZ5IYVV7.js → import-BNNFCQ7U.js} +11 -11
  63. package/dist/{isolation-FPF4UTH2.js → isolation-4Q4P7HRV.js} +4 -4
  64. package/dist/{join-6SZCA5FX.js → join-E2EJEXZO.js} +3 -3
  65. package/dist/{list-KHJZJPEJ.js → list-BYCEDASJ.js} +1 -1
  66. package/dist/{login-GYTH67ES.js → login-JNSIYFVH.js} +1 -1
  67. package/dist/{login-BGF5JY3I.js → login-Y6UAYQVO.js} +3 -3
  68. package/dist/{logout-YHQLOFLR.js → logout-M6FI62BL.js} +1 -1
  69. package/dist/{logout-GQHI6KH6.js → logout-VHGWVH63.js} +3 -3
  70. package/dist/message-delivery-7BCYJLH2.js +40 -0
  71. package/dist/{mind-QMJIWWWW.js → mind-ELPD6VBP.js} +19 -19
  72. package/dist/{mind-activity-tracker-Z3KBQTVU.js → mind-activity-tracker-XFRVGXFS.js} +5 -5
  73. package/dist/{mind-history-WHCNZ6I5.js → mind-history-W4JPTPTV.js} +1 -1
  74. package/dist/{mind-list-H3HC2ZRG.js → mind-list-QROHCLGT.js} +1 -1
  75. package/dist/mind-manager-EDCLALBF.js +30 -0
  76. package/dist/{mind-profile-7VYRJGFZ.js → mind-profile-3LNKXNPA.js} +2 -2
  77. package/dist/mind-service-WTOFVNLW.js +36 -0
  78. package/dist/{mind-sleep-ZL5ZXFTM.js → mind-sleep-PVCMLTAH.js} +1 -1
  79. package/dist/{mind-status-ZWULKOUO.js → mind-status-RAJFLRCN.js} +1 -1
  80. package/dist/{mind-wake-HK5ORGUK.js → mind-wake-HZUOVE7J.js} +1 -1
  81. package/dist/{package-IR4ZEQA2.js → package-GO3PHHUF.js} +28 -27
  82. package/dist/{read-CP7MYMJQ.js → read-OOINVR2H.js} +1 -1
  83. package/dist/{read-stdin-4B5UYPPM.js → read-stdin-P6HCZUAA.js} +1 -1
  84. package/dist/{register-XOBFEMI4.js → register-ISOH34RF.js} +1 -1
  85. package/dist/{registry-LWEOAO53.js → registry-GV35XFT7.js} +3 -3
  86. package/dist/{reject-43AGXB6B.js → reject-ZBWP62X5.js} +1 -1
  87. package/dist/{restart-O5QIYQJT.js → restart-QBJV5455.js} +1 -1
  88. package/dist/{sandbox-KUAVC37A.js → sandbox-5R32QKWQ.js} +5 -5
  89. package/dist/scheduler-GQBDCGLU.js +30 -0
  90. package/dist/{schema-K575EBPE.js → schema-LYQUDL7G.js} +4 -2
  91. package/dist/{seed-GPBERPXJ.js → seed-MO2C7CWC.js} +2 -2
  92. package/dist/{seed-check-HZPVFJKZ.js → seed-check-3WXXRHTC.js} +2 -2
  93. package/dist/{seed-cmd-PWF4FYFG.js → seed-cmd-ETIF4WD6.js} +4 -4
  94. package/dist/{seed-create-NTIICZVB.js → seed-create-BPQC2IOR.js} +6 -6
  95. package/dist/{seed-sprout-PRPFKGJF.js → seed-sprout-TX72K33T.js} +11 -11
  96. package/dist/{send-IMHDYJHW.js → send-WO3CVG73.js} +4 -4
  97. package/dist/{service-DWBRRFXS.js → service-WMV6R6VJ.js} +6 -6
  98. package/dist/service-install-W3FQXJKB.js +17 -0
  99. package/dist/{setup-IFYS3TLS.js → setup-OTEC3ZPM.js} +4 -4
  100. package/dist/{setup-4CWQ53VV.js → setup-ZTW4FVMQ.js} +9 -9
  101. package/dist/{skill-ICN6Y2ZF.js → skill-QHACP7EU.js} +1 -1
  102. package/dist/{skills-FJ5W73AV.js → skills-JEWMHIIE.js} +7 -7
  103. package/dist/sleep-manager-5277OBHI.js +34 -0
  104. package/dist/{spirit-U6MDQLYT.js → spirit-NVXWSVKM.js} +9 -9
  105. package/dist/{split-5YBEQTBF.js → split-7AO4IO42.js} +3 -3
  106. package/dist/{sprout-ABX2G76C.js → sprout-NFJZKYEW.js} +2 -2
  107. package/dist/{src-OYWRPLC6.js → src-2IMB3V27.js} +1 -1
  108. package/dist/src-OS75U2O7.js +241910 -0
  109. package/dist/{src-O4PRLMKM.js → src-XNUXP2FP.js} +1 -1
  110. package/dist/{start-LMXXRR3X.js → start-2U57BBK7.js} +1 -1
  111. package/dist/{status-C6BBWNU3.js → status-BRYYYO7C.js} +7 -7
  112. package/dist/{stop-TWDKVEUX.js → stop-QILBDUAR.js} +1 -1
  113. package/dist/system-chat-LQSJVY55.js +34 -0
  114. package/dist/{systems-Y2WZV2K4.js → systems-3XTAHA35.js} +4 -4
  115. package/dist/{tailscale-2KKXDMNK.js → tailscale-X4HRKRTW.js} +3 -3
  116. package/dist/{template-hash-SSIBEEYK.js → template-hash-DMHSLRDX.js} +1 -1
  117. package/dist/up-4H7TL6WB.js +19 -0
  118. package/dist/{update-V26PT4VN.js → update-OY2JYXYK.js} +61 -14
  119. package/dist/update-check-IUB7QHAF.js +24 -0
  120. package/dist/{upgrade-RXFZR5FI.js → upgrade-25J6AE47.js} +3 -3
  121. package/dist/{variant-HHDTW74J.js → variant-5LQOPCQA.js} +1 -1
  122. package/dist/{version-notify-ZBMY2TEX.js → version-notify-N37N2C4G.js} +18 -18
  123. package/dist/{volute-config-TS62GS6A.js → volute-config-7FO6QKWU.js} +1 -1
  124. package/dist/web-assets/assets/index-DaUp9iE2.js +79 -0
  125. package/dist/web-assets/assets/index-DqHSW9Fr.css +1 -0
  126. package/dist/web-assets/index.html +2 -2
  127. package/drizzle/0009_mind_notices.sql +14 -0
  128. package/drizzle/meta/_journal.json +7 -0
  129. package/package.json +27 -26
  130. package/packages/extensions/notes/dist/ui/assets/index-BbgANhFm.js +67 -0
  131. package/packages/extensions/notes/dist/ui/index.html +1 -1
  132. package/packages/extensions/pages/dist/ui/assets/index-Dxk-DYrf.js +2 -0
  133. package/packages/extensions/pages/dist/ui/index.html +1 -1
  134. package/packages/extensions/plan/dist/ui/assets/index-Cfzs-0G9.js +67 -0
  135. package/packages/extensions/plan/dist/ui/index.html +1 -1
  136. package/templates/_base/.init/.local/hooks/pre-prompt/notices.ts +46 -0
  137. package/templates/_base/src/lib/context-breakdown.ts +281 -126
  138. package/templates/_base/src/lib/daemon-client.ts +5 -2
  139. package/templates/_base/src/lib/transparency.ts +5 -3
  140. package/templates/_base/src/lib/volute-server.ts +23 -0
  141. package/templates/claude/package.json.tmpl +5 -5
  142. package/templates/claude/src/agent.ts +54 -16
  143. package/templates/claude/src/server.ts +1 -0
  144. package/templates/codex/package.json.tmpl +5 -5
  145. package/templates/codex/src/agent.ts +45 -12
  146. package/templates/codex/src/server.ts +1 -0
  147. package/templates/pi/package.json.tmpl +6 -6
  148. package/templates/pi/src/agent.ts +74 -13
  149. package/templates/pi/src/lib/subagents.ts +19 -17
  150. package/templates/pi/src/server.ts +1 -0
  151. package/dist/chunk-K3NQKI34.js +0 -10
  152. package/dist/db-32YFKMFL.js +0 -9
  153. package/dist/delivery-manager-N34XKUGL.js +0 -30
  154. package/dist/down-TCEBGTMY.js +0 -17
  155. package/dist/echo-text-N3BC3WNX.js +0 -31
  156. package/dist/exec-2MFHTGOS.js +0 -17
  157. package/dist/message-delivery-X7MKA3TA.js +0 -40
  158. package/dist/mind-manager-7GWTTKWD.js +0 -30
  159. package/dist/mind-service-MO2XJKJN.js +0 -36
  160. package/dist/scheduler-CGUPAEBA.js +0 -30
  161. package/dist/service-install-KEBDNZVE.js +0 -17
  162. package/dist/sleep-manager-6QRMUNHK.js +0 -34
  163. package/dist/src-6Z4XYDH5.js +0 -2164
  164. package/dist/system-chat-D343HYGR.js +0 -34
  165. package/dist/up-ZU6ILVMA.js +0 -19
  166. package/dist/update-check-XRERV36Y.js +0 -18
  167. package/dist/web-assets/assets/index-B1zwqwt8.css +0 -1
  168. package/dist/web-assets/assets/index-D1guJF1i.js +0 -73
  169. package/packages/extensions/notes/dist/ui/assets/index-Dsua7alJ.js +0 -61
  170. package/packages/extensions/pages/dist/ui/assets/index-BM7gRdg7.js +0 -2
  171. package/packages/extensions/plan/dist/ui/assets/index-DWiqtzBv.js +0 -61
@@ -120,12 +120,17 @@ export type ContextInfo = {
120
120
  systemPrompt: number;
121
121
  };
122
122
 
123
+ export type { ContextBlock, ContextMessage, ContextMessages } from "./context-breakdown.js";
124
+
125
+ import type { ContextMessages } from "./context-breakdown.js";
126
+
123
127
  export function createVoluteServer(options: {
124
128
  router: Router;
125
129
  port: number;
126
130
  name: string;
127
131
  version: string;
128
132
  getContextInfo?: () => ContextInfo | Promise<ContextInfo>;
133
+ getContextMessages?: () => ContextMessages | Promise<ContextMessages>;
129
134
  }): Server {
130
135
  const { router, port, name, version } = options;
131
136
 
@@ -162,6 +167,24 @@ export function createVoluteServer(options: {
162
167
  return;
163
168
  }
164
169
 
170
+ if (req.method === "GET" && url.pathname === "/context/messages") {
171
+ if (!options.getContextMessages) {
172
+ res.writeHead(404);
173
+ res.end("Not Found");
174
+ return;
175
+ }
176
+ try {
177
+ const messages = await options.getContextMessages();
178
+ res.writeHead(200, { "Content-Type": "application/json" });
179
+ res.end(JSON.stringify(messages));
180
+ } catch (err) {
181
+ log("server", "error in /context/messages:", err);
182
+ res.writeHead(500);
183
+ res.end("Internal Server Error");
184
+ }
185
+ return;
186
+ }
187
+
165
188
  if (req.method === "POST" && url.pathname === "/message") {
166
189
  try {
167
190
  const body = JSON.parse(await readBody(req)) as VoluteRequest;
@@ -9,13 +9,13 @@
9
9
  "typecheck": "tsc --noEmit"
10
10
  },
11
11
  "dependencies": {
12
- "@anthropic-ai/claude-agent-sdk": "^0.2.89",
12
+ "@anthropic-ai/claude-agent-sdk": "^0.3.185",
13
13
  "@anthropic-ai/tokenizer": "^0.0.4",
14
- "tsx": "^4.21.0"
14
+ "tsx": "^4.22.4"
15
15
  },
16
16
  "devDependencies": {
17
- "@biomejs/biome": "2.4.10",
18
- "@types/node": "^25.5.0",
19
- "typescript": "^6.0.2"
17
+ "@biomejs/biome": "2.5.0",
18
+ "@types/node": "^26.0.0",
19
+ "typescript": "^6.0.3"
20
20
  }
21
21
  }
@@ -8,7 +8,9 @@ import {
8
8
  countSkillDescriptionTokens,
9
9
  countSystemPromptTokens,
10
10
  findClaudeSessionFile,
11
- parseClaudeSessionJSONL,
11
+ processClaudeSession,
12
+ readSdkInstructions,
13
+ readSkillDescriptions,
12
14
  } from "./lib/context-breakdown.js";
13
15
  import { daemonEmit } from "./lib/daemon-client.js";
14
16
  import { runHooks } from "./lib/hook-loader.js";
@@ -30,7 +32,7 @@ import type {
30
32
  VoluteContentPart,
31
33
  VoluteEvent,
32
34
  } from "./lib/types.js";
33
- import type { ContextInfo } from "./lib/volute-server.js";
35
+ import type { ContextInfo, ContextMessages } from "./lib/volute-server.js";
34
36
 
35
37
  type Session = {
36
38
  name: string;
@@ -60,6 +62,7 @@ export function createMind(options: {
60
62
  resolve: HandlerResolver;
61
63
  waitForCommits: () => Promise<void>;
62
64
  getContextInfo: () => ContextInfo;
65
+ getContextMessages: () => ContextMessages;
63
66
  } {
64
67
  const autoCommit = createAutoCommitHook(options.cwd);
65
68
  const identityReload = createIdentityReloadHook(options.cwd);
@@ -369,6 +372,17 @@ export function createMind(options: {
369
372
  });
370
373
  }
371
374
 
375
+ /**
376
+ * Tell the daemon this turn failed so it can record a notice for the mind's next
377
+ * successful turn. Awaited before emitDone so the daemon flags the session errored
378
+ * (and thus won't mark notices delivered) before the done arrives.
379
+ */
380
+ async function emitError(err: unknown) {
381
+ await daemonEmit({ type: "error", session: session.name, content: String(err) }).catch(
382
+ (e) => log("mind", `session "${session.name}": failed to emit error to daemon:`, e),
383
+ );
384
+ }
385
+
372
386
  async function runStream(resume?: string) {
373
387
  const q = createStream(session, streamAbort, preCompact.hook, resume);
374
388
  session.currentQuery = q;
@@ -428,11 +442,13 @@ export function createMind(options: {
428
442
  await runStream();
429
443
  } catch (retryErr) {
430
444
  log("mind", `session "${session.name}": stream consumer error:`, retryErr);
445
+ await emitError(retryErr);
431
446
  emitDone();
432
447
  sessions.delete(session.name);
433
448
  }
434
449
  } else {
435
450
  log("mind", `session "${session.name}": stream consumer error:`, err);
451
+ await emitError(err);
436
452
  emitDone();
437
453
  sessions.delete(session.name);
438
454
  }
@@ -543,26 +559,24 @@ export function createMind(options: {
543
559
  const claudeMdTokens = countSdkInstructionTokens(options.cwd);
544
560
  const skillDescTokens = countSkillDescriptionTokens([resolvePath(options.cwd, ".claude/skills")]);
545
561
 
562
+ function processSession(sessionName: string) {
563
+ const sessionId = sessionStore.load(sessionName);
564
+ const jsonlPath = sessionId ? findClaudeSessionFile(options.cwd, sessionId) : null;
565
+ return jsonlPath
566
+ ? processClaudeSession(jsonlPath, systemPromptTokens, claudeMdTokens, skillDescTokens)
567
+ : null;
568
+ }
569
+
546
570
  function getContextInfo(): ContextInfo {
547
571
  return {
548
572
  sessions: Array.from(sessions.values()).map((s) => {
549
573
  try {
550
- const sessionId = sessionStore.load(s.name);
551
- const jsonlPath = sessionId ? findClaudeSessionFile(options.cwd, sessionId) : null;
552
- const parsed = jsonlPath
553
- ? parseClaudeSessionJSONL(
554
- jsonlPath,
555
- systemPromptTokens,
556
- claudeMdTokens,
557
- skillDescTokens,
558
- )
559
- : null;
560
-
574
+ const result = processSession(s.name);
561
575
  return {
562
576
  name: s.name,
563
- contextTokens: parsed?.contextTokens ?? s.contextTokens,
577
+ contextTokens: result?.parsed?.contextTokens ?? s.contextTokens,
564
578
  contextWindow: maxContextTokens,
565
- breakdown: parsed?.breakdown,
579
+ breakdown: result?.parsed?.breakdown,
566
580
  };
567
581
  } catch (err) {
568
582
  log("mind", `failed to get context breakdown for session "${s.name}":`, err);
@@ -573,5 +587,29 @@ export function createMind(options: {
573
587
  };
574
588
  }
575
589
 
576
- return { resolve, waitForCommits: autoCommit.waitForCommits, getContextInfo };
590
+ function getContextMessages(): ContextMessages {
591
+ const skillsDir = resolvePath(options.cwd, ".claude/skills");
592
+ return {
593
+ preamble: {
594
+ systemPrompt: options.systemPrompt,
595
+ sdkInstructions: readSdkInstructions(options.cwd),
596
+ skillDescriptions: readSkillDescriptions([skillsDir]),
597
+ },
598
+ sessions: Array.from(sessions.values()).map((s) => {
599
+ try {
600
+ const result = processSession(s.name);
601
+ return { name: s.name, messages: result?.messages ?? [] };
602
+ } catch (err) {
603
+ log("mind", `failed to extract messages for session "${s.name}":`, err);
604
+ return { name: s.name, messages: [] };
605
+ }
606
+ }),
607
+ };
608
+ }
609
+
610
+ // Pre-warm the main session so the SDK subprocess starts immediately
611
+ // instead of waiting for the first message (which adds minutes of latency).
612
+ getOrCreateSession("main");
613
+
614
+ return { resolve, waitForCommits: autoCommit.waitForCommits, getContextInfo, getContextMessages };
577
615
  }
@@ -53,6 +53,7 @@ const server = createVoluteServer({
53
53
  name: pkg.name,
54
54
  version: pkg.version,
55
55
  getContextInfo: mind.getContextInfo,
56
+ getContextMessages: mind.getContextMessages,
56
57
  });
57
58
 
58
59
  server.listen(port, () => {
@@ -10,12 +10,12 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "@anthropic-ai/tokenizer": "^0.0.4",
13
- "@openai/codex-sdk": "^0.115.0",
14
- "tsx": "^4.21.0"
13
+ "@openai/codex-sdk": "^0.141.0",
14
+ "tsx": "^4.22.4"
15
15
  },
16
16
  "devDependencies": {
17
- "@biomejs/biome": "2.4.10",
18
- "@types/node": "^25.5.0",
19
- "typescript": "^6.0.2"
17
+ "@biomejs/biome": "2.5.0",
18
+ "@types/node": "^26.0.0",
19
+ "typescript": "^6.0.3"
20
20
  }
21
21
  }
@@ -8,7 +8,9 @@ import {
8
8
  countSkillDescriptionTokens,
9
9
  countSystemPromptTokens,
10
10
  findCodexSessionFile,
11
- parseCodexSessionJSONL,
11
+ processCodexSession,
12
+ readSdkInstructions,
13
+ readSkillDescriptions,
12
14
  } from "./lib/context-breakdown.js";
13
15
  import { daemonEmit, daemonRestart, type EventType } from "./lib/daemon-client.js";
14
16
  import { runHooks } from "./lib/hook-loader.js";
@@ -24,7 +26,7 @@ import type {
24
26
  VoluteContentPart,
25
27
  VoluteEvent,
26
28
  } from "./lib/types.js";
27
- import type { ContextInfo } from "./lib/volute-server.js";
29
+ import type { ContextInfo, ContextMessages } from "./lib/volute-server.js";
28
30
 
29
31
  /** Minimal interface for a Codex SDK thread — typed to the methods we actually use */
30
32
  type CodexThread = {
@@ -73,7 +75,11 @@ export function createMind(options: {
73
75
  model?: string;
74
76
  reasoningEffort?: "minimal" | "low" | "medium" | "high" | "xhigh";
75
77
  maxContextTokens?: number;
76
- }): { resolve: HandlerResolver; getContextInfo: () => ContextInfo } {
78
+ }): {
79
+ resolve: HandlerResolver;
80
+ getContextInfo: () => ContextInfo;
81
+ getContextMessages: () => ContextMessages;
82
+ } {
77
83
  const sessions = new Map<string, CodexSession>();
78
84
  const prompts = loadPrompts();
79
85
  const maxContextTokens = options.maxContextTokens;
@@ -599,21 +605,24 @@ export function createMind(options: {
599
605
  const claudeMdTokens = countSdkInstructionTokens(options.cwd);
600
606
  const skillDescTokens = countSkillDescriptionTokens([resolvePath(options.cwd, ".agents/skills")]);
601
607
 
608
+ function processSession(sessionName: string) {
609
+ const threadId = sessionStore.load(sessionName);
610
+ const jsonlPath = threadId ? findCodexSessionFile(threadId, options.mindDir) : null;
611
+ return jsonlPath
612
+ ? processCodexSession(jsonlPath, systemPromptTokens, claudeMdTokens, skillDescTokens)
613
+ : null;
614
+ }
615
+
602
616
  function getContextInfo(): ContextInfo {
603
617
  return {
604
618
  sessions: Array.from(sessions.values()).map((s) => {
605
619
  try {
606
- const threadId = sessionStore.load(s.name);
607
- const jsonlPath = threadId ? findCodexSessionFile(threadId) : null;
608
- const parsed = jsonlPath
609
- ? parseCodexSessionJSONL(jsonlPath, systemPromptTokens, claudeMdTokens, skillDescTokens)
610
- : null;
611
-
620
+ const result = processSession(s.name);
612
621
  return {
613
622
  name: s.name,
614
- contextTokens: parsed?.contextTokens ?? s.cumulativeInputTokens,
623
+ contextTokens: result?.parsed?.contextTokens ?? s.cumulativeInputTokens,
615
624
  contextWindow: maxContextTokens,
616
- breakdown: parsed?.breakdown,
625
+ breakdown: result?.parsed?.breakdown,
617
626
  };
618
627
  } catch (err) {
619
628
  log("mind", `failed to get context breakdown for session "${s.name}":`, err);
@@ -628,5 +637,29 @@ export function createMind(options: {
628
637
  };
629
638
  }
630
639
 
631
- return { resolve, getContextInfo };
640
+ function getContextMessages(): ContextMessages {
641
+ const skillsDir = resolvePath(options.cwd, ".agents/skills");
642
+ return {
643
+ preamble: {
644
+ systemPrompt: options.systemPrompt,
645
+ sdkInstructions: readSdkInstructions(options.cwd),
646
+ skillDescriptions: readSkillDescriptions([skillsDir]),
647
+ },
648
+ sessions: Array.from(sessions.values()).map((s) => {
649
+ try {
650
+ const result = processSession(s.name);
651
+ return { name: s.name, messages: result?.messages ?? [] };
652
+ } catch (err) {
653
+ log("mind", `failed to extract messages for session "${s.name}":`, err);
654
+ return { name: s.name, messages: [] };
655
+ }
656
+ }),
657
+ };
658
+ }
659
+
660
+ // Pre-warm the main session so the thread is created immediately
661
+ // instead of waiting for the first message.
662
+ getOrCreateSession("main");
663
+
664
+ return { resolve, getContextInfo, getContextMessages };
632
665
  }
@@ -45,6 +45,7 @@ const server = createVoluteServer({
45
45
  name: pkg.name,
46
46
  version: pkg.version,
47
47
  getContextInfo: mind.getContextInfo,
48
+ getContextMessages: mind.getContextMessages,
48
49
  });
49
50
 
50
51
  server.listen(port, async () => {
@@ -10,13 +10,13 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "@anthropic-ai/tokenizer": "^0.0.4",
13
- "@mariozechner/pi-coding-agent": "^0.64.0",
14
- "@sinclair/typebox": "^0.34.0",
15
- "tsx": "^4.21.0"
13
+ "@mariozechner/pi-coding-agent": "^0.73.1",
14
+ "@sinclair/typebox": "^0.34.49",
15
+ "tsx": "^4.22.4"
16
16
  },
17
17
  "devDependencies": {
18
- "@biomejs/biome": "2.4.10",
19
- "@types/node": "^25.5.0",
20
- "typescript": "^6.0.2"
18
+ "@biomejs/biome": "2.5.0",
19
+ "@types/node": "^26.0.0",
20
+ "typescript": "^6.0.3"
21
21
  }
22
22
  }
@@ -1,10 +1,11 @@
1
- import { readFileSync } from "node:fs";
1
+ import { readFileSync, watchFile } from "node:fs";
2
2
  import { resolve as resolvePath } from "node:path";
3
3
  import {
4
4
  AuthStorage,
5
5
  createAgentSession,
6
6
  DefaultResourceLoader,
7
7
  type ExtensionFactory,
8
+ getAgentDir,
8
9
  ModelRegistry,
9
10
  SessionManager,
10
11
  SettingsManager,
@@ -15,8 +16,11 @@ import {
15
16
  countSkillDescriptionTokens,
16
17
  countSystemPromptTokens,
17
18
  findPiSessionFile,
18
- parsePiSessionJSONL,
19
+ processPiSession,
20
+ readSdkInstructions,
21
+ readSkillDescriptions,
19
22
  } from "./lib/context-breakdown.js";
23
+ import { daemonEmit } from "./lib/daemon-client.js";
20
24
  import { createEventHandler, emit } from "./lib/event-handler.js";
21
25
  import { runHooks } from "./lib/hook-loader.js";
22
26
  import { log } from "./lib/logger.js";
@@ -32,7 +36,7 @@ import type {
32
36
  VoluteContentPart,
33
37
  VoluteEvent,
34
38
  } from "./lib/types.js";
35
- import type { ContextInfo } from "./lib/volute-server.js";
39
+ import type { ContextInfo, ContextMessages } from "./lib/volute-server.js";
36
40
 
37
41
  type PiAgentSession = Awaited<ReturnType<typeof createAgentSession>>["session"];
38
42
 
@@ -57,7 +61,11 @@ export function createMind(options: {
57
61
  compactionMessage?: string;
58
62
  maxContextTokens?: number;
59
63
  subagents?: Record<string, SubagentConfig>;
60
- }): { resolve: HandlerResolver; getContextInfo: () => ContextInfo } {
64
+ }): {
65
+ resolve: HandlerResolver;
66
+ getContextInfo: () => ContextInfo;
67
+ getContextMessages: () => ContextMessages;
68
+ } {
61
69
  const sessions = new Map<string, PiSession>();
62
70
  const prompts = loadPrompts();
63
71
  const today = new Date().toLocaleDateString("en-CA");
@@ -77,6 +85,29 @@ export function createMind(options: {
77
85
  const authStorage = AuthStorage.create();
78
86
  const modelRegistry = ModelRegistry.create(authStorage);
79
87
 
88
+ // The daemon centrally refreshes the system OAuth token and rewrites auth.json
89
+ // with the fresh token. Watch the file and reload so a running mind adopts the
90
+ // new token without a restart (mirrors the Claude Agent SDK credential reload).
91
+ // This is why the mind itself does not refresh the rotating grant.
92
+ const piAuthPath = process.env.PI_CODING_AGENT_DIR
93
+ ? resolvePath(process.env.PI_CODING_AGENT_DIR, "auth.json")
94
+ : null;
95
+ if (piAuthPath) {
96
+ try {
97
+ watchFile(piAuthPath, { interval: 2000 }, (curr, prev) => {
98
+ if (curr.mtimeMs === prev.mtimeMs) return;
99
+ try {
100
+ authStorage.reload();
101
+ log("mind", "reloaded auth.json after credential refresh");
102
+ } catch (err) {
103
+ log("mind", "failed to reload auth.json:", err);
104
+ }
105
+ });
106
+ } catch (err) {
107
+ log("mind", "failed to watch auth.json for credential refresh:", err);
108
+ }
109
+ }
110
+
80
111
  // --- Subagents (config-driven) ---
81
112
 
82
113
  function loadSubagents(
@@ -296,6 +327,7 @@ export function createMind(options: {
296
327
 
297
328
  const resourceLoader = new DefaultResourceLoader({
298
329
  cwd: options.cwd,
330
+ agentDir: getAgentDir(),
299
331
  settingsManager,
300
332
  systemPrompt: options.systemPrompt,
301
333
  extensionFactories: [
@@ -457,8 +489,14 @@ export function createMind(options: {
457
489
  } else {
458
490
  session.agentSession.prompt(text, opts);
459
491
  }
460
- })().catch((err) => {
492
+ })().catch(async (err) => {
461
493
  log("mind", `session "${sessionName}": prompt failed:`, err);
494
+ // Tell the daemon the turn failed (so it records a notice for the mind's next
495
+ // successful turn) before done, then complete the turn locally and on the daemon.
496
+ await daemonEmit({ type: "error", session: sessionName, content: String(err) }).catch(
497
+ () => {},
498
+ );
499
+ await daemonEmit({ type: "done", session: sessionName }).catch(() => {});
462
500
  broadcast(session, { type: "done" });
463
501
  });
464
502
 
@@ -489,20 +527,23 @@ export function createMind(options: {
489
527
  const claudeMdTokens = countSdkInstructionTokens(options.cwd);
490
528
  const skillDescTokens = countSkillDescriptionTokens([resolvePath(options.cwd, ".pi/skills")]);
491
529
 
530
+ function processSession(sessionName: string) {
531
+ const jsonlPath = findPiSessionFile(piSessionsDir, sessionName);
532
+ return jsonlPath
533
+ ? processPiSession(jsonlPath, systemPromptTokens, claudeMdTokens, skillDescTokens)
534
+ : null;
535
+ }
536
+
492
537
  function getContextInfo(): ContextInfo {
493
538
  return {
494
539
  sessions: Array.from(sessions.values()).map((s) => {
495
540
  try {
496
- const jsonlPath = findPiSessionFile(piSessionsDir, s.name);
497
- const parsed = jsonlPath
498
- ? parsePiSessionJSONL(jsonlPath, systemPromptTokens, claudeMdTokens, skillDescTokens)
499
- : null;
500
-
541
+ const result = processSession(s.name);
501
542
  return {
502
543
  name: s.name,
503
- contextTokens: parsed?.contextTokens ?? s.contextTokens,
544
+ contextTokens: result?.parsed?.contextTokens ?? s.contextTokens,
504
545
  contextWindow: maxContextTokens,
505
- breakdown: parsed?.breakdown,
546
+ breakdown: result?.parsed?.breakdown,
506
547
  };
507
548
  } catch (err) {
508
549
  log("mind", `failed to get context breakdown for session "${s.name}":`, err);
@@ -513,5 +554,25 @@ export function createMind(options: {
513
554
  };
514
555
  }
515
556
 
516
- return { resolve, getContextInfo };
557
+ function getContextMessages(): ContextMessages {
558
+ const skillsDir = resolvePath(options.cwd, ".pi/skills");
559
+ return {
560
+ preamble: {
561
+ systemPrompt: options.systemPrompt,
562
+ sdkInstructions: readSdkInstructions(options.cwd),
563
+ skillDescriptions: readSkillDescriptions([skillsDir]),
564
+ },
565
+ sessions: Array.from(sessions.values()).map((s) => {
566
+ try {
567
+ const result = processSession(s.name);
568
+ return { name: s.name, messages: result?.messages ?? [] };
569
+ } catch (err) {
570
+ log("mind", `failed to extract messages for session "${s.name}":`, err);
571
+ return { name: s.name, messages: [] };
572
+ }
573
+ }),
574
+ };
575
+ }
576
+
577
+ return { resolve, getContextInfo, getContextMessages };
517
578
  }
@@ -1,17 +1,13 @@
1
1
  import type { Model } from "@mariozechner/pi-ai";
2
2
  import {
3
3
  type AuthStorage,
4
- bashTool,
5
- codingTools,
6
4
  createAgentSession,
7
5
  DefaultResourceLoader,
8
6
  type ExtensionFactory,
9
- editTool,
7
+ getAgentDir,
10
8
  type ModelRegistry,
11
- readTool,
12
9
  SessionManager,
13
10
  SettingsManager,
14
- writeTool,
15
11
  } from "@mariozechner/pi-coding-agent";
16
12
  import { Type } from "@sinclair/typebox";
17
13
  import { log } from "./logger.js";
@@ -47,6 +43,7 @@ export function createSubagentExtension(
47
43
 
48
44
  const loader = new DefaultResourceLoader({
49
45
  cwd: context.cwd,
46
+ agentDir: getAgentDir(),
50
47
  systemPromptOverride: () => def.prompt,
51
48
  settingsManager: SettingsManager.inMemory({}),
52
49
  });
@@ -125,29 +122,34 @@ export function createSubagentExtension(
125
122
  };
126
123
  }
127
124
 
128
- const TOOL_MAP: Record<string, (typeof codingTools)[number]> = {
129
- Read: readTool,
130
- Write: writeTool,
131
- Bash: bashTool,
132
- Edit: editTool,
125
+ // Maps subagent tool labels to pi's built-in coding tool names.
126
+ const TOOL_NAME_MAP: Record<string, string> = {
127
+ Read: "read",
128
+ Write: "write",
129
+ Bash: "bash",
130
+ Edit: "edit",
133
131
  };
134
132
 
135
- function resolveTools(names: string[] | undefined) {
136
- if (!names) return codingTools;
133
+ // Resolves subagent tool labels to pi tool names for createAgentSession's
134
+ // `tools` filter. Returns undefined to enable all built-in coding tools.
135
+ function resolveTools(names: string[] | undefined): string[] | undefined {
136
+ if (!names) return undefined;
137
137
  const resolved = names
138
138
  .map((n) => {
139
- if (!TOOL_MAP[n]) {
139
+ const toolName = TOOL_NAME_MAP[n];
140
+ if (!toolName) {
140
141
  log(
141
142
  "mind",
142
- `unknown subagent tool "${n}" — available: ${Object.keys(TOOL_MAP).join(", ")}`,
143
+ `unknown subagent tool "${n}" — available: ${Object.keys(TOOL_NAME_MAP).join(", ")}`,
143
144
  );
145
+ return undefined;
144
146
  }
145
- return TOOL_MAP[n];
147
+ return toolName;
146
148
  })
147
- .filter(Boolean);
149
+ .filter((t) => t !== undefined);
148
150
  if (resolved.length === 0) {
149
151
  log("mind", "no valid tools resolved for subagent, falling back to all coding tools");
150
- return codingTools;
152
+ return undefined;
151
153
  }
152
154
  return resolved;
153
155
  }
@@ -45,6 +45,7 @@ const server = createVoluteServer({
45
45
  name: pkg.name,
46
46
  version: pkg.version,
47
47
  getContextInfo: mind.getContextInfo,
48
+ getContextMessages: mind.getContextMessages,
48
49
  });
49
50
 
50
51
  server.listen(port, () => {
@@ -1,10 +0,0 @@
1
- #!/usr/bin/env node
2
- var __defProp = Object.defineProperty;
3
- var __export = (target, all) => {
4
- for (var name in all)
5
- __defProp(target, name, { get: all[name], enumerable: true });
6
- };
7
-
8
- export {
9
- __export
10
- };
@@ -1,9 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- getDb
4
- } from "./chunk-QDWP435B.js";
5
- import "./chunk-CJ26DXZL.js";
6
- import "./chunk-K3NQKI34.js";
7
- export {
8
- getDb
9
- };
@@ -1,30 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- DeliveryManager,
4
- getDeliveryManager,
5
- initDeliveryManager
6
- } from "./chunk-CHCISTH7.js";
7
- import "./chunk-WUH54G5S.js";
8
- import "./chunk-Y4W3OS7Q.js";
9
- import "./chunk-46DYYHN6.js";
10
- import "./chunk-IBOU6Q3Y.js";
11
- import "./chunk-QDWPM73D.js";
12
- import "./chunk-CU6OFXMM.js";
13
- import "./chunk-EVKGKQ2P.js";
14
- import "./chunk-DZ4LYU6J.js";
15
- import "./chunk-OS5MRIEP.js";
16
- import "./chunk-OEXOFBW5.js";
17
- import "./chunk-6FSSMAXU.js";
18
- import "./chunk-7W3UWCQD.js";
19
- import "./chunk-7GZB3ZJP.js";
20
- import "./chunk-B7MWXFAH.js";
21
- import "./chunk-TCNCRDB5.js";
22
- import "./chunk-T2TP6ZC6.js";
23
- import "./chunk-QDWP435B.js";
24
- import "./chunk-CJ26DXZL.js";
25
- import "./chunk-K3NQKI34.js";
26
- export {
27
- DeliveryManager,
28
- getDeliveryManager,
29
- initDeliveryManager
30
- };
@@ -1,17 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- run,
4
- stopDaemon
5
- } from "./chunk-QJ4GU5XT.js";
6
- import "./chunk-TXSA4Q3V.js";
7
- import "./chunk-O7IGP7ZW.js";
8
- import "./chunk-6V2U3GFQ.js";
9
- import "./chunk-OEXOFBW5.js";
10
- import "./chunk-6FSSMAXU.js";
11
- import "./chunk-QDWP435B.js";
12
- import "./chunk-CJ26DXZL.js";
13
- import "./chunk-K3NQKI34.js";
14
- export {
15
- run,
16
- stopDaemon
17
- };