volute 0.38.0 → 0.40.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 (109) hide show
  1. package/dist/{activity-events-N6HCHU4P.js → activity-events-OJZ7G5CG.js} +3 -3
  2. package/dist/{ai-service-C2YNARGH.js → ai-service-JMV4KZN4.js} +8 -4
  3. package/dist/{archive-AWIJTVQV.js → archive-BC2MTNPV.js} +3 -3
  4. package/dist/{auth-2QOOPMBX.js → auth-2IMZ6N5G.js} +4 -4
  5. package/dist/{chat-XL7CVWVJ.js → chat-O5QCGCUG.js} +2 -2
  6. package/dist/{chunk-A6FLW5XD.js → chunk-2AQCEQYN.js} +1 -1
  7. package/dist/{chunk-3F7XK5Q7.js → chunk-3XSFMN3H.js} +1 -1
  8. package/dist/{chunk-NGXQ2ESO.js → chunk-43PVCF4V.js} +3 -3
  9. package/dist/{chunk-N42QMDID.js → chunk-5VBXKFPO.js} +2 -2
  10. package/dist/{chunk-BIEWHAAM.js → chunk-76Q6YLJL.js} +1 -1
  11. package/dist/{chunk-5DPRTREW.js → chunk-B7YJUWMM.js} +3 -3
  12. package/dist/{chunk-XLBQYIHH.js → chunk-DNQPHLGM.js} +1 -1
  13. package/dist/{chunk-SNW2NPP4.js → chunk-FBV22PFY.js} +1 -1
  14. package/dist/{chunk-WC635OPK.js → chunk-FRJ73IWR.js} +1 -1
  15. package/dist/{chunk-PMMHVSCR.js → chunk-HAJHDGBV.js} +1 -1
  16. package/dist/{chunk-SZJWC2GA.js → chunk-HEZM5GCV.js} +2 -2
  17. package/dist/{chunk-PLL2FM7A.js → chunk-JSITYTIL.js} +6 -6
  18. package/dist/{chunk-CJ26DXZL.js → chunk-MHFAWJCC.js} +16 -0
  19. package/dist/{chunk-7AZQFSOV.js → chunk-PC55DGNX.js} +1 -1
  20. package/dist/{chunk-2NHRJ3YO.js → chunk-PDSA3ILI.js} +3 -3
  21. package/dist/{chunk-ORNY3MZR.js → chunk-PGX52NFO.js} +15 -1
  22. package/dist/{chunk-UIM5NHPP.js → chunk-PHR4XQPL.js} +3 -3
  23. package/dist/chunk-PIRL2HAO.js +124 -0
  24. package/dist/{chunk-JOJT34M2.js → chunk-PZTVQL3J.js} +2 -2
  25. package/dist/{chunk-GVVVMZ4J.js → chunk-RMZSICLS.js} +1 -1
  26. package/dist/{chunk-LQ6Z4FXN.js → chunk-S4YO5OWV.js} +1 -1
  27. package/dist/{chunk-TNZ5XQA4.js → chunk-SGC6CDXB.js} +2 -2
  28. package/dist/{chunk-LN67VPZJ.js → chunk-SOPMVTTW.js} +2 -2
  29. package/dist/{chunk-VUM2K6EN.js → chunk-T26YLQPU.js} +5 -5
  30. package/dist/{chunk-2TR2OPVA.js → chunk-WGRMLXTH.js} +8 -8
  31. package/dist/{chunk-TEZRVSSB.js → chunk-XUJR6NZW.js} +513 -335
  32. package/dist/cli.js +15 -15
  33. package/dist/{cloud-sync-PEM3IVI4.js → cloud-sync-5JF2Z7VM.js} +16 -16
  34. package/dist/{conversations-G6YRSABR.js → conversations-LUVHGQSS.js} +4 -4
  35. package/dist/{create-PN73742N.js → create-TDQM3SHV.js} +1 -1
  36. package/dist/{daemon-restart-6QONRBTK.js → daemon-restart-XHDJDOPQ.js} +8 -8
  37. package/dist/daemon.js +221 -118
  38. package/dist/{db-CBOCDYVA.js → db-2GIS7HSG.js} +2 -2
  39. package/dist/delivery-manager-L5WFP2ND.js +30 -0
  40. package/dist/{delivery-router-QTFEZ26O.js → delivery-router-IU4OMVKZ.js} +3 -3
  41. package/dist/down-KIJVZ3OC.js +17 -0
  42. package/dist/echo-text-TJWCUBPB.js +31 -0
  43. package/dist/{exec-ONYZEA5B.js → exec-BGMU7VXR.js} +4 -4
  44. package/dist/{export-JPDBQESV.js → export-RKUWSQSG.js} +3 -3
  45. package/dist/{extensions-P477XR46.js → extensions-VN5SEVRP.js} +16 -16
  46. package/dist/{files-LAQ3NXQK.js → files-J2CKZ6PK.js} +3 -3
  47. package/dist/{import-EROF27RH.js → import-KVY6BYXG.js} +6 -6
  48. package/dist/{isolation-G5J3MTKU.js → isolation-MM7YHK6J.js} +3 -3
  49. package/dist/{login-F6YMAVLE.js → login-OHM32QVD.js} +2 -2
  50. package/dist/{logout-HHPH52KZ.js → logout-74WBDMOE.js} +2 -2
  51. package/dist/{message-delivery-54IXVE5H.js → message-delivery-VBBAJX3J.js} +16 -16
  52. package/dist/{mind-G5HDKBKL.js → mind-PUPXDIYQ.js} +5 -5
  53. package/dist/{mind-activity-tracker-42ENM32S.js → mind-activity-tracker-7GE24YZE.js} +4 -4
  54. package/dist/mind-manager-4Q6KGZHL.js +30 -0
  55. package/dist/mind-service-7OD4PRO5.js +36 -0
  56. package/dist/{package-LYQHSDPG.js → package-6YPVWGD3.js} +1 -1
  57. package/dist/{registry-KMELPC3X.js → registry-L272BZJR.js} +2 -2
  58. package/dist/{sandbox-PQYEICEF.js → sandbox-BQAFYHLE.js} +6 -6
  59. package/dist/scheduler-7E22Q7IC.js +30 -0
  60. package/dist/{schema-K575EBPE.js → schema-HHYJDG7B.js} +3 -1
  61. package/dist/{seed-3KH4ZRJL.js → seed-4V2O7FQE.js} +1 -1
  62. package/dist/{seed-cmd-6A76SSAA.js → seed-cmd-JKDEW4MP.js} +2 -2
  63. package/dist/{seed-create-XXRRGZCO.js → seed-create-EPAO3H5F.js} +3 -3
  64. package/dist/{seed-sprout-HLMUH2AI.js → seed-sprout-EONCL4DU.js} +8 -8
  65. package/dist/{send-7CIP5GLS.js → send-232RAHPX.js} +3 -3
  66. package/dist/{service-RH6Q2XWR.js → service-AG5BKRUV.js} +5 -5
  67. package/dist/{service-install-AIQBLE6P.js → service-install-HVAEUBAH.js} +6 -6
  68. package/dist/{setup-PF7JSFMO.js → setup-IL7QJZN4.js} +3 -3
  69. package/dist/{setup-NPAZXBIJ.js → setup-PSA7LJ5U.js} +8 -8
  70. package/dist/{skills-NVOWS5NY.js → skills-SLEY5X6W.js} +6 -6
  71. package/dist/sleep-manager-27S7MKJO.js +34 -0
  72. package/dist/{spirit-ZFYG4JOH.js → spirit-7BUHSEO2.js} +8 -8
  73. package/dist/{sprout-FLBCETYG.js → sprout-JWQ57KF3.js} +1 -1
  74. package/dist/{status-TGOKAFW3.js → status-6GSOSYOD.js} +6 -6
  75. package/dist/system-chat-EIINIHFB.js +34 -0
  76. package/dist/{tailscale-LTYNKIPZ.js → tailscale-6MHNLBAX.js} +2 -2
  77. package/dist/up-4XS3RCPP.js +19 -0
  78. package/dist/{update-ZCTQ6UGY.js → update-BT2PY73H.js} +6 -6
  79. package/dist/{update-check-IKS7SGK5.js → update-check-Y3HBEMF2.js} +3 -3
  80. package/dist/{version-notify-764EXVQQ.js → version-notify-NFQMI5IY.js} +17 -17
  81. package/dist/web-assets/assets/index-DqHSW9Fr.css +1 -0
  82. package/dist/web-assets/assets/index-Jluza4c-.js +73 -0
  83. package/dist/web-assets/index.html +2 -2
  84. package/drizzle/0009_mind_notices.sql +14 -0
  85. package/drizzle/meta/_journal.json +7 -0
  86. package/package.json +1 -1
  87. package/templates/_base/.init/.local/hooks/pre-prompt/notices.ts +46 -0
  88. package/templates/_base/src/lib/context-breakdown.ts +281 -126
  89. package/templates/_base/src/lib/daemon-client.ts +5 -2
  90. package/templates/_base/src/lib/transparency.ts +5 -3
  91. package/templates/_base/src/lib/volute-server.ts +23 -0
  92. package/templates/claude/src/agent.ts +54 -16
  93. package/templates/claude/src/server.ts +1 -0
  94. package/templates/codex/src/agent.ts +51 -12
  95. package/templates/codex/src/server.ts +1 -0
  96. package/templates/pi/src/agent.ts +72 -13
  97. package/templates/pi/src/server.ts +1 -0
  98. package/dist/chunk-KXXJYY62.js +0 -112
  99. package/dist/delivery-manager-IMCRV6A3.js +0 -30
  100. package/dist/down-5XZWH4ZP.js +0 -17
  101. package/dist/echo-text-ADKLNTIA.js +0 -31
  102. package/dist/mind-manager-5G6P2EAT.js +0 -30
  103. package/dist/mind-service-FDATHL6X.js +0 -36
  104. package/dist/scheduler-ZRUZNQI5.js +0 -30
  105. package/dist/sleep-manager-VPPQXBPH.js +0 -34
  106. package/dist/system-chat-YUIT7RA6.js +0 -34
  107. package/dist/up-YRZAEZ3Z.js +0 -19
  108. package/dist/web-assets/assets/index-BI7hUTBr.js +0 -73
  109. package/dist/web-assets/assets/index-BJifO9h_.css +0 -1
