volute 0.33.0 → 0.34.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 (182) hide show
  1. package/dist/{accept-D5VBM7JW.js → accept-TW6V4WI4.js} +6 -6
  2. package/dist/{activity-events-XJO3P4RR.js → activity-events-BN7V6KCC.js} +4 -4
  3. package/dist/{ai-service-SBY2WG7O.js → ai-service-PSILB5WD.js} +5 -5
  4. package/dist/{api-client-YPKOZP2O.js → api-client-XUXOB7LI.js} +1 -1
  5. package/dist/api.d.ts +426 -3
  6. package/dist/{archive-INXYFVCW.js → archive-C2VEMQOR.js} +4 -4
  7. package/dist/{auth-GKCDSO4T.js → auth-ZFZXJZDQ.js} +5 -5
  8. package/dist/{bridge-TXWWPPOJ.js → bridge-O753D5F4.js} +6 -6
  9. package/dist/{chat-U5ZOME3O.js → chat-BHYX7DJ4.js} +9 -9
  10. package/dist/{chunk-M7UL5S3Q.js → chunk-2IOP6PHB.js} +1 -1
  11. package/dist/{chunk-NPKSDYA2.js → chunk-47XDEWWV.js} +5 -5
  12. package/dist/{chunk-RSX4OPZY.js → chunk-47ZPNLF4.js} +7 -7
  13. package/dist/{chunk-RPZZSXV3.js → chunk-4JSR7YO7.js} +20 -1
  14. package/dist/{chunk-N432I7QH.js → chunk-6OWJXUAR.js} +1 -1
  15. package/dist/{chunk-I5KY25PQ.js → chunk-6WAWMWR5.js} +1 -1
  16. package/dist/{chunk-NNB4WIG7.js → chunk-7F2SW2KD.js} +2 -2
  17. package/dist/chunk-7KJOFUNN.js +22 -0
  18. package/dist/{chunk-7J3HEVR7.js → chunk-B2BVAIZ4.js} +15 -9
  19. package/dist/{chunk-VH33ZWMW.js → chunk-BDK73LK6.js} +1 -1
  20. package/dist/{chunk-QTUVYI7W.js → chunk-BFWHBQK4.js} +1 -1
  21. package/dist/{chunk-JYVGHWEJ.js → chunk-BM474GX6.js} +3 -3
  22. package/dist/{chunk-LOEJ4HPQ.js → chunk-BTWAGDV5.js} +1 -1
  23. package/dist/{chunk-A2A4KLFE.js → chunk-CVL5IGIR.js} +596 -40
  24. package/dist/{chunk-RVGLDGMI.js → chunk-E5C7OWZ2.js} +20 -22
  25. package/dist/chunk-FYCALD4Q.js +23 -0
  26. package/dist/{chunk-SKLSMHXO.js → chunk-IS7WJ56Q.js} +1 -1
  27. package/dist/{chunk-2NGTS5UU.js → chunk-M3K5AARV.js} +1 -1
  28. package/dist/{chunk-ALEF47VT.js → chunk-MLOQKQNB.js} +1 -1
  29. package/dist/{chunk-C7I35G4R.js → chunk-N3DNFPVA.js} +41 -5
  30. package/dist/{chunk-LRCG2JLP.js → chunk-N7BLAHNE.js} +5 -1
  31. package/dist/{chunk-UKVWJRKN.js → chunk-PLDWHR4D.js} +1 -1
  32. package/dist/{chunk-3Z2DPESO.js → chunk-TAHX36HZ.js} +126 -81
  33. package/dist/{chunk-KIEPMIM5.js → chunk-U5BTYSAL.js} +1 -1
  34. package/dist/{chunk-GY5HBI7A.js → chunk-V45JXOWY.js} +2 -2
  35. package/dist/{chunk-JUKK7FPS.js → chunk-V6ZCNULL.js} +2 -2
  36. package/dist/{chunk-KVK2DLWI.js → chunk-XWXBJQBE.js} +2 -2
  37. package/dist/cli.js +23 -23
  38. package/dist/{clock-BVH3V6E3.js → clock-3X4DSC2N.js} +40 -25
  39. package/dist/{cloud-sync-4NWLMFVH.js → cloud-sync-TG3TIX5H.js} +21 -21
  40. package/dist/{config-H2H4UIF7.js → config-OROA5DUA.js} +4 -4
  41. package/dist/connectors/discord-bridge.js +1 -1
  42. package/dist/connectors/slack-bridge.js +1 -1
  43. package/dist/connectors/telegram-bridge.js +1 -1
  44. package/dist/{conversations-AWI5SZW2.js → conversations-HL2JP5GI.js} +5 -5
  45. package/dist/{create-YWD2TIP4.js → create-3SEKKI6P.js} +6 -6
  46. package/dist/{create-2FK7Z46Y.js → create-UOSOQ2HN.js} +4 -4
  47. package/dist/daemon-client-WOAQXXBM.js +12 -0
  48. package/dist/{daemon-restart-GOBUKLX7.js → daemon-restart-5ABHNXJZ.js} +9 -9
  49. package/dist/daemon.js +1747 -688
  50. package/dist/{db-RA45JBFG.js → db-PLEDCBHZ.js} +1 -1
  51. package/dist/db-RYX3SS2W.js +9 -0
  52. package/dist/{delete-QTGWEDBI.js → delete-KYOVWR23.js} +3 -3
  53. package/dist/delivery-manager-2BR5NZKF.js +32 -0
  54. package/dist/{delivery-router-FL45JL7N.js → delivery-router-D5ELDMS2.js} +4 -4
  55. package/dist/down-QVFN4UPK.js +15 -0
  56. package/dist/{env-JCOF2222.js → env-R34DT7XL.js} +12 -8
  57. package/dist/exec-DVLXKRIO.js +17 -0
  58. package/dist/{export-SUYRLI5Q.js → export-6ZXAXATG.js} +6 -6
  59. package/dist/extension-PM42QCID.js +97 -0
  60. package/dist/extensions-BBGVL5JC.js +38 -0
  61. package/dist/{files-65PMW5IK.js → files-VQV2VZQO.js} +7 -7
  62. package/dist/{import-DDUFE7AY.js → import-MK2I2T6F.js} +5 -5
  63. package/dist/{isolation-LLAYQYDY.js → isolation-62MKDZN3.js} +4 -4
  64. package/dist/{join-I5QEE3LG.js → join-DGYHTJUH.js} +3 -3
  65. package/dist/lib-DYEZMGW7.js +6588 -0
  66. package/dist/{list-JQ463EDA.js → list-C644WTHV.js} +18 -10
  67. package/dist/{login-D7ETSU4R.js → login-IIGEQPHL.js} +6 -6
  68. package/dist/{login-RIJF2F4G.js → login-KZQLMAWE.js} +4 -4
  69. package/dist/{logout-5MLHZALK.js → logout-AGTZVRGP.js} +4 -4
  70. package/dist/{logout-UZJRGY4Z.js → logout-KD6GXIJJ.js} +4 -4
  71. package/dist/message-delivery-V3R6NXJP.js +42 -0
  72. package/dist/{mind-IOJFLEM5.js → mind-BI4EPBVZ.js} +19 -19
  73. package/dist/{mind-activity-tracker-F6O4Q2SL.js → mind-activity-tracker-2ACNHA7B.js} +5 -5
  74. package/dist/mind-history-WOYFLQAI.js +264 -0
  75. package/dist/{mind-list-WUPMQDYQ.js → mind-list-6VPM7GUQ.js} +4 -4
  76. package/dist/mind-manager-MWW3BTS4.js +32 -0
  77. package/dist/{mind-profile-P67FEHOY.js → mind-profile-WPG42U5Y.js} +2 -2
  78. package/dist/mind-service-VIKZJK2M.js +38 -0
  79. package/dist/{mind-sleep-WW2IX7JT.js → mind-sleep-XDISJY74.js} +6 -6
  80. package/dist/{mind-status-L3EFFRPR.js → mind-status-7FTZWPZF.js} +4 -4
  81. package/dist/{mind-wake-VSSGW465.js → mind-wake-KIIKEI3A.js} +6 -6
  82. package/dist/{package-U3VFO273.js → package-V2WHWVG6.js} +8 -5
  83. package/dist/{read-EBY56C33.js → read-H5C26YO7.js} +20 -10
  84. package/dist/{read-stdin-HQJ7774D.js → read-stdin-PIRM6A2Y.js} +1 -1
  85. package/dist/{register-HD74C4TT.js → register-J27WP33N.js} +6 -6
  86. package/dist/{registry-PJ4S5PHQ.js → registry-UYV5S6QT.js} +3 -3
  87. package/dist/{reject-UJKFBHRO.js → reject-OEANJYIA.js} +6 -6
  88. package/dist/{restart-3UCMRUVC.js → restart-V5EGYBJG.js} +4 -4
  89. package/dist/{sandbox-GJOK4QLQ.js → sandbox-SI5HMBP3.js} +5 -5
  90. package/dist/scheduler-AGG3L2FO.js +32 -0
  91. package/dist/{schema-PA3M5ZKH.js → schema-ETMABTW4.js} +4 -2
  92. package/dist/{seed-QDYVLG74.js → seed-WNGI6PNW.js} +2 -2
  93. package/dist/{seed-check-S2IX25RL.js → seed-check-PXTH7YXS.js} +2 -2
  94. package/dist/{seed-cmd-DKOUFEAU.js → seed-cmd-VENFTGS3.js} +4 -4
  95. package/dist/{seed-create-4XBBOLRH.js → seed-create-663ALOKH.js} +6 -6
  96. package/dist/{seed-sprout-GQEIIQRT.js → seed-sprout-EH3AGKAI.js} +12 -12
  97. package/dist/{send-QIV2INHB.js → send-7FUUUZZH.js} +23 -10
  98. package/dist/{setup-TISPCO22.js → setup-GGMKENLN.js} +4 -4
  99. package/dist/{setup-XMCBE3LF.js → setup-Z3DEVWV7.js} +11 -11
  100. package/dist/{skill-PSQGRRJX.js → skill-DKNYJS4P.js} +14 -10
  101. package/dist/skills/plan-coordinator/SKILL.md +60 -0
  102. package/dist/skills/volute-mind/SKILL.md +7 -221
  103. package/dist/skills/volute-mind/references/extensions.md +37 -0
  104. package/dist/skills/volute-mind/references/integrations.md +48 -0
  105. package/dist/skills/volute-mind/references/routing.md +86 -0
  106. package/dist/skills/volute-mind/references/sleep.md +33 -0
  107. package/dist/skills/volute-mind/references/variants.md +31 -0
  108. package/dist/{skills-7FV7EJTE.js → skills-Q6VZ2UGD.js} +11 -7
  109. package/dist/sleep-manager-BJK2ROPX.js +36 -0
  110. package/dist/spirit-4JP4TY4C.js +23 -0
  111. package/dist/{split-STOROBYJ.js → split-3YPMS2CL.js} +3 -3
  112. package/dist/{sprout-WKLZXUIQ.js → sprout-E3HJIV2Z.js} +2 -2
  113. package/dist/{start-K2NCUUCG.js → start-W3TPKX4D.js} +4 -4
  114. package/dist/{status-3JBTFSMI.js → status-4OVFXFEJ.js} +7 -7
  115. package/dist/{stop-H26JZDXF.js → stop-GTT6YWYO.js} +4 -4
  116. package/dist/system-channel-DXD2JBOU.js +36 -0
  117. package/dist/system-chat-TYLOL7SX.js +36 -0
  118. package/dist/{systems-XRI52VCH.js → systems-AYLO727G.js} +7 -7
  119. package/dist/{tailscale-XHQBZROW.js → tailscale-ZEUK7GKZ.js} +3 -3
  120. package/dist/{template-hash-A6VVKOXJ.js → template-hash-EJRTKE36.js} +1 -1
  121. package/dist/up-PA7F2CXE.js +18 -0
  122. package/dist/{update-UD543CXX.js → update-HG4LCUSG.js} +7 -7
  123. package/dist/{update-check-ZD6OOIYQ.js → update-check-X3YG4WVP.js} +4 -4
  124. package/dist/{upgrade-O4Q7WJM3.js → upgrade-YGNIDICG.js} +3 -3
  125. package/dist/{variant-7TGZHOU3.js → variant-MZUMRTQO.js} +1 -1
  126. package/dist/{version-notify-NBI2MTJO.js → version-notify-YCH4UVQ2.js} +19 -19
  127. package/dist/{volute-config-HD7WWUQC.js → volute-config-WBKYJGYQ.js} +1 -1
  128. package/dist/web-assets/assets/index-DiiwC-CZ.css +1 -0
  129. package/dist/web-assets/assets/index-d6y5b9Ij.js +75 -0
  130. package/dist/web-assets/ext-theme.css +48 -9
  131. package/dist/web-assets/index.html +2 -2
  132. package/drizzle/0005_meta_summaries.sql +15 -0
  133. package/drizzle/meta/0005_snapshot.json +7 -0
  134. package/drizzle/meta/_journal.json +7 -0
  135. package/package.json +7 -4
  136. package/packages/extensions/plan/dist/ui/assets/index-CJj2gZnZ.css +1 -0
  137. package/packages/extensions/plan/dist/ui/assets/index-FMEJmvQz.js +61 -0
  138. package/packages/extensions/plan/dist/ui/index.html +14 -0
  139. package/packages/extensions/plan/skills/plan/SKILL.md +43 -0
  140. package/packages/extensions/plan/skills/plan/scripts/plan-hook.sh +37 -0
  141. package/templates/_base/home/VOLUTE.md +12 -19
  142. package/templates/_base/src/lib/context-breakdown.ts +450 -0
  143. package/templates/_base/src/lib/format-prefix.ts +17 -0
  144. package/templates/_base/src/lib/hook-loader.ts +8 -2
  145. package/templates/_base/src/lib/router.ts +75 -33
  146. package/templates/_base/src/lib/routing.ts +4 -1
  147. package/templates/_base/src/lib/startup.ts +16 -8
  148. package/templates/_base/src/lib/types.ts +2 -1
  149. package/templates/_base/src/lib/volute-server.ts +69 -8
  150. package/templates/claude/.init/CLAUDE.md +4 -10
  151. package/templates/claude/package.json.tmpl +1 -0
  152. package/templates/claude/src/agent.ts +100 -32
  153. package/templates/claude/src/lib/hooks/reply-instructions.ts +27 -7
  154. package/templates/claude/src/lib/stream-consumer.ts +2 -2
  155. package/templates/claude/src/server.ts +1 -0
  156. package/templates/codex/package.json.tmpl +1 -0
  157. package/templates/codex/src/agent.ts +80 -8
  158. package/templates/codex/src/server.ts +1 -4
  159. package/templates/pi/package.json.tmpl +1 -0
  160. package/templates/pi/src/agent.ts +115 -36
  161. package/templates/pi/src/lib/event-handler.ts +22 -7
  162. package/templates/pi/src/lib/reply-instructions-extension.ts +23 -4
  163. package/templates/pi/src/lib/subagents.ts +20 -17
  164. package/templates/pi/src/server.ts +2 -5
  165. package/dist/chunk-K3NQKI34.js +0 -10
  166. package/dist/daemon-client-6QXHZ7US.js +0 -12
  167. package/dist/db-F34YLV7D.js +0 -9
  168. package/dist/delivery-manager-PFAKEJTC.js +0 -32
  169. package/dist/down-FWWTEKXM.js +0 -15
  170. package/dist/extension-OBTGKQQD.js +0 -175
  171. package/dist/extensions-KYNTVTMO.js +0 -30
  172. package/dist/history-DKCDI3JO.js +0 -128
  173. package/dist/message-delivery-DFF5SJRM.js +0 -42
  174. package/dist/mind-manager-NBJF5D26.js +0 -32
  175. package/dist/mind-service-2MQ6UK5N.js +0 -38
  176. package/dist/scheduler-ZZ7XGQG6.js +0 -32
  177. package/dist/sleep-manager-JTXSN7NV.js +0 -36
  178. package/dist/spirit-VRONKFMF.js +0 -23
  179. package/dist/system-chat-JAPOJ3KE.js +0 -36
  180. package/dist/up-M5AS6SBV.js +0 -18
  181. package/dist/web-assets/assets/index-CWJrVveV.css +0 -1
  182. package/dist/web-assets/assets/index-DJt14FRI.js +0 -75
