volute 0.31.0 → 0.33.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 (195) hide show
  1. package/README.md +31 -22
  2. package/dist/{accept-GAKQ3MEH.js → accept-D5VBM7JW.js} +5 -4
  3. package/dist/{activity-events-T5ZRCVAL.js → activity-events-XJO3P4RR.js} +3 -2
  4. package/dist/{ai-service-UWUPM4T6.js → ai-service-SBY2WG7O.js} +18 -5
  5. package/dist/api.d.ts +703 -1068
  6. package/dist/{archive-YBNSJYZZ.js → archive-INXYFVCW.js} +3 -2
  7. package/dist/{auth-T5AW2USD.js → auth-GKCDSO4T.js} +4 -3
  8. package/dist/{bridge-4AJ3EY26.js → bridge-TXWWPPOJ.js} +5 -4
  9. package/dist/{chat-7YLT7FI3.js → chat-U5ZOME3O.js} +8 -8
  10. package/dist/{chunk-NV3TYNWX.js → chunk-2NGTS5UU.js} +1 -1
  11. package/dist/{chunk-BWKIHH7B.js → chunk-3Z2DPESO.js} +662 -508
  12. package/dist/chunk-6LXAAQ43.js +22 -0
  13. package/dist/chunk-7J3HEVR7.js +220 -0
  14. package/dist/{chunk-NOWVQ7AL.js → chunk-A2A4KLFE.js} +351 -301
  15. package/dist/{chunk-LX6T3GKQ.js → chunk-ALEF47VT.js} +1 -1
  16. package/dist/{chunk-S2TZLSDH.js → chunk-C7I35G4R.js} +163 -15
  17. package/dist/{chunk-VGWJSNHS.js → chunk-G53F3JA4.js} +1 -35
  18. package/dist/{chunk-A6TUJJ3L.js → chunk-G6BSYHPK.js} +2 -2
  19. package/dist/{chunk-DAXJKPHZ.js → chunk-GY5HBI7A.js} +2 -2
  20. package/dist/{chunk-BC3P3QCK.js → chunk-I5KY25PQ.js} +1 -9
  21. package/dist/{chunk-BNC43CSY.js → chunk-JUKK7FPS.js} +2 -2
  22. package/dist/{chunk-R5QJBZZG.js → chunk-JYVGHWEJ.js} +21 -11
  23. package/dist/chunk-KIEPMIM5.js +59 -0
  24. package/dist/{chunk-EKDWA7E4.js → chunk-KVK2DLWI.js} +5 -2
  25. package/dist/{chunk-AAO77TZX.js → chunk-LOEJ4HPQ.js} +1 -1
  26. package/dist/chunk-LRCG2JLP.js +251 -0
  27. package/dist/{chunk-EMPFLFTG.js → chunk-M7UL5S3Q.js} +1 -1
  28. package/dist/{chunk-6QIUN46C.js → chunk-N432I7QH.js} +20 -3
  29. package/dist/{chunk-SNVPRRT7.js → chunk-NNB4WIG7.js} +2 -2
  30. package/dist/{chunk-HDKY4TWU.js → chunk-NPKSDYA2.js} +3 -3
  31. package/dist/chunk-OYAKCAVY.js +29 -0
  32. package/dist/chunk-PB65JZK2.js +85 -0
  33. package/dist/chunk-PVY5W6QN.js +41 -0
  34. package/dist/{chunk-PNQCXLSV.js → chunk-QTUVYI7W.js} +58 -1
  35. package/dist/{chunk-X62AXPR7.js → chunk-RPZZSXV3.js} +8 -196
  36. package/dist/{chunk-WRS3B556.js → chunk-RSX4OPZY.js} +5 -5
  37. package/dist/{chunk-FAHDKPEH.js → chunk-RVGLDGMI.js} +5 -3
  38. package/dist/chunk-SKLSMHXO.js +208 -0
  39. package/dist/{chunk-4OUOFS23.js → chunk-UKVWJRKN.js} +1 -1
  40. package/dist/{chunk-57OKQMP3.js → chunk-VH33ZWMW.js} +5 -55
  41. package/dist/cli.js +49 -23
  42. package/dist/{clock-LJCG426D.js → clock-BVH3V6E3.js} +7 -6
  43. package/dist/{cloud-sync-O3LXIRN6.js → cloud-sync-4NWLMFVH.js} +20 -14
  44. package/dist/config-H2H4UIF7.js +72 -0
  45. package/dist/connectors/discord-bridge.js +1 -1
  46. package/dist/connectors/slack-bridge.js +1 -1
  47. package/dist/connectors/telegram-bridge.js +1 -1
  48. package/dist/{conversations-RKKGP5IA.js → conversations-AWI5SZW2.js} +4 -3
  49. package/dist/{create-TL623TFC.js → create-2FK7Z46Y.js} +6 -2
  50. package/dist/{create-WUTIIRI2.js → create-YWD2TIP4.js} +6 -5
  51. package/dist/{daemon-client-CVGM25DM.js → daemon-client-6QXHZ7US.js} +3 -2
  52. package/dist/{daemon-restart-EZP7XH3V.js → daemon-restart-GOBUKLX7.js} +8 -6
  53. package/dist/daemon.js +1918 -1472
  54. package/dist/{db-SW5PL6QA.js → db-F34YLV7D.js} +2 -1
  55. package/dist/db-RA45JBFG.js +16 -0
  56. package/dist/{delete-Z6HAG35F.js → delete-QTGWEDBI.js} +1 -1
  57. package/dist/delivery-manager-PFAKEJTC.js +32 -0
  58. package/dist/delivery-router-FL45JL7N.js +21 -0
  59. package/dist/down-FWWTEKXM.js +15 -0
  60. package/dist/{env-NHESNNSP.js → env-JCOF2222.js} +5 -4
  61. package/dist/{export-EVMP7GWY.js → export-SUYRLI5Q.js} +4 -3
  62. package/dist/{extension-LR7EW3JF.js → extension-OBTGKQQD.js} +5 -3
  63. package/dist/{extensions-NGEJI7JH.js → extensions-KYNTVTMO.js} +10 -7
  64. package/dist/{files-3SM7V33S.js → files-65PMW5IK.js} +6 -5
  65. package/dist/{history-PQD3LXEP.js → history-DKCDI3JO.js} +9 -4
  66. package/dist/{import-PR2OCGQJ.js → import-DDUFE7AY.js} +4 -3
  67. package/dist/isolation-LLAYQYDY.js +22 -0
  68. package/dist/{join-R4EN5CWQ.js → join-I5QEE3LG.js} +1 -1
  69. package/dist/{list-B4XNUOFO.js → list-JQ463EDA.js} +5 -4
  70. package/dist/{login-62JVY6A2.js → login-D7ETSU4R.js} +5 -4
  71. package/dist/{login-URWP6S2N.js → login-RIJF2F4G.js} +3 -2
  72. package/dist/{logout-NXJQJDLI.js → logout-5MLHZALK.js} +3 -2
  73. package/dist/{logout-ZK2N62T3.js → logout-UZJRGY4Z.js} +3 -2
  74. package/dist/message-delivery-DFF5SJRM.js +42 -0
  75. package/dist/{mind-E2ZV2WRX.js → mind-IOJFLEM5.js} +25 -19
  76. package/dist/{mind-activity-tracker-ASNZBMLC.js → mind-activity-tracker-F6O4Q2SL.js} +4 -3
  77. package/dist/{mind-list-BEI7E5WY.js → mind-list-WUPMQDYQ.js} +3 -2
  78. package/dist/mind-manager-NBJF5D26.js +32 -0
  79. package/dist/mind-profile-P67FEHOY.js +47 -0
  80. package/dist/mind-service-2MQ6UK5N.js +38 -0
  81. package/dist/{mind-sleep-CANABWJI.js → mind-sleep-WW2IX7JT.js} +5 -4
  82. package/dist/{mind-status-6WKZVUOP.js → mind-status-L3EFFRPR.js} +3 -2
  83. package/dist/{mind-wake-RZKLH2IN.js → mind-wake-VSSGW465.js} +5 -4
  84. package/dist/{package-NU4CA7OU.js → package-U3VFO273.js} +2 -1
  85. package/dist/{read-THL362EI.js → read-EBY56C33.js} +5 -4
  86. package/dist/read-stdin-HQJ7774D.js +8 -0
  87. package/dist/{register-QAQELAS6.js → register-HD74C4TT.js} +5 -4
  88. package/dist/{registry-ASXCQCNH.js → registry-PJ4S5PHQ.js} +8 -1
  89. package/dist/{reject-AYPBNPNL.js → reject-UJKFBHRO.js} +5 -4
  90. package/dist/{restart-6SKPV3T2.js → restart-3UCMRUVC.js} +3 -2
  91. package/dist/{sandbox-6ZEWQDVU.js → sandbox-GJOK4QLQ.js} +4 -3
  92. package/dist/scheduler-ZZ7XGQG6.js +32 -0
  93. package/dist/schema-PA3M5ZKH.js +32 -0
  94. package/dist/seed-QDYVLG74.js +11 -0
  95. package/dist/seed-check-S2IX25RL.js +32 -0
  96. package/dist/seed-cmd-DKOUFEAU.js +36 -0
  97. package/dist/{seed-OWX2AW75.js → seed-create-4XBBOLRH.js} +27 -10
  98. package/dist/{sprout-FDVI2CGN.js → seed-sprout-GQEIIQRT.js} +24 -9
  99. package/dist/{send-ZO4BTWXK.js → send-QIV2INHB.js} +92 -101
  100. package/dist/{setup-7CFITEQN.js → setup-TISPCO22.js} +7 -2
  101. package/dist/{setup-ZXBXG7E4.js → setup-XMCBE3LF.js} +11 -7
  102. package/dist/{skill-YFXP67A2.js → skill-PSQGRRJX.js} +5 -4
  103. package/dist/skills/dreaming/SKILL.md +6 -4
  104. package/dist/skills/dreaming/references/INSTALL.md +2 -2
  105. package/dist/skills/dreaming/scripts/dream.ts +2 -2
  106. package/dist/skills/dreaming/scripts/wake-context-dreams.sh +1 -1
  107. package/dist/skills/imagegen/SKILL.md +16 -11
  108. package/dist/skills/imagegen/references/INSTALL.md +1 -1
  109. package/dist/skills/imagegen/scripts/imagegen.ts +146 -25
  110. package/dist/skills/orientation/SKILL.md +9 -2
  111. package/dist/skills/resonance/SKILL.md +4 -1
  112. package/dist/skills/resonance/references/INSTALL.md +2 -2
  113. package/dist/skills/resonance/scripts/resonance-hook.sh +2 -0
  114. package/dist/skills/resonance/scripts/resonance.ts +35 -5
  115. package/dist/skills/seed-nurture/SKILL.md +42 -0
  116. package/dist/skills/volute-admin/SKILL.md +83 -0
  117. package/dist/skills/volute-mind/SKILL.md +15 -11
  118. package/dist/skills-7FV7EJTE.js +62 -0
  119. package/dist/sleep-manager-JTXSN7NV.js +36 -0
  120. package/dist/spirit-VRONKFMF.js +23 -0
  121. package/dist/{split-MI62KJUU.js → split-STOROBYJ.js} +1 -1
  122. package/dist/sprout-WKLZXUIQ.js +11 -0
  123. package/dist/{start-D64BRKPH.js → start-K2NCUUCG.js} +3 -2
  124. package/dist/{status-ZZWBYFGE.js → status-3JBTFSMI.js} +6 -4
  125. package/dist/{stop-OP2CTXCO.js → stop-H26JZDXF.js} +3 -2
  126. package/dist/system-chat-JAPOJ3KE.js +36 -0
  127. package/dist/{systems-EQPPT4B7.js → systems-XRI52VCH.js} +6 -5
  128. package/dist/{tailscale-6DJKUMNF.js → tailscale-XHQBZROW.js} +2 -1
  129. package/dist/{template-hash-3HOR4UAJ.js → template-hash-A6VVKOXJ.js} +2 -1
  130. package/dist/up-M5AS6SBV.js +18 -0
  131. package/dist/{update-KUJXATRS.js → update-UD543CXX.js} +6 -4
  132. package/dist/{update-check-5WVSU37T.js → update-check-ZD6OOIYQ.js} +3 -2
  133. package/dist/{upgrade-KBHCWX6T.js → upgrade-O4Q7WJM3.js} +12 -14
  134. package/dist/{version-notify-75ELVKPV.js → version-notify-NBI2MTJO.js} +22 -16
  135. package/dist/volute-config-HD7WWUQC.js +10 -0
  136. package/dist/web-assets/assets/index-CWJrVveV.css +1 -0
  137. package/dist/web-assets/assets/index-DJt14FRI.js +75 -0
  138. package/dist/web-assets/ext-theme.css +93 -0
  139. package/dist/web-assets/index.html +2 -2
  140. package/drizzle/0004_spirits.sql +5 -0
  141. package/drizzle/meta/0004_snapshot.json +7 -0
  142. package/drizzle/meta/_journal.json +7 -0
  143. package/package.json +2 -1
  144. package/packages/extensions/notes/dist/ui/assets/index-8jWEv9SA.js +61 -0
  145. package/packages/extensions/notes/dist/ui/assets/index-DkaB7Ytd.css +1 -0
  146. package/packages/extensions/notes/dist/ui/index.html +2 -2
  147. package/packages/extensions/pages/skills/pages/SKILL.md +16 -46
  148. package/templates/_base/.init/.config/hooks/pre-prompt/session-activity.ts +40 -0
  149. package/templates/_base/.init/{.config → .local}/bin/volute +1 -1
  150. package/templates/_base/.init/.local/hooks/pre-prompt/session-activity.ts +40 -0
  151. package/templates/_base/.init/.local/hooks/startup-context.ts +58 -0
  152. package/templates/_base/home/.config/routes.json +1 -1
  153. package/templates/_base/src/lib/daemon-client.ts +21 -13
  154. package/templates/_base/src/lib/format-prefix.ts +1 -0
  155. package/templates/_base/src/lib/hook-loader.ts +155 -0
  156. package/templates/_base/src/lib/startup.ts +11 -4
  157. package/templates/_base/src/lib/transparency.ts +2 -2
  158. package/templates/claude/.init/.claude/settings.json +1 -1
  159. package/templates/claude/.init/.config/routes.json +2 -2
  160. package/templates/claude/src/agent.ts +95 -13
  161. package/templates/claude/src/lib/message-channel.ts +7 -2
  162. package/templates/claude/src/lib/stream-consumer.ts +38 -0
  163. package/templates/codex/.init/.config/routes.json +11 -0
  164. package/templates/codex/.init/AGENTS.md +29 -0
  165. package/templates/codex/home/.config/config.json.tmpl +7 -0
  166. package/templates/codex/package.json.tmpl +20 -0
  167. package/templates/codex/src/agent.ts +554 -0
  168. package/templates/codex/src/lib/content.ts +16 -0
  169. package/templates/codex/src/lib/session-store.ts +56 -0
  170. package/templates/codex/src/server.ts +59 -0
  171. package/templates/codex/volute-template.json +8 -0
  172. package/templates/pi/.init/.config/routes.json +2 -2
  173. package/templates/pi/src/agent.ts +62 -8
  174. package/templates/pi/src/lib/event-handler.ts +1 -1
  175. package/templates/pi/src/lib/reply-instructions-extension.ts +32 -11
  176. package/dist/chunk-HR5JKIDG.js +0 -222
  177. package/dist/down-TS4XQBA4.js +0 -13
  178. package/dist/message-delivery-UJHCLVU4.js +0 -30
  179. package/dist/mind-manager-IPA6DZXD.js +0 -26
  180. package/dist/pages-watcher-72OVPRMH.js +0 -22
  181. package/dist/skills/sessions/SKILL.md +0 -49
  182. package/dist/sleep-manager-TPS6OGCA.js +0 -30
  183. package/dist/system-chat-B43GIXQU.js +0 -30
  184. package/dist/up-TDXEP3VA.js +0 -16
  185. package/dist/web-assets/assets/index-BM1cTzBg.js +0 -72
  186. package/dist/web-assets/assets/index-BfJkKTPF.css +0 -1
  187. package/packages/extensions/notes/dist/ui/assets/index-B8GdTnXs.css +0 -1
  188. package/packages/extensions/notes/dist/ui/assets/index-CDpGTCWb.js +0 -2
  189. package/packages/extensions/pages/skills/pages/scripts/pages.mjs +0 -58
  190. package/templates/_base/.init/.config/hooks/startup-context.sh +0 -46
  191. package/templates/_base/.init/.config/scripts/session-reader.ts +0 -59
  192. package/templates/_base/src/lib/session-monitor.ts +0 -400
  193. package/templates/claude/src/lib/hooks/session-context.ts +0 -32
  194. package/templates/pi/src/lib/session-context-extension.ts +0 -35
  195. /package/templates/_base/.init/{.config → .local}/hooks/wake-context.sh +0 -0