@@ -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;
@@ -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, () => {
@@ -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;
@@ -161,7 +167,11 @@ export function createMind(options: {
161
167
  model: options.model,
162
168
  modelReasoningEffort: options.reasoningEffort,
163
169
  skipGitRepoCheck: true,
170
+ // Codex's native seatbelt sandbox panics on macOS due to a bug in the
171
+ // system-configuration Rust crate (SCDynamicStore NULL object). Use full
172
+ // access until upstream ships a fix (mullvad/system-configuration-rs#59).
164
173
  sandboxMode: "danger-full-access",
174
+ networkAccessEnabled: true,
165
175
  });
166
176
  return;
167
177
  } catch (err) {
@@ -176,7 +186,9 @@ export function createMind(options: {
176
186
  model: options.model,
177
187
  modelReasoningEffort: options.reasoningEffort,
178
188
  skipGitRepoCheck: true,
189
+ // See comment in resumeThread above
179
190
  sandboxMode: "danger-full-access",
191
+ networkAccessEnabled: true,
180
192
  });
181
193
  log("mind", `session "${session.name}": new thread started`);
182
194
  } catch (err) {
@@ -593,21 +605,24 @@ export function createMind(options: {
593
605
  const claudeMdTokens = countSdkInstructionTokens(options.cwd);
594
606
  const skillDescTokens = countSkillDescriptionTokens([resolvePath(options.cwd, ".agents/skills")]);
595
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
+
596
616
  function getContextInfo(): ContextInfo {
597
617
  return {
598
618
  sessions: Array.from(sessions.values()).map((s) => {
599
619
  try {
600
- const threadId = sessionStore.load(s.name);
601
- const jsonlPath = threadId ? findCodexSessionFile(threadId) : null;
602
- const parsed = jsonlPath
603
- ? parseCodexSessionJSONL(jsonlPath, systemPromptTokens, claudeMdTokens, skillDescTokens)
604
- : null;
605
-
620
+ const result = processSession(s.name);
606
621
  return {
607
622
  name: s.name,
608
- contextTokens: parsed?.contextTokens ?? s.cumulativeInputTokens,
623
+ contextTokens: result?.parsed?.contextTokens ?? s.cumulativeInputTokens,
609
624
  contextWindow: maxContextTokens,
610
- breakdown: parsed?.breakdown,
625
+ breakdown: result?.parsed?.breakdown,
611
626
  };
612
627
  } catch (err) {
613
628
  log("mind", `failed to get context breakdown for session "${s.name}":`, err);
@@ -622,5 +637,29 @@ export function createMind(options: {
622
637
  };
623
638
  }
624
639
 
625
- 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 };
626
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 () => {
@@ -1,4 +1,4 @@
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,
@@ -15,8 +15,11 @@ import {
15
15
  countSkillDescriptionTokens,
16
16
  countSystemPromptTokens,
17
17
  findPiSessionFile,
18
- parsePiSessionJSONL,
18
+ processPiSession,
19
+ readSdkInstructions,
20
+ readSkillDescriptions,
19
21
  } from "./lib/context-breakdown.js";
22
+ import { daemonEmit } from "./lib/daemon-client.js";
20
23
  import { createEventHandler, emit } from "./lib/event-handler.js";
21
24
  import { runHooks } from "./lib/hook-loader.js";
22
25
  import { log } from "./lib/logger.js";
@@ -32,7 +35,7 @@ import type {
32
35
  VoluteContentPart,
33
36
  VoluteEvent,
34
37
  } from "./lib/types.js";
35
- import type { ContextInfo } from "./lib/volute-server.js";
38
+ import type { ContextInfo, ContextMessages } from "./lib/volute-server.js";
36
39
 
37
40
  type PiAgentSession = Awaited<ReturnType<typeof createAgentSession>>["session"];
38
41
 
@@ -57,7 +60,11 @@ export function createMind(options: {
57
60
  compactionMessage?: string;
58
61
  maxContextTokens?: number;
59
62
  subagents?: Record<string, SubagentConfig>;
60
- }): { resolve: HandlerResolver; getContextInfo: () => ContextInfo } {
63
+ }): {
64
+ resolve: HandlerResolver;
65
+ getContextInfo: () => ContextInfo;
66
+ getContextMessages: () => ContextMessages;
67
+ } {
61
68
  const sessions = new Map<string, PiSession>();
62
69
  const prompts = loadPrompts();
63
70
  const today = new Date().toLocaleDateString("en-CA");
@@ -77,6 +84,29 @@ export function createMind(options: {
77
84
  const authStorage = AuthStorage.create();
78
85
  const modelRegistry = ModelRegistry.create(authStorage);
79
86
 
87
+ // The daemon centrally refreshes the system OAuth token and rewrites auth.json
88
+ // with the fresh token. Watch the file and reload so a running mind adopts the
89
+ // new token without a restart (mirrors the Claude Agent SDK credential reload).
90
+ // This is why the mind itself does not refresh the rotating grant.
91
+ const piAuthPath = process.env.PI_CODING_AGENT_DIR
92
+ ? resolvePath(process.env.PI_CODING_AGENT_DIR, "auth.json")
93
+ : null;
94
+ if (piAuthPath) {
95
+ try {
96
+ watchFile(piAuthPath, { interval: 2000 }, (curr, prev) => {
97
+ if (curr.mtimeMs === prev.mtimeMs) return;
98
+ try {
99
+ authStorage.reload();
100
+ log("mind", "reloaded auth.json after credential refresh");
101
+ } catch (err) {
102
+ log("mind", "failed to reload auth.json:", err);
103
+ }
104
+ });
105
+ } catch (err) {
106
+ log("mind", "failed to watch auth.json for credential refresh:", err);
107
+ }
108
+ }
109
+
80
110
  // --- Subagents (config-driven) ---
81
111
 
82
112
  function loadSubagents(
@@ -457,8 +487,14 @@ export function createMind(options: {
457
487
  } else {
458
488
  session.agentSession.prompt(text, opts);
459
489
  }
460
- })().catch((err) => {
490
+ })().catch(async (err) => {
461
491
  log("mind", `session "${sessionName}": prompt failed:`, err);
492
+ // Tell the daemon the turn failed (so it records a notice for the mind's next
493
+ // successful turn) before done, then complete the turn locally and on the daemon.
494
+ await daemonEmit({ type: "error", session: sessionName, content: String(err) }).catch(
495
+ () => {},
496
+ );
497
+ await daemonEmit({ type: "done", session: sessionName }).catch(() => {});
462
498
  broadcast(session, { type: "done" });
463
499
  });
464
500
 
@@ -489,20 +525,23 @@ export function createMind(options: {
489
525
  const claudeMdTokens = countSdkInstructionTokens(options.cwd);
490
526
  const skillDescTokens = countSkillDescriptionTokens([resolvePath(options.cwd, ".pi/skills")]);
491
527
 
528
+ function processSession(sessionName: string) {
529
+ const jsonlPath = findPiSessionFile(piSessionsDir, sessionName);
530
+ return jsonlPath
531
+ ? processPiSession(jsonlPath, systemPromptTokens, claudeMdTokens, skillDescTokens)
532
+ : null;
533
+ }
534
+
492
535
  function getContextInfo(): ContextInfo {
493
536
  return {
494
537
  sessions: Array.from(sessions.values()).map((s) => {
495
538
  try {
496
- const jsonlPath = findPiSessionFile(piSessionsDir, s.name);
497
- const parsed = jsonlPath
498
- ? parsePiSessionJSONL(jsonlPath, systemPromptTokens, claudeMdTokens, skillDescTokens)
499
- : null;
500
-
539
+ const result = processSession(s.name);
501
540
  return {
502
541
  name: s.name,
503
- contextTokens: parsed?.contextTokens ?? s.contextTokens,
542
+ contextTokens: result?.parsed?.contextTokens ?? s.contextTokens,
504
543
  contextWindow: maxContextTokens,
505
- breakdown: parsed?.breakdown,
544
+ breakdown: result?.parsed?.breakdown,
506
545
  };
507
546
  } catch (err) {
508
547
  log("mind", `failed to get context breakdown for session "${s.name}":`, err);
@@ -513,5 +552,25 @@ export function createMind(options: {
513
552
  };
514
553
  }
515
554
 
516
- return { resolve, getContextInfo };
555
+ function getContextMessages(): ContextMessages {
556
+ const skillsDir = resolvePath(options.cwd, ".pi/skills");
557
+ return {
558
+ preamble: {
559
+ systemPrompt: options.systemPrompt,
560
+ sdkInstructions: readSdkInstructions(options.cwd),
561
+ skillDescriptions: readSkillDescriptions([skillsDir]),
562
+ },
563
+ sessions: Array.from(sessions.values()).map((s) => {
564
+ try {
565
+ const result = processSession(s.name);
566
+ return { name: s.name, messages: result?.messages ?? [] };
567
+ } catch (err) {
568
+ log("mind", `failed to extract messages for session "${s.name}":`, err);
569
+ return { name: s.name, messages: [] };
570
+ }
571
+ }),
572
+ };
573
+ }
574
+
575
+ return { resolve, getContextInfo, getContextMessages };
517
576
  }
@@ -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,112 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- readGlobalConfig
4
- } from "./chunk-A6FLW5XD.js";
5
- import {
6
- logger_default
7
- } from "./chunk-T2TP6ZC6.js";
8
- import {
9
- getBaseName,
10
- readRegistry,
11
- voluteHome,
12
- voluteSystemDir,
13
- voluteUserHome
14
- } from "./chunk-2NHRJ3YO.js";
15
-
16
- // packages/daemon/src/lib/mind/sandbox.ts
17
- import { resolve } from "path";
18
- var slog = logger_default.child("sandbox");
19
- var sandboxManager = null;
20
- function isSandboxEnabled() {
21
- if (process.env.VOLUTE_SANDBOX === "0") return false;
22
- return readGlobalConfig().setup?.isolation === "sandbox";
23
- }
24
- async function initSandbox() {
25
- if (!isSandboxEnabled()) return;
26
- try {
27
- const { SandboxManager } = await import("@anthropic-ai/sandbox-runtime");
28
- const config = {
29
- network: {
30
- allowedDomains: ["*"],
31
- deniedDomains: [],
32
- allowLocalBinding: true,
33
- allowAllUnixSockets: true
34
- },
35
- filesystem: {
36
- denyRead: [],
37
- allowWrite: [],
38
- denyWrite: []
39
- }
40
- };
41
- await SandboxManager.initialize(config);
42
- sandboxManager = SandboxManager;
43
- } catch (err) {
44
- slog.error(
45
- "sandbox runtime not available \u2014 minds will run without sandbox isolation",
46
- logger_default.errorData(err)
47
- );
48
- }
49
- }
50
- async function buildDenyRead(mindName, mindDir) {
51
- const home = voluteHome();
52
- const userHome = process.env.HOME || "";
53
- const mindsDir = process.env.VOLUTE_MINDS_DIR || resolve(home, "minds");
54
- const deny = [];
55
- deny.push(voluteSystemDir());
56
- const userVoluteHome = voluteUserHome();
57
- if (userVoluteHome !== home) {
58
- deny.push(userVoluteHome);
59
- }
60
- try {
61
- const entries = await readRegistry();
62
- for (const entry of entries) {
63
- if (entry.name === await getBaseName(mindName)) continue;
64
- const otherDir = resolve(mindsDir, entry.name);
65
- if (otherDir !== mindDir) {
66
- deny.push(otherDir);
67
- }
68
- }
69
- } catch (err) {
70
- slog.warn("failed to read minds registry for deny-read list", logger_default.errorData(err));
71
- }
72
- if (userHome) {
73
- deny.push(resolve(userHome, ".ssh"));
74
- deny.push(resolve(userHome, ".aws"));
75
- deny.push(resolve(userHome, ".gnupg"));
76
- deny.push(resolve(userHome, ".config"));
77
- }
78
- return deny;
79
- }
80
- function shellEscape(s) {
81
- return `'${s.replace(/'/g, "'\\''")}'`;
82
- }
83
- async function wrapForSandbox(cmd, args, mindDir, mindName, allowWrite) {
84
- if (!sandboxManager) return [cmd, args];
85
- const denyRead = await buildDenyRead(mindName, mindDir);
86
- const customConfig = {
87
- filesystem: {
88
- denyRead,
89
- allowWrite: allowWrite ?? [mindDir],
90
- denyWrite: []
91
- }
92
- };
93
- try {
94
- const shellCmd = [cmd, ...args].map(shellEscape).join(" ");
95
- const wrapped = await sandboxManager.wrapWithSandbox(shellCmd, void 0, customConfig);
96
- return ["bash", ["-c", wrapped]];
97
- } catch (err) {
98
- slog.error(
99
- `failed to sandbox mind ${mindName} \u2014 running without isolation`,
100
- logger_default.errorData(err)
101
- );
102
- return [cmd, args];
103
- }
104
- }
105
-
106
- export {
107
- isSandboxEnabled,
108
- initSandbox,
109
- buildDenyRead,
110
- shellEscape,
111
- wrapForSandbox
112
- };
@@ -1,30 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- DeliveryManager,
4
- getDeliveryManager,
5
- initDeliveryManager
6
- } from "./chunk-TEZRVSSB.js";
7
- import "./chunk-PMMHVSCR.js";
8
- import "./chunk-2TR2OPVA.js";
9
- import "./chunk-46DYYHN6.js";
10
- import "./chunk-VUM2K6EN.js";
11
- import "./chunk-5DPRTREW.js";
12
- import "./chunk-CU6OFXMM.js";
13
- import "./chunk-KXXJYY62.js";
14
- import "./chunk-7AZQFSOV.js";
15
- import "./chunk-GVVVMZ4J.js";
16
- import "./chunk-3F7XK5Q7.js";
17
- import "./chunk-SNW2NPP4.js";
18
- import "./chunk-UIM5NHPP.js";
19
- import "./chunk-N42QMDID.js";
20
- import "./chunk-ORNY3MZR.js";
21
- import "./chunk-A6FLW5XD.js";
22
- import "./chunk-T2TP6ZC6.js";
23
- import "./chunk-2NHRJ3YO.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-LN67VPZJ.js";
6
- import "./chunk-TXSA4Q3V.js";
7
- import "./chunk-O7IGP7ZW.js";
8
- import "./chunk-TNZ5XQA4.js";
9
- import "./chunk-3F7XK5Q7.js";
10
- import "./chunk-SNW2NPP4.js";
11
- import "./chunk-2NHRJ3YO.js";
12
- import "./chunk-CJ26DXZL.js";
13
- import "./chunk-K3NQKI34.js";
14
- export {
15
- run,
16
- stopDaemon
17
- };
@@ -1,31 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- clearEchoTextCache,
4
- echoTextToChannel
5
- } from "./chunk-PLL2FM7A.js";
6
- import "./chunk-LQ6Z4FXN.js";
7
- import "./chunk-TEZRVSSB.js";
8
- import "./chunk-PMMHVSCR.js";
9
- import "./chunk-2TR2OPVA.js";
10
- import "./chunk-46DYYHN6.js";
11
- import "./chunk-VUM2K6EN.js";
12
- import "./chunk-5DPRTREW.js";
13
- import "./chunk-CU6OFXMM.js";
14
- import "./chunk-KXXJYY62.js";
15
- import "./chunk-7AZQFSOV.js";
16
- import "./chunk-GVVVMZ4J.js";
17
- import "./chunk-3F7XK5Q7.js";
18
- import "./chunk-SNW2NPP4.js";
19
- import "./chunk-UIM5NHPP.js";
20
- import "./chunk-N42QMDID.js";
21
- import "./chunk-ORNY3MZR.js";
22
- import "./chunk-A6FLW5XD.js";
23
- import "./chunk-T2TP6ZC6.js";
24
- import "./chunk-LGNUFVMR.js";
25
- import "./chunk-2NHRJ3YO.js";
26
- import "./chunk-CJ26DXZL.js";
27
- import "./chunk-K3NQKI34.js";
28
- export {
29
- clearEchoTextCache,
30
- echoTextToChannel
31
- };
@@ -1,30 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- MindManager,
4
- getMindManager,
5
- initMindManager
6
- } from "./chunk-TEZRVSSB.js";
7
- import "./chunk-PMMHVSCR.js";
8
- import "./chunk-2TR2OPVA.js";
9
- import "./chunk-46DYYHN6.js";
10
- import "./chunk-VUM2K6EN.js";
11
- import "./chunk-5DPRTREW.js";
12
- import "./chunk-CU6OFXMM.js";
13
- import "./chunk-KXXJYY62.js";
14
- import "./chunk-7AZQFSOV.js";
15
- import "./chunk-GVVVMZ4J.js";
16
- import "./chunk-3F7XK5Q7.js";
17
- import "./chunk-SNW2NPP4.js";
18
- import "./chunk-UIM5NHPP.js";
19
- import "./chunk-N42QMDID.js";
20
- import "./chunk-ORNY3MZR.js";
21
- import "./chunk-A6FLW5XD.js";
22
- import "./chunk-T2TP6ZC6.js";
23
- import "./chunk-2NHRJ3YO.js";
24
- import "./chunk-CJ26DXZL.js";
25
- import "./chunk-K3NQKI34.js";
26
- export {
27
- MindManager,
28
- getMindManager,
29
- initMindManager
30
- };