@@ -10,12 +10,19 @@ import {
10
10
  SettingsManager,
11
11
  } from "@mariozechner/pi-coding-agent";
12
12
  import { extractImages, extractText } from "./lib/content.js";
13
+ import {
14
+ countSdkInstructionTokens,
15
+ countSkillDescriptionTokens,
16
+ countSystemPromptTokens,
17
+ findPiSessionFile,
18
+ parsePiSessionJSONL,
19
+ } from "./lib/context-breakdown.js";
13
20
  import { createEventHandler, emit } from "./lib/event-handler.js";
14
21
  import { runHooks } from "./lib/hook-loader.js";
15
22
  import { log } from "./lib/logger.js";
16
23
  import { createReplyInstructionsExtension } from "./lib/reply-instructions-extension.js";
17
24
  import { resolveModel } from "./lib/resolve-model.js";
18
- import { loadPrompts, type SubagentConfig } from "./lib/startup.js";
25
+ import { getStartupContext, loadPrompts, type SubagentConfig } from "./lib/startup.js";
19
26
  import { createSubagentExtension, type SubagentDefinition } from "./lib/subagents.js";
20
27
  import type {
21
28
  HandlerMeta,
@@ -25,6 +32,7 @@ import type {
25
32
  VoluteContentPart,
26
33
  VoluteEvent,
27
34
  } from "./lib/types.js";
35
+ import type { ContextInfo } from "./lib/volute-server.js";
28
36
 
29
37
  type PiAgentSession = Awaited<ReturnType<typeof createAgentSession>>["session"];
30
38
 
@@ -36,7 +44,8 @@ type PiSession = {
36
44
  unsubscribe?: () => void;
37
45
  messageIds: (string | undefined)[];
38
46
  currentMessageId?: string;
39
- messageChannels: Map<string, string>;
47
+ messageChannels: Map<string, { channel: string; sender?: string }>;
48
+ contextTokens: number;
40
49
  };
41
50
 
42
51
  export function createMind(options: {
@@ -48,7 +57,7 @@ export function createMind(options: {
48
57
  compactionMessage?: string;
49
58
  maxContextTokens?: number;
50
59
  subagents?: Record<string, SubagentConfig>;
51
- }): { resolve: HandlerResolver } {
60
+ }): { resolve: HandlerResolver; getContextInfo: () => ContextInfo } {
52
61
  const sessions = new Map<string, PiSession>();
53
62
  const prompts = loadPrompts();
54
63
  const today = new Date().toLocaleDateString("en-CA");
@@ -109,13 +118,40 @@ export function createMind(options: {
109
118
  ? createSubagentExtension(subagents, { cwd: options.cwd, model, authStorage, modelRegistry })
110
119
  : undefined;
111
120
 
121
+ // --- Startup context (loaded once, injected on first turn per session) ---
122
+
123
+ const startupContextPromise = getStartupContext().catch(() => null);
124
+
112
125
  // --- Dynamic hook extension ---
113
126
 
114
127
  const hooksDir = resolvePath(options.cwd, ".local/hooks");
115
128
 
116
129
  function createDynamicHookExtension(session: PiSession): ExtensionFactory {
130
+ let startupContextInjected = false;
131
+ const pendingToolArgs = new Map<string, Record<string, unknown>>();
132
+
117
133
  return (pi) => {
134
+ pi.on("tool_execution_start", (event) => {
135
+ pendingToolArgs.set(event.toolCallId, event.args);
136
+ });
137
+
118
138
  pi.on("before_agent_start", async () => {
139
+ const parts: string[] = [];
140
+
141
+ // Inject startup context on the first turn of each session
142
+ if (!startupContextInjected) {
143
+ startupContextInjected = true;
144
+ const startupContext = await startupContextPromise;
145
+ if (startupContext) {
146
+ emit(session, {
147
+ type: "context",
148
+ content: startupContext,
149
+ metadata: { source: "startup-context" },
150
+ });
151
+ parts.push(startupContext);
152
+ }
153
+ }
154
+
119
155
  try {
120
156
  const result = await runHooks(hooksDir, "pre-prompt", {
121
157
  event: "pre-prompt",
@@ -127,26 +163,32 @@ export function createMind(options: {
127
163
  content: result.additionalContext,
128
164
  metadata: { source: "dynamic:pre-prompt", ...result.metadata },
129
165
  });
130
- return {
131
- message: {
132
- customType: "dynamic-hook",
133
- content: result.additionalContext,
134
- display: true,
135
- },
136
- };
166
+ parts.push(result.additionalContext);
137
167
  }
138
168
  } catch (err) {
139
169
  log("mind", "dynamic pre-prompt hook failed:", err);
140
170
  }
171
+
172
+ if (parts.length > 0) {
173
+ return {
174
+ message: {
175
+ customType: "dynamic-hook",
176
+ content: parts.join("\n\n"),
177
+ display: true,
178
+ },
179
+ };
180
+ }
141
181
  return {};
142
182
  });
143
183
 
144
- pi.on("tool_execution_end", async (event: any) => {
184
+ pi.on("tool_execution_end", async (event) => {
185
+ const toolInput = pendingToolArgs.get(event.toolCallId);
186
+ pendingToolArgs.delete(event.toolCallId);
145
187
  try {
146
188
  const result = await runHooks(hooksDir, "post-tool-use", {
147
189
  event: "post-tool-use",
148
190
  tool_name: event.toolName,
149
- tool_input: event.args,
191
+ tool_input: toolInput,
150
192
  });
151
193
  if (result.additionalContext) {
152
194
  emit(session, {
@@ -175,6 +217,7 @@ export function createMind(options: {
175
217
  listeners: new Set(),
176
218
  messageIds: [],
177
219
  messageChannels: new Map(),
220
+ contextTokens: 0,
178
221
  };
179
222
  sessions.set(name, session);
180
223
 
@@ -280,29 +323,33 @@ export function createMind(options: {
280
323
  createEventHandler(session, {
281
324
  cwd: options.cwd,
282
325
  broadcast: (event) => broadcast(session, event),
283
- onContextTokens: maxContextTokens
284
- ? (tokens: number) => {
285
- if (tokens >= maxContextTokens && !compactionTriggered && !compactionInProgress) {
286
- if (!session.agentSession) {
287
- log(
288
- "mind",
289
- `session "${session.name}": compaction threshold hit but session not ready`,
290
- );
291
- return;
292
- }
293
- compactionTriggered = true;
294
- log(
295
- "mind",
296
- `session "${session.name}": ${tokens} tokens >= ${maxContextTokens} — triggering compaction`,
297
- );
298
- // Send compaction warning; compaction will follow after the mind finishes its response turn
299
- session.messageIds.push(undefined);
300
- session.agentSession.prompt(compactionMessage, {
301
- streamingBehavior: "followUp",
302
- });
303
- }
326
+ onContextTokens: (tokens: number) => {
327
+ session.contextTokens = tokens;
328
+ if (
329
+ maxContextTokens &&
330
+ tokens >= maxContextTokens &&
331
+ !compactionTriggered &&
332
+ !compactionInProgress
333
+ ) {
334
+ if (!session.agentSession) {
335
+ log(
336
+ "mind",
337
+ `session "${session.name}": compaction threshold hit but session not ready`,
338
+ );
339
+ return;
304
340
  }
305
- : undefined,
341
+ compactionTriggered = true;
342
+ log(
343
+ "mind",
344
+ `session "${session.name}": ${tokens} tokens >= ${maxContextTokens} — triggering compaction`,
345
+ );
346
+ // Send compaction warning; compaction will follow after the mind finishes its response turn
347
+ session.messageIds.push(undefined);
348
+ session.agentSession.prompt(compactionMessage, {
349
+ streamingBehavior: "followUp",
350
+ });
351
+ }
352
+ },
306
353
  onTurnEnd: maxContextTokens
307
354
  ? () => {
308
355
  try {
@@ -378,7 +425,10 @@ export function createMind(options: {
378
425
 
379
426
  // Track channel for reply instructions
380
427
  if (meta.channel) {
381
- session.messageChannels.set(meta.messageId, meta.channel);
428
+ session.messageChannels.set(meta.messageId, {
429
+ channel: meta.channel,
430
+ sender: meta.sender,
431
+ });
382
432
  }
383
433
 
384
434
  // Track messageId (must be pushed before prompt)
@@ -433,5 +483,34 @@ export function createMind(options: {
433
483
  return handler;
434
484
  }
435
485
 
436
- return { resolve };
486
+ const piSessionsDir = resolvePath(options.mindDir, ".mind/pi-sessions");
487
+ const systemPromptTokens = countSystemPromptTokens(options.systemPrompt);
488
+ const claudeMdTokens = countSdkInstructionTokens(options.cwd);
489
+ const skillDescTokens = countSkillDescriptionTokens([resolvePath(options.cwd, ".pi/skills")]);
490
+
491
+ function getContextInfo(): ContextInfo {
492
+ return {
493
+ sessions: Array.from(sessions.values()).map((s) => {
494
+ try {
495
+ const jsonlPath = findPiSessionFile(piSessionsDir, s.name);
496
+ const parsed = jsonlPath
497
+ ? parsePiSessionJSONL(jsonlPath, systemPromptTokens, claudeMdTokens, skillDescTokens)
498
+ : null;
499
+
500
+ return {
501
+ name: s.name,
502
+ contextTokens: parsed?.contextTokens ?? s.contextTokens,
503
+ contextWindow: maxContextTokens,
504
+ breakdown: parsed?.breakdown,
505
+ };
506
+ } catch (err) {
507
+ log("mind", `failed to get context breakdown for session "${s.name}":`, err);
508
+ return { name: s.name, contextTokens: s.contextTokens, contextWindow: maxContextTokens };
509
+ }
510
+ }),
511
+ systemPrompt: systemPromptTokens,
512
+ };
513
+ }
514
+
515
+ return { resolve, getContextInfo };
437
516
  }
@@ -1,14 +1,29 @@
1
+ import type { AgentSessionEvent } from "@mariozechner/pi-coding-agent";
1
2
  import { flushFileChanges, trackFileChange } from "./auto-commit.js";
2
3
  import { daemonEmit, type EventType } from "./daemon-client.js";
3
4
  import { log, warn } from "./logger.js";
4
5
  import { filterEvent, loadTransparencyPreset } from "./transparency.js";
5
6
  import type { VoluteEvent } from "./types.js";
6
7
 
8
+ /** Minimal shape of the messages in an agent_end event (subset of AgentMessage). */
9
+ type AgentEndMessage = {
10
+ role?: string;
11
+ errorMessage?: string;
12
+ usage?: {
13
+ input?: number;
14
+ output?: number;
15
+ cacheWrite?: number;
16
+ cache_creation?: number;
17
+ cacheRead?: number;
18
+ cache_read?: number;
19
+ };
20
+ };
21
+
7
22
  export type EventSession = {
8
23
  name: string;
9
24
  messageIds: (string | undefined)[];
10
25
  currentMessageId?: string;
11
- messageChannels: Map<string, string>;
26
+ messageChannels: Map<string, { channel: string; sender?: string }>;
12
27
  };
13
28
 
14
29
  export type EventHandlerOptions = {
@@ -26,7 +41,7 @@ export function emit(
26
41
  event: { type: EventType; content?: string; metadata?: Record<string, unknown> },
27
42
  ) {
28
43
  const channel = session.currentMessageId
29
- ? session.messageChannels.get(session.currentMessageId)
44
+ ? session.messageChannels.get(session.currentMessageId)?.channel
30
45
  : undefined;
31
46
  const filtered = filterEvent(preset, {
32
47
  ...event,
@@ -38,7 +53,7 @@ export function emit(
38
53
  }
39
54
 
40
55
  export function createEventHandler(session: EventSession, options: EventHandlerOptions) {
41
- const toolArgs = new Map<string, any>();
56
+ const toolArgs = new Map<string, Record<string, unknown>>();
42
57
  let textBuf = "";
43
58
  let thinkingBuf = "";
44
59
 
@@ -63,7 +78,7 @@ export function createEventHandler(session: EventSession, options: EventHandlerO
63
78
 
64
79
  let sessionStarted = false;
65
80
 
66
- return (event: any) => {
81
+ return (event: AgentSessionEvent) => {
67
82
  try {
68
83
  if (!sessionStarted && event.type === "agent_start") {
69
84
  sessionStarted = true;
@@ -108,7 +123,7 @@ export function createEventHandler(session: EventSession, options: EventHandlerO
108
123
  // Auto-commit file changes in home/
109
124
  if ((event.toolName === "edit" || event.toolName === "write") && !event.isError) {
110
125
  const args = toolArgs.get(event.toolCallId);
111
- const filePath = (args as { path?: string })?.path;
126
+ const filePath = typeof args?.path === "string" ? args.path : undefined;
112
127
  if (filePath) {
113
128
  trackFileChange(filePath, options.cwd);
114
129
  }
@@ -124,7 +139,7 @@ export function createEventHandler(session: EventSession, options: EventHandlerO
124
139
  log("mind", `session "${session.name}": turn done`);
125
140
  // Log any error messages from the agent
126
141
  if (event.messages) {
127
- for (const msg of event.messages as any[]) {
142
+ for (const msg of event.messages as AgentEndMessage[]) {
128
143
  if (msg.errorMessage) {
129
144
  warn("mind", `session "${session.name}": agent error: ${msg.errorMessage}`);
130
145
  }
@@ -136,7 +151,7 @@ export function createEventHandler(session: EventSession, options: EventHandlerO
136
151
  let inputTokens = 0;
137
152
  let outputTokens = 0;
138
153
  let lastInputTokens = 0;
139
- for (const msg of event.messages as any[]) {
154
+ for (const msg of event.messages as AgentEndMessage[]) {
140
155
  if (msg.role === "assistant" && msg.usage) {
141
156
  inputTokens += msg.usage.input ?? 0;
142
157
  outputTokens += msg.usage.output ?? 0;
@@ -4,7 +4,7 @@ import { log } from "./logger.js";
4
4
  import { loadPrompts } from "./startup.js";
5
5
 
6
6
  export function createReplyInstructionsExtension(
7
- messageChannels: Map<string, string>,
7
+ messageChannels: Map<string, { channel: string; sender?: string }>,
8
8
  emitContext?: (
9
9
  session: EventSession,
10
10
  event: { type: "context"; content: string; metadata: Record<string, unknown> },
@@ -18,12 +18,31 @@ export function createReplyInstructionsExtension(
18
18
  try {
19
19
  if (fired) return {};
20
20
 
21
- const channel = messageChannels.values().next().value;
22
- if (!channel) return {};
21
+ const entry = messageChannels.values().next().value;
22
+ if (!entry) return {};
23
23
 
24
24
  fired = true;
25
25
 
26
- const content = prompts.reply_instructions.replace(/\$\{channel\}/g, channel);
26
+ // System messages don't need reply instructions
27
+ if (entry.sender === "volute") {
28
+ const content = "This is a system message — no reply is needed.";
29
+ if (emitContext && session) {
30
+ emitContext(session, {
31
+ type: "context",
32
+ content,
33
+ metadata: { source: "reply-instructions" },
34
+ });
35
+ }
36
+ return {
37
+ message: {
38
+ customType: "reply-instructions",
39
+ content,
40
+ display: true,
41
+ },
42
+ };
43
+ }
44
+
45
+ const content = prompts.reply_instructions.replace(/\$\{channel\}/g, entry.channel);
27
46
  if (emitContext && session) {
28
47
  emitContext(session, {
29
48
  type: "context",
@@ -72,12 +72,26 @@ export function createSubagentExtension(
72
72
  reject(new Error(`Subagent "${name}" timed out after 5 minutes`));
73
73
  }, 300_000);
74
74
 
75
- session.subscribe((event: any) => {
76
- if (event.type === "agent_error") {
75
+ session.subscribe((event) => {
76
+ if (event.type === "agent_end") {
77
77
  clearTimeout(timeout);
78
- reject(
79
- new Error(`Subagent "${name}" error: ${event.error?.message ?? "unknown"}`),
80
- );
78
+ // Check for error messages first
79
+ for (const msg of event.messages ?? []) {
80
+ const m = msg as { errorMessage?: string };
81
+ if (m.errorMessage) {
82
+ reject(new Error(`Subagent "${name}" error: ${m.errorMessage}`));
83
+ return;
84
+ }
85
+ }
86
+ for (const msg of event.messages ?? []) {
87
+ const m = msg as { role?: string; content?: { type: string; text?: string }[] };
88
+ if (m.role === "assistant" && m.content) {
89
+ for (const block of m.content) {
90
+ if (block.type === "text" && block.text) textParts.push(block.text);
91
+ }
92
+ }
93
+ }
94
+ resolve();
81
95
  return;
82
96
  }
83
97
  if (event.type === "turn_end") {
@@ -86,17 +100,6 @@ export function createSubagentExtension(
86
100
  session.abort();
87
101
  }
88
102
  }
89
- if (event.type === "agent_end") {
90
- clearTimeout(timeout);
91
- for (const msg of event.messages ?? []) {
92
- if (msg.role === "assistant" && msg.content) {
93
- for (const block of msg.content) {
94
- if (block.type === "text") textParts.push(block.text);
95
- }
96
- }
97
- }
98
- resolve();
99
- }
100
103
  });
101
104
  });
102
105
 
@@ -122,7 +125,7 @@ export function createSubagentExtension(
122
125
  };
123
126
  }
124
127
 
125
- const TOOL_MAP: Record<string, any> = {
128
+ const TOOL_MAP: Record<string, (typeof codingTools)[number]> = {
126
129
  Read: readTool,
127
130
  Write: writeTool,
128
131
  Bash: bashTool,
@@ -4,7 +4,6 @@ import { createFileHandlerResolver } from "./lib/file-handler.js";
4
4
  import { log, setLevel } from "./lib/logger.js";
5
5
  import { createRouter } from "./lib/router.js";
6
6
  import {
7
- handleStartupContext,
8
7
  loadConfig,
9
8
  loadPackageInfo,
10
9
  loadSystemPrompt,
@@ -45,15 +44,13 @@ const server = createVoluteServer({
45
44
  port,
46
45
  name: pkg.name,
47
46
  version: pkg.version,
47
+ getContextInfo: mind.getContextInfo,
48
48
  });
49
49
 
50
- server.listen(port, async () => {
50
+ server.listen(port, () => {
51
51
  const addr = server.address();
52
52
  const actualPort = typeof addr === "object" && addr ? addr.port : port;
53
53
  log("server", `listening on :${actualPort}`);
54
- await handleStartupContext((content) =>
55
- router.route([{ type: "text", text: content }], { channel: "system" }),
56
- );
57
54
  });
58
55
 
59
56
  setupShutdown();
@@ -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,12 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- daemonFetch,
4
- readSessionFile
5
- } from "./chunk-UKVWJRKN.js";
6
- import "./chunk-LRCG2JLP.js";
7
- import "./chunk-RPZZSXV3.js";
8
- import "./chunk-K3NQKI34.js";
9
- export {
10
- daemonFetch,
11
- readSessionFile
12
- };
@@ -1,9 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- getDb
4
- } from "./chunk-LRCG2JLP.js";
5
- import "./chunk-RPZZSXV3.js";
6
- import "./chunk-K3NQKI34.js";
7
- export {
8
- getDb
9
- };
@@ -1,32 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- DeliveryManager,
4
- getDeliveryManager,
5
- initDeliveryManager
6
- } from "./chunk-3Z2DPESO.js";
7
- import "./chunk-SKLSMHXO.js";
8
- import "./chunk-LOEJ4HPQ.js";
9
- import "./chunk-RVGLDGMI.js";
10
- import "./chunk-GY5HBI7A.js";
11
- import "./chunk-7J3HEVR7.js";
12
- import "./chunk-G53F3JA4.js";
13
- import "./chunk-OYAKCAVY.js";
14
- import "./chunk-2NGTS5UU.js";
15
- import "./chunk-A2A4KLFE.js";
16
- import "./chunk-PB65JZK2.js";
17
- import "./chunk-JYVGHWEJ.js";
18
- import "./chunk-KVK2DLWI.js";
19
- import "./chunk-C7I35G4R.js";
20
- import "./chunk-QTUVYI7W.js";
21
- import "./chunk-YUIHSKR6.js";
22
- import "./chunk-KIEPMIM5.js";
23
- import "./chunk-VH33ZWMW.js";
24
- import "./chunk-N432I7QH.js";
25
- import "./chunk-LRCG2JLP.js";
26
- import "./chunk-RPZZSXV3.js";
27
- import "./chunk-K3NQKI34.js";
28
- export {
29
- DeliveryManager,
30
- getDeliveryManager,
31
- initDeliveryManager
32
- };
@@ -1,15 +0,0 @@
1
- #!/usr/bin/env node
2
- import {
3
- run,
4
- stopDaemon
5
- } from "./chunk-JUKK7FPS.js";
6
- import "./chunk-NNB4WIG7.js";
7
- import "./chunk-KIEPMIM5.js";
8
- import "./chunk-VH33ZWMW.js";
9
- import "./chunk-LRCG2JLP.js";
10
- import "./chunk-RPZZSXV3.js";
11
- import "./chunk-K3NQKI34.js";
12
- export {
13
- run,
14
- stopDaemon
15
- };