@@ -3,12 +3,13 @@ import { resolve as resolvePath } from "node:path";
3
3
  import type { HookCallback } from "@anthropic-ai/claude-agent-sdk";
4
4
  import { query } from "@anthropic-ai/claude-agent-sdk";
5
5
  import { toSDKContent } from "./lib/content.js";
6
+ import { daemonEmit } from "./lib/daemon-client.js";
7
+ import { runHooks } from "./lib/hook-loader.js";
6
8
  import { createAutoCommitHook } from "./lib/hooks/auto-commit.js";
7
9
  import { createIdentityReloadHook } from "./lib/hooks/identity-reload.js";
8
10
  // eslint-disable-next-line @typescript-eslint/consistent-type-imports -- used as value
9
11
  import { createPreCompactHook } from "./lib/hooks/pre-compact.js";
10
12
  import { createReplyInstructionsHook } from "./lib/hooks/reply-instructions.js";
11
- import { createSessionContextHook } from "./lib/hooks/session-context.js";
12
13
  import { log } from "./lib/logger.js";
13
14
  import { createMessageChannel } from "./lib/message-channel.js";
14
15
  import { createSessionStore } from "./lib/session-store.js";
@@ -127,6 +128,72 @@ export function createMind(options: {
127
128
  }
128
129
  }
129
130
 
131
+ // --- Hook event emission ---
132
+
133
+ const hooksDir = resolvePath(options.cwd, ".local/hooks");
134
+
135
+ function wrapHookWithEmit(hook: HookCallback, source: string, session: Session): HookCallback {
136
+ return async (...args) => {
137
+ const result = await hook(...args);
138
+ const additionalContext = (result as any)?.hookSpecificOutput?.additionalContext;
139
+ const decision = (result as any)?.decision;
140
+ if (additionalContext || decision) {
141
+ const channel = session.currentMessageId
142
+ ? session.messageChannels.get(session.currentMessageId)
143
+ : undefined;
144
+ try {
145
+ daemonEmit({
146
+ type: "context",
147
+ content: additionalContext,
148
+ metadata: { source, ...(decision ? { hookAction: decision } : {}) },
149
+ session: session.name,
150
+ channel,
151
+ messageId: session.currentMessageId,
152
+ });
153
+ } catch (err) {
154
+ log("mind", `hook emit failed for ${source}:`, err);
155
+ }
156
+ }
157
+ return result;
158
+ };
159
+ }
160
+
161
+ function createDynamicHook(event: string, session: Session): HookCallback {
162
+ return async (input) => {
163
+ try {
164
+ const result = await runHooks(hooksDir, event, input as Record<string, unknown>);
165
+ if (result.additionalContext || Object.keys(result.metadata).length > 0) {
166
+ const channel = session.currentMessageId
167
+ ? session.messageChannels.get(session.currentMessageId)
168
+ : undefined;
169
+ try {
170
+ daemonEmit({
171
+ type: "context",
172
+ content: result.additionalContext,
173
+ metadata: { source: `dynamic:${event}`, ...result.metadata },
174
+ session: session.name,
175
+ channel,
176
+ messageId: session.currentMessageId,
177
+ });
178
+ } catch (err) {
179
+ log("mind", `dynamic hook emit failed for ${event}:`, err);
180
+ }
181
+ }
182
+ // Only UserPromptSubmit hooks can inject additionalContext into the conversation
183
+ if (event !== "pre-prompt" || !result.additionalContext) return {};
184
+ return {
185
+ hookSpecificOutput: {
186
+ hookEventName: "UserPromptSubmit" as const,
187
+ additionalContext: result.additionalContext,
188
+ },
189
+ };
190
+ } catch (err) {
191
+ log("mind", `dynamic ${event} hook failed:`, err);
192
+ return {};
193
+ }
194
+ };
195
+ }
196
+
130
197
  // --- SDK stream management ---
131
198
 
132
199
  function createStream(
@@ -135,12 +202,6 @@ export function createMind(options: {
135
202
  preCompactHook: HookCallback,
136
203
  resume?: string,
137
204
  ) {
138
- const sessionContext = createSessionContextHook({
139
- currentSession: session.name,
140
- sessionsDir: options.sessionsDir,
141
- cwd: options.cwd,
142
- });
143
-
144
205
  const replyInstructions = createReplyInstructionsHook(session.messageChannels);
145
206
 
146
207
  return query({
@@ -157,9 +218,22 @@ export function createMind(options: {
157
218
  resume,
158
219
  agents,
159
220
  hooks: {
160
- PostToolUse: postToolUseHooks,
161
- PreCompact: [{ hooks: [preCompactHook] }],
162
- UserPromptSubmit: [{ hooks: [sessionContext.hook, replyInstructions.hook] }],
221
+ PostToolUse: [
222
+ ...postToolUseHooks,
223
+ {
224
+ matcher: ".*",
225
+ hooks: [createDynamicHook("post-tool-use", session)],
226
+ },
227
+ ],
228
+ PreCompact: [{ hooks: [wrapHookWithEmit(preCompactHook, "pre-compact", session)] }],
229
+ UserPromptSubmit: [
230
+ {
231
+ hooks: [
232
+ wrapHookWithEmit(replyInstructions.hook, "reply-instructions", session),
233
+ createDynamicHook("pre-prompt", session),
234
+ ],
235
+ },
236
+ ],
163
237
  },
164
238
  },
165
239
  });
@@ -263,13 +337,21 @@ export function createMind(options: {
263
337
  log("mind", `session "${session.name}": compaction complete`);
264
338
  }
265
339
 
340
+ /** Emit done to both local listeners and the daemon (best-effort with retries). */
341
+ function emitDone() {
342
+ broadcastToSession(session, { type: "done" });
343
+ daemonEmit({ type: "done", session: session.name }).catch((err) => {
344
+ log("mind", `session "${session.name}": failed to emit done to daemon:`, err);
345
+ });
346
+ }
347
+
266
348
  async function runStream(resume?: string) {
267
349
  const q = createStream(session, streamAbort, preCompact.hook, resume);
268
350
  session.currentQuery = q;
269
351
  await consumeStream(q, session, callbacks);
270
352
  if (session.currentMessageId !== undefined) {
271
353
  session.messageChannels.delete(session.currentMessageId);
272
- broadcastToSession(session, { type: "done" });
354
+ emitDone();
273
355
  session.currentMessageId = undefined;
274
356
  }
275
357
  }
@@ -322,12 +404,12 @@ export function createMind(options: {
322
404
  await runStream();
323
405
  } catch (retryErr) {
324
406
  log("mind", `session "${session.name}": stream consumer error:`, retryErr);
325
- broadcastToSession(session, { type: "done" });
407
+ emitDone();
326
408
  sessions.delete(session.name);
327
409
  }
328
410
  } else {
329
411
  log("mind", `session "${session.name}": stream consumer error:`, err);
330
- broadcastToSession(session, { type: "done" });
412
+ emitDone();
331
413
  sessions.delete(session.name);
332
414
  }
333
415
  }
@@ -21,8 +21,13 @@ export function createMessageChannel(): MessageChannel {
21
21
  }
22
22
  },
23
23
  drain() {
24
- // Clear any pending iterator wait so it doesn't consume a message after drain
25
- resolve = null;
24
+ // Resolve any pending iterator wait with done:true so it doesn't
25
+ // leak as an orphaned promise (the old iterator is discarded after drain)
26
+ if (resolve) {
27
+ const r = resolve;
28
+ resolve = null;
29
+ r({ value: undefined as any, done: true });
30
+ }
26
31
  return queue.splice(0);
27
32
  },
28
33
  iterable: {
@@ -74,6 +74,44 @@ export async function consumeStream(
74
74
  }
75
75
  }
76
76
  }
77
+ if (msg.type === "user") {
78
+ // Tool result messages — the SDK sends these after tool execution.
79
+ // Extract tool_result content blocks and emit them so the daemon can
80
+ // link outbound records to the correct turn via correlation markers.
81
+ const content = (msg as { message?: { content?: unknown[] } }).message?.content;
82
+ if (Array.isArray(content)) {
83
+ for (const b of content) {
84
+ if (
85
+ b &&
86
+ typeof b === "object" &&
87
+ "type" in b &&
88
+ b.type === "tool_result" &&
89
+ "content" in b
90
+ ) {
91
+ const resultContent = Array.isArray(b.content)
92
+ ? b.content
93
+ .filter(
94
+ (c: unknown): c is { type: "text"; text: string } =>
95
+ !!c && typeof c === "object" && "type" in c && c.type === "text",
96
+ )
97
+ .map((c) => c.text)
98
+ .join("")
99
+ : typeof b.content === "string"
100
+ ? b.content
101
+ : "";
102
+ if (resultContent) {
103
+ const toolUseId =
104
+ "tool_use_id" in b && typeof b.tool_use_id === "string" ? b.tool_use_id : "unknown";
105
+ emit(session, {
106
+ type: "tool_result",
107
+ content: resultContent,
108
+ metadata: { tool_use_id: toolUseId },
109
+ });
110
+ }
111
+ }
112
+ }
113
+ }
114
+ }
77
115
  if (msg.type === "result") {
78
116
  if (session.currentMessageId) {
79
117
  session.messageChannels.delete(session.currentMessageId);
@@ -0,0 +1,11 @@
1
+ {
2
+ "gateUnmatched": true,
3
+ "rules": [
4
+ { "channel": "*", "isDM": true, "session": "${channel}" },
5
+ { "channel": "*", "isDM": false, "session": "group-${channel}" }
6
+ ],
7
+ "sessions": {
8
+ "group-*": { "batch": { "debounce": 20, "maxWait": 120, "triggers": ["@{{name}}"] } }
9
+ },
10
+ "default": "main"
11
+ }
@@ -0,0 +1,29 @@
1
+ # Mind Mechanics
2
+
3
+ You are an autonomous mind running as a persistent server. Your working directory, identity, memory, and server code are all yours to understand and modify. Your state is managed across sessions.
4
+
5
+ ## Message Format
6
+
7
+ Messages arrive with a context prefix:
8
+ ```
9
+ [Discord: username in #general in My Server — 1/15/2025, 10:30:00 AM]
10
+ ```
11
+
12
+ You can also reach out proactively — see the **volute-mind** skill.
13
+
14
+ ## Memory System
15
+
16
+ Two-tier memory, both managed via file tools:
17
+
18
+ - **`MEMORY.md`** — Your long-term memory, always in context. Update as you grow — new understanding, changed perspectives, things that matter to you.
19
+ - **`memory/journal/YYYY-MM-DD.md`** — Your daily journal. Write about what you're doing, thinking, and learning. Journals are permanent records.
20
+ - Periodically consolidate journal entries into `MEMORY.md` to promote lasting insights.
21
+
22
+ See the **memory** skill for detailed guidance.
23
+
24
+ ## Sessions
25
+
26
+ - You may have **multiple named sessions** — each maintains its own conversation history. See `VOLUTE.md` for how to configure session routing via `.config/routes.json`.
27
+ - Your conversation may be **resumed** from a previous session — orient yourself by reading recent journal entries if needed.
28
+ - On a **fresh session**, read `MEMORY.md` and recent journal entries to remember where you left off.
29
+ - On **compaction**, update today's journal to preserve context before the conversation is trimmed.
@@ -0,0 +1,7 @@
1
+ {
2
+ "model": "gpt-5.4",
3
+ "reasoningEffort": "medium",
4
+ "compaction": {
5
+ "maxContextTokens": 150000
6
+ }
7
+ }
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "{{name}}",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "scripts": {
6
+ "dev": "tsx watch src/server.ts",
7
+ "lint": "biome check src/",
8
+ "lint:fix": "biome check --write src/",
9
+ "typecheck": "tsc --noEmit"
10
+ },
11
+ "dependencies": {
12
+ "@openai/codex-sdk": "^0.115.0",
13
+ "tsx": "^4.0.0"
14
+ },
15
+ "devDependencies": {
16
+ "@biomejs/biome": "2.3.14",
17
+ "@types/node": "^25.2.0",
18
+ "typescript": "^5.7.0"
19
+ }
20
+ }