volute 0.50.0 → 0.51.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 (140) hide show
  1. package/dist/{activity-events-H7YI3BTF.js → activity-events-LQII6PZS.js} +3 -3
  2. package/dist/{ai-service-V4CTNGI3.js → ai-service-YW5SWCSB.js} +7 -3
  3. package/dist/{archive-ORM3ENHI.js → archive-FOHGXHPT.js} +3 -3
  4. package/dist/{auth-DT7GY7EZ.js → auth-X6EMNEGB.js} +4 -4
  5. package/dist/{avatar-image-QEPAP3A5.js → avatar-image-52WGXLHV.js} +3 -3
  6. package/dist/{backup-IE6PMXJO.js → backup-YVGV2ZNM.js} +7 -7
  7. package/dist/{chat-W5K6HCBE.js → chat-VAZ25KVT.js} +4 -4
  8. package/dist/{chunk-5IIVOWSP.js → chunk-2RNMBXD2.js} +16 -0
  9. package/dist/{chunk-U3EJJ7JN.js → chunk-5N32EN4O.js} +2 -2
  10. package/dist/{chunk-ZUG76D3P.js → chunk-5Y54AE3L.js} +1 -1
  11. package/dist/{chunk-5FVANDON.js → chunk-6NLW72HP.js} +2 -2
  12. package/dist/{chunk-JI67D7O2.js → chunk-7HN63Q7J.js} +120 -25
  13. package/dist/{chunk-PW65BXY5.js → chunk-7YJY3F2R.js} +11 -1
  14. package/dist/{chunk-HW6Q7AMB.js → chunk-AI47FDGL.js} +6 -7
  15. package/dist/{chunk-636GFQNE.js → chunk-BSHZ23MS.js} +8 -7
  16. package/dist/{chunk-7DXLIYPY.js → chunk-CDKQJWGK.js} +6 -6
  17. package/dist/{chunk-SSNZ6ZNN.js → chunk-CV6BNGFK.js} +3 -3
  18. package/dist/{chunk-QFVGPKJI.js → chunk-DMK5ASKV.js} +1 -1
  19. package/dist/{chunk-MLZBBLMT.js → chunk-DN3LKLYR.js} +4 -4
  20. package/dist/{chunk-WBBKYUGF.js → chunk-EDR47UE2.js} +2 -2
  21. package/dist/{chunk-632FJ23O.js → chunk-EGV6R6UF.js} +26 -7
  22. package/dist/{chunk-KAEZOVP6.js → chunk-FLIEXG2T.js} +1 -1
  23. package/dist/{chunk-5TPC4KCV.js → chunk-FXWBFDQR.js} +2 -2
  24. package/dist/{chunk-74T3CEXK.js → chunk-G7A5LEVI.js} +2 -2
  25. package/dist/{chunk-7UWXAJMK.js → chunk-GLM7TVWP.js} +1 -1
  26. package/dist/{chunk-CAIYV33P.js → chunk-H6X6CKDN.js} +2 -1
  27. package/dist/{chunk-UC6BNMGT.js → chunk-HKP6OAJP.js} +3 -3
  28. package/dist/{chunk-C3KLHJDB.js → chunk-HTRSSOHY.js} +1 -1
  29. package/dist/{chunk-77IZUVBB.js → chunk-I7U5EEZC.js} +2 -2
  30. package/dist/{chunk-7OZWKHA3.js → chunk-ICL6T4SN.js} +18 -4
  31. package/dist/{chunk-X67VT4QO.js → chunk-ICXY54ZC.js} +1 -1
  32. package/dist/{chunk-YQOYZ6G7.js → chunk-IWAVIJO2.js} +2 -2
  33. package/dist/{chunk-5PKFJHCR.js → chunk-JHKBALS2.js} +2 -2
  34. package/dist/{chunk-7XDOTN4R.js → chunk-L7BKTZ5U.js} +1 -1
  35. package/dist/{chunk-AFSLNUTT.js → chunk-LAUCO2HC.js} +1 -1
  36. package/dist/{chunk-AJWFDIEY.js → chunk-M77B73VV.js} +1 -1
  37. package/dist/{chunk-4CN7OCJE.js → chunk-OXWUEU5V.js} +2 -2
  38. package/dist/{chunk-7FDWPQCU.js → chunk-Q7XQLKZB.js} +6 -6
  39. package/dist/{chunk-TGJHGVBZ.js → chunk-QT4D5XCI.js} +4 -4
  40. package/dist/{chunk-574M6I7F.js → chunk-TPX6E6PL.js} +3 -3
  41. package/dist/{chunk-3BAFMGCJ.js → chunk-U54PJPTB.js} +2 -2
  42. package/dist/{chunk-6BM2I736.js → chunk-UDZLPR34.js} +1 -1
  43. package/dist/{chunk-E5S3ILVA.js → chunk-UFTDRTCP.js} +1 -1
  44. package/dist/{chunk-ALIVXDM4.js → chunk-VAEZCIJC.js} +95 -51
  45. package/dist/{chunk-EQKSRVDE.js → chunk-VP5PFNZO.js} +89 -41
  46. package/dist/{chunk-SFRYRW37.js → chunk-VSBMLB2W.js} +9 -9
  47. package/dist/{chunk-RRZ4RQQU.js → chunk-X67BDDZR.js} +5 -7
  48. package/dist/cli.js +19 -19
  49. package/dist/{clock-LH7UFNLJ.js → clock-ZT5YT6HA.js} +2 -2
  50. package/dist/{cloud-sync-IDJFVDEP.js → cloud-sync-GPXYPTIC.js} +22 -22
  51. package/dist/{conversations-S2232Y5I.js → conversations-ZL6PHROD.js} +4 -4
  52. package/dist/{create-2M7DAHQX.js → create-G2RIKQYK.js} +1 -1
  53. package/dist/{daemon-restart-2AAGYFHE.js → daemon-restart-2EWBRTN5.js} +6 -6
  54. package/dist/daemon.js +478 -331
  55. package/dist/{db-QAY45Z56.js → db-BONH4SUG.js} +2 -2
  56. package/dist/delivery-manager-HGVZSEES.js +35 -0
  57. package/dist/{delivery-router-JE7SHYNU.js → delivery-router-RHANNTAC.js} +3 -3
  58. package/dist/{down-D4IRNEMJ.js → down-QSNEMTTX.js} +5 -5
  59. package/dist/echo-text-6ANI5HHN.js +39 -0
  60. package/dist/{env-ABM5IMA7.js → env-IBW2QI6K.js} +1 -1
  61. package/dist/{env-AZK5X4W7.js → env-SQFCWW52.js} +3 -3
  62. package/dist/{exec-2H65LMTA.js → exec-ABD3DYA2.js} +3 -3
  63. package/dist/{export-3E3EL6W3.js → export-VBNWM7I4.js} +3 -3
  64. package/dist/{extensions-7KP3X6G7.js → extensions-NY6WST33.js} +22 -22
  65. package/dist/{files-6BMKVG5G.js → files-BRY5RRIM.js} +3 -3
  66. package/dist/{import-UPNTEBBT.js → import-DB2KULRO.js} +6 -6
  67. package/dist/{isolation-OKDFM6WA.js → isolation-GVV2IBAU.js} +3 -3
  68. package/dist/{list-4BVGH7AN.js → list-UQGIW7OF.js} +1 -1
  69. package/dist/{login-MOXVLULE.js → login-SB5OENMV.js} +2 -2
  70. package/dist/{logout-DWD3OQVT.js → logout-YFZAJRPY.js} +2 -2
  71. package/dist/message-delivery-JHMAJ2GU.js +43 -0
  72. package/dist/{migrate-thread-config-4A5LW36Z.js → migrate-thread-config-EQSEP2XG.js} +2 -2
  73. package/dist/{mind-NYRALSV2.js → mind-WF6QIXWV.js} +7 -7
  74. package/dist/{mind-activity-tracker-L7GCYXQR.js → mind-activity-tracker-QF6DCX2X.js} +4 -4
  75. package/dist/{mind-history-NXXOOTX6.js → mind-history-OGNR4L7W.js} +1 -1
  76. package/dist/mind-service-DZCUUC7J.js +47 -0
  77. package/dist/{mind-sleep-TH6BRQ5X.js → mind-sleep-LZAFBDZD.js} +2 -2
  78. package/dist/{package-XYSV7NUT.js → package-B6O5APCZ.js} +1 -1
  79. package/dist/{prompts-QKFPX6DQ.js → prompts-NBO3HRK3.js} +3 -3
  80. package/dist/{read-ZXGUT4QC.js → read-RJ5NAW5J.js} +3 -14
  81. package/dist/{registry-4UF6OCOE.js → registry-TRC2HJXR.js} +2 -2
  82. package/dist/{sandbox-EGEU5GGE.js → sandbox-NNWCTHRI.js} +3 -3
  83. package/dist/scheduler-TNBN54ZK.js +25 -0
  84. package/dist/{schema-C2GIPD4V.js → schema-ZZQVM4GR.js} +3 -1
  85. package/dist/{seed-3IMBOAGK.js → seed-K2LYODIH.js} +1 -1
  86. package/dist/{seed-cmd-U73XVQMI.js → seed-cmd-2EWBTR6P.js} +2 -2
  87. package/dist/{seed-create-FBLNVSUP.js → seed-create-VGTLU5FD.js} +3 -3
  88. package/dist/{seed-readiness-QRA46ZJQ.js → seed-readiness-DYOVCYHN.js} +4 -4
  89. package/dist/{seed-sprout-3B4SZX7J.js → seed-sprout-PILHJ3YS.js} +7 -7
  90. package/dist/{send-NBRZJXKX.js → send-2YKOZJJ5.js} +81 -49
  91. package/dist/{service-XMXAXGJD.js → service-XQPMZAGI.js} +4 -4
  92. package/dist/{service-install-MOKTEG7B.js → service-install-2EWD7KXD.js} +5 -5
  93. package/dist/{setup-JGYAXXW7.js → setup-MDPSK2XG.js} +6 -6
  94. package/dist/{setup-CAJFOR2N.js → setup-WZSXHHRJ.js} +2 -2
  95. package/dist/{skill-NXIHDQCG.js → skill-B5OJEQUA.js} +1 -1
  96. package/dist/{skills-5MSMT7PT.js → skills-NH2GQKAF.js} +4 -4
  97. package/dist/sleep-manager-7JXEQQ2P.js +41 -0
  98. package/dist/{spirit-IL5EMWYZ.js → spirit-25JVAL3M.js} +7 -7
  99. package/dist/spirit-availability-PJDWZJ3J.js +42 -0
  100. package/dist/{sprout-UPXGJ5HX.js → sprout-EXUZMIOR.js} +1 -1
  101. package/dist/{status-IL67CYM4.js → status-MWLE3RVC.js} +5 -5
  102. package/dist/{system-events-A6IGCEC3.js → system-events-ZPSH63QX.js} +3 -3
  103. package/dist/{tailscale-SJUW2ETR.js → tailscale-KHNZFC7L.js} +2 -2
  104. package/dist/{template-hash-GSOUARW4.js → template-hash-YKWMVYMM.js} +2 -2
  105. package/dist/{template-staleness-T27VCGYA.js → template-staleness-PY2CULH6.js} +5 -5
  106. package/dist/{turn-tracker-6EI2EL7G.js → turn-tracker-TAT2HGK6.js} +5 -5
  107. package/dist/{up-CLL4665A.js → up-J7XCAH7P.js} +5 -5
  108. package/dist/{update-HVAUCQPN.js → update-CIWI2JYA.js} +5 -5
  109. package/dist/{update-check-2MGBBAFF.js → update-check-SZ7B5IFI.js} +3 -3
  110. package/dist/variant-cleanup-YXN25ZVW.js +22 -0
  111. package/dist/{version-notify-7BUHWZNT.js → version-notify-EN3KJ7HH.js} +8 -8
  112. package/dist/web-assets/assets/index-BOyJ4Gqs.css +1 -0
  113. package/dist/web-assets/assets/{index-ClsUcXiW.js → index-CrxBwnzp.js} +19 -19
  114. package/dist/web-assets/index.html +2 -2
  115. package/drizzle/0018_api_tokens.sql +20 -0
  116. package/drizzle/meta/_journal.json +7 -0
  117. package/package.json +1 -1
  118. package/templates/_base/{biome.json.tmpl → biome.json} +2 -1
  119. package/templates/_base/gitignore +44 -0
  120. package/templates/_base/home/public/.gitkeep +0 -0
  121. package/templates/_base/src/lib/format-prefix.ts +15 -8
  122. package/templates/_base/src/lib/router.ts +13 -8
  123. package/templates/claude/volute-template.json +1 -3
  124. package/templates/codex/volute-template.json +1 -3
  125. package/templates/pi/{package.json.tmpl → package.json} +2 -2
  126. package/templates/pi/volute-template.json +1 -3
  127. package/dist/delivery-manager-B4OKWD2P.js +0 -33
  128. package/dist/echo-text-WYAAODBV.js +0 -39
  129. package/dist/message-delivery-3456PXB7.js +0 -43
  130. package/dist/mind-service-5TA7UJAT.js +0 -47
  131. package/dist/scheduler-UV6RUJ7P.js +0 -25
  132. package/dist/sleep-manager-LIEPO6WG.js +0 -41
  133. package/dist/spirit-availability-YFEGJNJ2.js +0 -42
  134. package/dist/variant-cleanup-HJ7Z75R6.js +0 -22
  135. package/dist/web-assets/assets/index-Dz3wTbtR.css +0 -1
  136. /package/templates/_base/home/.config/{config.json.tmpl → config.json} +0 -0
  137. /package/templates/claude/{package.json.tmpl → package.json} +0 -0
  138. /package/templates/codex/home/.config/{config.json.tmpl → config.json} +0 -0
  139. /package/templates/codex/{package.json.tmpl → package.json} +0 -0
  140. /package/templates/pi/home/.config/{config.json.tmpl → config.json} +0 -0
@@ -2,31 +2,33 @@
2
2
  import {
3
3
  clearMind,
4
4
  summarizeOrphanedTurns
5
- } from "./chunk-7DXLIYPY.js";
5
+ } from "./chunk-CDKQJWGK.js";
6
6
  import {
7
7
  deliverEvent,
8
8
  recordNotice
9
- } from "./chunk-7OZWKHA3.js";
9
+ } from "./chunk-ICL6T4SN.js";
10
10
  import {
11
11
  getPrompt
12
- } from "./chunk-5TPC4KCV.js";
12
+ } from "./chunk-FXWBFDQR.js";
13
13
  import {
14
14
  isSandboxEnabled,
15
15
  wrapForSandbox
16
- } from "./chunk-ZUG76D3P.js";
16
+ } from "./chunk-5Y54AE3L.js";
17
17
  import {
18
18
  chownMindDir,
19
19
  isIsolationEnabled,
20
20
  wrapForIsolation
21
- } from "./chunk-AJWFDIEY.js";
21
+ } from "./chunk-M77B73VV.js";
22
22
  import {
23
23
  loadMergedEnv
24
- } from "./chunk-7UWXAJMK.js";
24
+ } from "./chunk-GLM7TVWP.js";
25
25
  import {
26
+ OAuthRefreshError,
26
27
  getAiConfig,
27
28
  resolveApiKey,
28
- resolveOAuthCredentials
29
- } from "./chunk-632FJ23O.js";
29
+ resolveOAuthCredentials,
30
+ resolveProviderKey
31
+ } from "./chunk-EGV6R6UF.js";
30
32
  import {
31
33
  logger_default
32
34
  } from "./chunk-QUSMYGNQ.js";
@@ -40,7 +42,7 @@ import {
40
42
  setMindRunning,
41
43
  stateDir,
42
44
  voluteSystemDir
43
- } from "./chunk-CAIYV33P.js";
45
+ } from "./chunk-H6X6CKDN.js";
44
46
 
45
47
  // packages/daemon/src/lib/util/json-state.ts
46
48
  import { existsSync, readFileSync, unlinkSync, writeFileSync } from "fs";
@@ -154,6 +156,18 @@ var RotatingLog = class extends Writable {
154
156
  import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
155
157
  import { resolve } from "path";
156
158
  var slog = logger_default.child("cred-sync");
159
+ var DAEMON_ONLY_OAUTH = /* @__PURE__ */ new Set(["xai"]);
160
+ var PI_PROVIDER_ENV_VAR = {
161
+ openrouter: "OPENROUTER_API_KEY",
162
+ openai: "OPENAI_API_KEY",
163
+ anthropic: "ANTHROPIC_API_KEY",
164
+ google: "GEMINI_API_KEY",
165
+ groq: "GROQ_API_KEY",
166
+ cerebras: "CEREBRAS_API_KEY",
167
+ xai: "XAI_API_KEY",
168
+ mistral: "MISTRAL_API_KEY",
169
+ zai: "ZAI_API_KEY"
170
+ };
157
171
  async function writeClaudeCredentials(homeDir, baseName, oauth) {
158
172
  const claudeDir = resolve(homeDir, ".claude");
159
173
  mkdirSync(claudeDir, { recursive: true });
@@ -194,6 +208,46 @@ async function writePiProviderOAuth(piAgentDir, baseName, provider, oauth) {
194
208
  await chownMindDir(piAgentDir, baseName);
195
209
  }
196
210
  }
211
+ async function injectPiProviderCredentials(opts) {
212
+ const { provider, piAgentDir, baseName, mindName, env } = opts;
213
+ let oauthCreds;
214
+ let oauthError;
215
+ try {
216
+ oauthCreds = await resolveOAuthCredentials(provider);
217
+ } catch (err) {
218
+ if (!(err instanceof OAuthRefreshError)) throw err;
219
+ slog.warn(
220
+ `OAuth token refresh failing for provider "${provider}" (mind ${mindName}); falling back to a static key if available`,
221
+ logger_default.errorData(err)
222
+ );
223
+ oauthError = err;
224
+ }
225
+ if (oauthCreds && !DAEMON_ONLY_OAUTH.has(provider)) {
226
+ await writePiProviderOAuth(piAgentDir, baseName, provider, oauthCreds);
227
+ env.PI_CODING_AGENT_DIR = piAgentDir;
228
+ return;
229
+ }
230
+ let apiKey;
231
+ if (oauthCreds) {
232
+ apiKey = oauthCreds.access;
233
+ } else if (oauthError) {
234
+ apiKey = await resolveProviderKey(provider, getAiConfig()?.providers[provider]?.apiKey);
235
+ } else {
236
+ apiKey = await resolveApiKey(provider);
237
+ }
238
+ if (apiKey) {
239
+ await writePiProviderKey(piAgentDir, baseName, provider, apiKey);
240
+ env.PI_CODING_AGENT_DIR = piAgentDir;
241
+ const providerEnv = PI_PROVIDER_ENV_VAR[provider];
242
+ if (providerEnv) env[providerEnv] = apiKey;
243
+ } else if (oauthError) {
244
+ slog.warn(
245
+ `OAuth token refresh is temporarily failing for provider "${provider}" and no static key is available \u2014 mind ${mindName} may start but stay silent until it recovers`
246
+ );
247
+ } else {
248
+ slog.warn(`no API key found for provider "${provider}" \u2014 mind ${mindName} may fail to start`);
249
+ }
250
+ }
197
251
  function piUsesProvider(piAgentDir, provider) {
198
252
  try {
199
253
  const authPath = resolve(piAgentDir, "auth.json");
@@ -325,7 +379,6 @@ var RestartTracker = class {
325
379
  // packages/daemon/src/lib/daemon/mind-manager.ts
326
380
  var mlog = logger_default.child("minds");
327
381
  var execFileAsync = promisify(execFile);
328
- var DAEMON_ONLY_OAUTH = /* @__PURE__ */ new Set(["xai"]);
329
382
  var MIND_ENV_ALLOWLIST = [
330
383
  "PATH",
331
384
  "HOME",
@@ -461,10 +514,10 @@ var MindManager = class {
461
514
  if (!existsSync4(dir)) throw new Error(`Mind directory missing: ${dir}`);
462
515
  return { dir, port: entry.port, baseName: name, template: entry.template };
463
516
  }
464
- async startMind(name) {
465
- return this.withLock(name, () => this._startMind(name));
517
+ async startMind(name, opts) {
518
+ return this.withLock(name, () => this._startMind(name, opts));
466
519
  }
467
- async _startMind(name) {
520
+ async _startMind(name, opts) {
468
521
  if (this.minds.has(name)) {
469
522
  throw new Error(`Mind ${name} is already running`);
470
523
  }
@@ -554,34 +607,13 @@ var MindManager = class {
554
607
  if (modelStr?.includes(":")) {
555
608
  const provider = modelStr.split(":")[0];
556
609
  const piAgentDir = resolve2(dir, ".mind", "pi-agent");
557
- const oauthCreds = await resolveOAuthCredentials(provider);
558
- if (oauthCreds && !DAEMON_ONLY_OAUTH.has(provider)) {
559
- await writePiProviderOAuth(piAgentDir, baseName, provider, oauthCreds);
560
- env.PI_CODING_AGENT_DIR = piAgentDir;
561
- } else {
562
- const apiKey = oauthCreds?.access ?? await resolveApiKey(provider);
563
- if (apiKey) {
564
- await writePiProviderKey(piAgentDir, baseName, provider, apiKey);
565
- env.PI_CODING_AGENT_DIR = piAgentDir;
566
- const providerEnvVars = {
567
- openrouter: "OPENROUTER_API_KEY",
568
- openai: "OPENAI_API_KEY",
569
- anthropic: "ANTHROPIC_API_KEY",
570
- google: "GEMINI_API_KEY",
571
- groq: "GROQ_API_KEY",
572
- cerebras: "CEREBRAS_API_KEY",
573
- xai: "XAI_API_KEY",
574
- mistral: "MISTRAL_API_KEY",
575
- zai: "ZAI_API_KEY"
576
- };
577
- const providerEnv = providerEnvVars[provider];
578
- if (providerEnv) env[providerEnv] = apiKey;
579
- } else {
580
- mlog.warn(
581
- `no API key found for provider "${provider}" \u2014 mind ${name} may fail to start`
582
- );
583
- }
584
- }
610
+ await injectPiProviderCredentials({
611
+ provider,
612
+ piAgentDir,
613
+ baseName,
614
+ mindName: name,
615
+ env
616
+ });
585
617
  }
586
618
  }
587
619
  } catch (err) {
@@ -647,6 +679,17 @@ var MindManager = class {
647
679
  if (key && oauth) {
648
680
  const claudeDir = await writeClaudeCredentials(resolve2(dir, "home"), baseName, oauth);
649
681
  env.CLAUDE_CONFIG_DIR = claudeDir;
682
+ } else {
683
+ mlog.error(
684
+ `Anthropic OAuth token refresh is failing for ${name}; it will spawn without credentials and stay silent until the provider recovers and it is restarted`
685
+ );
686
+ void recordNotice({
687
+ mind: name,
688
+ thread: "main",
689
+ kind: "startup",
690
+ reason: "oauth_refresh_failed",
691
+ detail: "Anthropic authentication is temporarily unavailable (OAuth token refresh is failing), so you started without model credentials and may be unable to respond until it recovers. This is usually transient; if it persists, ask your host to re-check the anthropic provider."
692
+ });
650
693
  }
651
694
  } else {
652
695
  const apiKey = await resolveApiKey("anthropic");
@@ -692,9 +735,10 @@ var MindManager = class {
692
735
  while (recentStderr.length > 20) recentStderr.shift();
693
736
  });
694
737
  try {
738
+ const healthTimeoutMs = opts?.healthTimeoutMs ?? 3e4;
695
739
  await new Promise((resolve3, reject) => {
696
740
  let settled = false;
697
- const deadline = Date.now() + 3e4;
741
+ const deadline = Date.now() + healthTimeoutMs;
698
742
  const onExit = (code) => {
699
743
  const errorLine = recentStderr.find((l) => l.includes("Error:"));
700
744
  const detail = errorLine ? `: ${errorLine.trim()}` : "";
@@ -729,7 +773,7 @@ var MindManager = class {
729
773
  finish(
730
774
  () => reject(
731
775
  new MindStartupError(
732
- `Mind ${name} did not become healthy within 30s`,
776
+ `Mind ${name} did not become healthy within ${Math.round(healthTimeoutMs / 1e3)}s`,
733
777
  recentStderr.join("\n")
734
778
  )
735
779
  )
@@ -761,9 +805,9 @@ var MindManager = class {
761
805
  mlog.info(`started mind ${name} on port ${port}`);
762
806
  await this.deliverPendingContext(name);
763
807
  try {
764
- const { getSleepManagerIfReady } = await import("./sleep-manager-LIEPO6WG.js");
808
+ const { getSleepManagerIfReady } = await import("./sleep-manager-7JXEQQ2P.js");
765
809
  if (!getSleepManagerIfReady()?.isSleeping(name)) {
766
- const { flushQueuedEvents } = await import("./system-events-A6IGCEC3.js");
810
+ const { flushQueuedEvents } = await import("./system-events-ZPSH63QX.js");
767
811
  const flushed = await flushQueuedEvents(name);
768
812
  if (flushed > 0) mlog.info(`redelivered ${flushed} pending event(s) to ${name}`);
769
813
  }
@@ -799,7 +843,7 @@ var MindManager = class {
799
843
  if (this.shuttingDown || this.stopping.has(name)) return;
800
844
  mlog.error(`mind ${name} exited with code ${code}`);
801
845
  try {
802
- const { getSleepManagerIfReady } = await import("./sleep-manager-LIEPO6WG.js");
846
+ const { getSleepManagerIfReady } = await import("./sleep-manager-7JXEQQ2P.js");
803
847
  const sleepMgr = getSleepManagerIfReady();
804
848
  const sleepState = sleepMgr?.getState(name);
805
849
  if (sleepState?.sleeping) {
@@ -829,15 +873,15 @@ var MindManager = class {
829
873
  (err) => mlog.warn(`failed to clear turn state for ${name} after crash`, logger_default.errorData(err))
830
874
  );
831
875
  try {
832
- const { getDeliveryManager } = await import("./delivery-manager-B4OKWD2P.js");
876
+ const { getDeliveryManager } = await import("./delivery-manager-HGVZSEES.js");
833
877
  getDeliveryManager().clearMindSessions(name);
834
878
  } catch (err) {
835
879
  if (!(err instanceof Error && err.message.includes("not initialized"))) {
836
880
  mlog.warn(`failed to clear delivery state for ${name} after crash`, logger_default.errorData(err));
837
881
  }
838
882
  }
839
- import("./mind-activity-tracker-L7GCYXQR.js").then(({ markIdle }) => markIdle(name)).catch((err) => mlog.warn(`failed to mark ${name} idle after crash`, logger_default.errorData(err)));
840
- import("./activity-events-H7YI3BTF.js").then(
883
+ import("./mind-activity-tracker-QF6DCX2X.js").then(({ markIdle }) => markIdle(name)).catch((err) => mlog.warn(`failed to mark ${name} idle after crash`, logger_default.errorData(err)));
884
+ import("./activity-events-LQII6PZS.js").then(
841
885
  ({ publish }) => publish({ type: "mind_stopped", mind: name, summary: `${name} crashed (exit ${code})` })
842
886
  ).catch((err) => mlog.warn(`failed to publish crash event for ${name}`, logger_default.errorData(err)));
843
887
  const { shouldRestart, delay, attempt } = this.restartTracker.recordCrash(name);
@@ -895,7 +939,7 @@ var MindManager = class {
895
939
  mlog.warn(`failed to clear turn state for ${name} on stop`, logger_default.errorData(err));
896
940
  }
897
941
  try {
898
- const { getDeliveryManager } = await import("./delivery-manager-B4OKWD2P.js");
942
+ const { getDeliveryManager } = await import("./delivery-manager-HGVZSEES.js");
899
943
  getDeliveryManager().clearMindSessions(name);
900
944
  } catch (err) {
901
945
  if (!(err instanceof Error && err.message.includes("not initialized"))) {
@@ -903,7 +947,7 @@ var MindManager = class {
903
947
  }
904
948
  }
905
949
  try {
906
- const { clearEchoTextCache } = await import("./echo-text-WYAAODBV.js");
950
+ const { clearEchoTextCache } = await import("./echo-text-6ANI5HHN.js");
907
951
  clearEchoTextCache(name);
908
952
  } catch (err) {
909
953
  mlog.debug(`failed to clear echo-text cache for ${name}`, logger_default.errorData(err));
@@ -1,36 +1,36 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  spiritDir
4
- } from "./chunk-SFRYRW37.js";
4
+ } from "./chunk-VSBMLB2W.js";
5
5
  import {
6
6
  getScheduler
7
- } from "./chunk-7FDWPQCU.js";
7
+ } from "./chunk-Q7XQLKZB.js";
8
8
  import {
9
9
  getDeliveryManager
10
- } from "./chunk-JI67D7O2.js";
10
+ } from "./chunk-7HN63Q7J.js";
11
11
  import {
12
12
  getMindManager,
13
13
  resolveMindToken
14
- } from "./chunk-ALIVXDM4.js";
14
+ } from "./chunk-VAEZCIJC.js";
15
15
  import {
16
16
  extractTextContent,
17
17
  getRoutingConfig,
18
18
  resolveRoute,
19
19
  shouldGate
20
- } from "./chunk-6BM2I736.js";
20
+ } from "./chunk-UDZLPR34.js";
21
21
  import {
22
22
  hashSkillDir,
23
23
  importSkillFromDir,
24
24
  removeSharedSkill,
25
25
  sharedSkillsDir
26
- } from "./chunk-TGJHGVBZ.js";
26
+ } from "./chunk-QT4D5XCI.js";
27
27
  import {
28
28
  getOrCreateMindUser,
29
29
  getOrCreateSystemUser,
30
30
  getUser,
31
31
  getUserByUsername,
32
32
  syncMindProfile
33
- } from "./chunk-UC6BNMGT.js";
33
+ } from "./chunk-HKP6OAJP.js";
34
34
  import {
35
35
  MIND_LEVEL_THREAD,
36
36
  deliverEvent,
@@ -38,13 +38,13 @@ import {
38
38
  pendingEventCount,
39
39
  pendingEventsLine,
40
40
  recordNotice
41
- } from "./chunk-7OZWKHA3.js";
41
+ } from "./chunk-ICL6T4SN.js";
42
42
  import {
43
43
  publish as publish2
44
44
  } from "./chunk-LNNI3MS4.js";
45
45
  import {
46
46
  getPrompt
47
- } from "./chunk-5TPC4KCV.js";
47
+ } from "./chunk-FXWBFDQR.js";
48
48
  import {
49
49
  addMessage,
50
50
  createChannel,
@@ -55,14 +55,14 @@ import {
55
55
  getParticipants,
56
56
  joinChannel,
57
57
  updateChannelSettings
58
- } from "./chunk-574M6I7F.js";
58
+ } from "./chunk-TPX6E6PL.js";
59
59
  import {
60
60
  markIdle
61
- } from "./chunk-E5S3ILVA.js";
61
+ } from "./chunk-UFTDRTCP.js";
62
62
  import {
63
63
  publish,
64
64
  subscribe
65
- } from "./chunk-3BAFMGCJ.js";
65
+ } from "./chunk-U54PJPTB.js";
66
66
  import {
67
67
  readVoluteConfig,
68
68
  resolveWakeTriggers
@@ -70,10 +70,10 @@ import {
70
70
  import {
71
71
  isIsolationEnabled,
72
72
  mindUserName
73
- } from "./chunk-AJWFDIEY.js";
73
+ } from "./chunk-M77B73VV.js";
74
74
  import {
75
75
  missingCredentialWarning
76
- } from "./chunk-632FJ23O.js";
76
+ } from "./chunk-EGV6R6UF.js";
77
77
  import {
78
78
  logger_default
79
79
  } from "./chunk-QUSMYGNQ.js";
@@ -90,14 +90,15 @@ import {
90
90
  voluteHome,
91
91
  voluteSystemDir,
92
92
  writeGlobalConfig
93
- } from "./chunk-CAIYV33P.js";
93
+ } from "./chunk-H6X6CKDN.js";
94
94
  import {
95
95
  activity,
96
+ apiTokens,
96
97
  deliveryQueue,
97
98
  messages,
98
99
  mindHistory,
99
100
  sessions
100
- } from "./chunk-5IIVOWSP.js";
101
+ } from "./chunk-2RNMBXD2.js";
101
102
 
102
103
  // packages/daemon/src/lib/extensions.ts
103
104
  import { existsSync as existsSync5, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
@@ -105,9 +106,43 @@ import { dirname, resolve as resolve5 } from "path";
105
106
 
106
107
  // packages/daemon/src/web/middleware/auth.ts
107
108
  import { timingSafeEqual } from "crypto";
108
- import { eq, lt } from "drizzle-orm";
109
+ import { eq as eq2, lt } from "drizzle-orm";
109
110
  import { getCookie } from "hono/cookie";
110
111
  import { createMiddleware } from "hono/factory";
112
+
113
+ // packages/daemon/src/lib/api-tokens.ts
114
+ import { createHash, randomBytes } from "crypto";
115
+ import { and, eq } from "drizzle-orm";
116
+ var API_TOKEN_PREFIX = "vmt_";
117
+ function generateToken() {
118
+ return API_TOKEN_PREFIX + randomBytes(32).toString("base64url");
119
+ }
120
+ function hashToken(token) {
121
+ return createHash("sha256").update(token).digest("hex");
122
+ }
123
+ async function issueApiToken(userId, label) {
124
+ const db = await getDb();
125
+ const token = generateToken();
126
+ const [row] = await db.insert(apiTokens).values({ user_id: userId, token_hash: hashToken(token), label: label ?? null }).returning({ id: apiTokens.id, label: apiTokens.label, created_at: apiTokens.created_at });
127
+ return { id: row.id, token, label: row.label, created_at: row.created_at };
128
+ }
129
+ async function resolveApiToken(token) {
130
+ if (!token.startsWith(API_TOKEN_PREFIX)) return null;
131
+ const db = await getDb();
132
+ const row = await db.select({ user_id: apiTokens.user_id }).from(apiTokens).where(eq(apiTokens.token_hash, hashToken(token))).get();
133
+ return row?.user_id ?? null;
134
+ }
135
+ async function revokeApiToken(userId, id) {
136
+ const db = await getDb();
137
+ const rows = await db.delete(apiTokens).where(and(eq(apiTokens.id, id), eq(apiTokens.user_id, userId))).returning({ id: apiTokens.id });
138
+ return rows.length > 0;
139
+ }
140
+ async function listApiTokens(userId) {
141
+ const db = await getDb();
142
+ return db.select({ id: apiTokens.id, label: apiTokens.label, created_at: apiTokens.created_at }).from(apiTokens).where(eq(apiTokens.user_id, userId)).orderBy(apiTokens.id).all();
143
+ }
144
+
145
+ // packages/daemon/src/web/middleware/auth.ts
111
146
  var MIND_USER_CACHE_TTL = 5 * 60 * 1e3;
112
147
  var mindUserCache = /* @__PURE__ */ new Map();
113
148
  var alog = logger_default.child("auth");
@@ -142,14 +177,14 @@ async function createSession(userId) {
142
177
  async function deleteSession(sessionId) {
143
178
  sessionCache.delete(sessionId);
144
179
  const db = await getDb();
145
- await db.delete(sessions).where(eq(sessions.id, sessionId));
180
+ await db.delete(sessions).where(eq2(sessions.id, sessionId));
146
181
  }
147
182
  async function getSessionUserId(sessionId) {
148
183
  const db = await getDb();
149
- const row = await db.select().from(sessions).where(eq(sessions.id, sessionId)).get();
184
+ const row = await db.select().from(sessions).where(eq2(sessions.id, sessionId)).get();
150
185
  if (!row) return void 0;
151
186
  if (Date.now() - row.createdAt > SESSION_MAX_AGE) {
152
- await db.delete(sessions).where(eq(sessions.id, sessionId));
187
+ await db.delete(sessions).where(eq2(sessions.id, sessionId));
153
188
  return void 0;
154
189
  }
155
190
  return row.userId;
@@ -231,6 +266,18 @@ var authMiddleware = createMiddleware(async (c, next) => {
231
266
  await next();
232
267
  return;
233
268
  }
269
+ if (token.startsWith(API_TOKEN_PREFIX)) {
270
+ const userId = await resolveApiToken(token);
271
+ if (userId != null) {
272
+ const user2 = await getUser(userId);
273
+ if (user2) {
274
+ if (user2.role === "pending") return c.json({ error: "Account pending approval" }, 403);
275
+ c.set("user", user2);
276
+ await next();
277
+ return;
278
+ }
279
+ }
280
+ }
234
281
  if (token) {
235
282
  const user2 = await resolveSession(token);
236
283
  if (user2) {
@@ -262,7 +309,7 @@ var requireSelf = (paramName = "name") => createMiddleware(async (c, next) => {
262
309
  });
263
310
 
264
311
  // packages/daemon/src/lib/delivery/message-delivery.ts
265
- import { and as and2, eq as eq3, inArray as inArray2 } from "drizzle-orm";
312
+ import { and as and3, eq as eq4, inArray as inArray2 } from "drizzle-orm";
266
313
 
267
314
  // packages/daemon/src/lib/daemon/sleep-manager.ts
268
315
  import { execFile, spawn as spawnChild } from "child_process";
@@ -278,7 +325,7 @@ import {
278
325
  import { resolve as resolve4 } from "path";
279
326
  import { promisify } from "util";
280
327
  import { CronExpressionParser } from "cron-parser";
281
- import { and, eq as eq2, inArray } from "drizzle-orm";
328
+ import { and as and2, eq as eq3, inArray } from "drizzle-orm";
282
329
 
283
330
  // packages/daemon/src/lib/daemon/mind-service.ts
284
331
  import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
@@ -292,7 +339,7 @@ async function ensureSystemDM(mindName) {
292
339
  const systemUser = await getOrCreateSystemUser();
293
340
  const mindUser = await getOrCreateMindUser(mindName);
294
341
  if (systemUser.id === mindUser.id) {
295
- throw new Error(`Cannot create system DM: mind "${mindName}" is the system user`);
342
+ return null;
296
343
  }
297
344
  const existing = await findDMConversation([systemUser.id, mindUser.id]);
298
345
  if (existing) {
@@ -868,7 +915,7 @@ async function startMindFull(name) {
868
915
  notifyExtensionsMindStart(baseName);
869
916
  }
870
917
  async function recordMissingCredentialsNotice(mind, template) {
871
- const { hasUndeliveredEvent, recordNotice: recordNotice2, MIND_LEVEL_THREAD: MIND_LEVEL_THREAD2 } = await import("./system-events-A6IGCEC3.js");
918
+ const { hasUndeliveredEvent, recordNotice: recordNotice2, MIND_LEVEL_THREAD: MIND_LEVEL_THREAD2 } = await import("./system-events-ZPSH63QX.js");
872
919
  const warning = await missingCredentialWarning(template, readMindModel(mind, template), mind);
873
920
  if (!warning) return;
874
921
  if (await hasUndeliveredEvent(mind, "no_credentials")) return;
@@ -912,7 +959,7 @@ async function wakeMind(name) {
912
959
  async function startSpiritFull(name) {
913
960
  const entry = await findMind(name);
914
961
  if (entry?.dir) {
915
- const { registerMindDir } = await import("./delivery-router-JE7SHYNU.js");
962
+ const { registerMindDir } = await import("./delivery-router-RHANNTAC.js");
916
963
  registerMindDir(name, entry.dir);
917
964
  }
918
965
  await getMindManager().startMind(name);
@@ -945,7 +992,7 @@ function buildSeedOrientation(createdBy, creatorType) {
945
992
  async function sendSeedOrientation(mindName, createdBy) {
946
993
  let creatorType;
947
994
  if (createdBy) {
948
- const { getUserByUsername: getUserByUsername2 } = await import("./auth-DT7GY7EZ.js");
995
+ const { getUserByUsername: getUserByUsername2 } = await import("./auth-X6EMNEGB.js");
949
996
  creatorType = (await getUserByUsername2(createdBy))?.user_type;
950
997
  }
951
998
  await deliverEvent(mindName, {
@@ -954,8 +1001,8 @@ async function sendSeedOrientation(mindName, createdBy) {
954
1001
  });
955
1002
  }
956
1003
  async function ensureCreatorDM(mindName, creatorUsername) {
957
- const { getOrCreateMindUser: getOrCreateMindUser2, getUserByUsername: getUserByUsername2 } = await import("./auth-DT7GY7EZ.js");
958
- const { findDMConversation: findDMConversation2, createConversation: createConversation2 } = await import("./conversations-S2232Y5I.js");
1004
+ const { getOrCreateMindUser: getOrCreateMindUser2, getUserByUsername: getUserByUsername2 } = await import("./auth-X6EMNEGB.js");
1005
+ const { findDMConversation: findDMConversation2, createConversation: createConversation2 } = await import("./conversations-ZL6PHROD.js");
959
1006
  const mindUser = await getOrCreateMindUser2(mindName);
960
1007
  const creatorUser = await getUserByUsername2(creatorUsername);
961
1008
  if (!creatorUser) {
@@ -1318,7 +1365,7 @@ var SleepManager = class {
1318
1365
  async flushQueuedMessages(name) {
1319
1366
  try {
1320
1367
  const db = await getDb();
1321
- const rows = await db.select().from(deliveryQueue).where(and(eq2(deliveryQueue.mind, name), eq2(deliveryQueue.status, "sleep-queued"))).all();
1368
+ const rows = await db.select().from(deliveryQueue).where(and2(eq3(deliveryQueue.mind, name), eq3(deliveryQueue.status, "sleep-queued"))).all();
1322
1369
  if (rows.length === 0) return 0;
1323
1370
  const groups = /* @__PURE__ */ new Map();
1324
1371
  for (const row of rows) {
@@ -1330,7 +1377,7 @@ var SleepManager = class {
1330
1377
  `dropping unparseable queued message ${row.id} for ${name}`,
1331
1378
  logger_default.errorData(err)
1332
1379
  );
1333
- await db.delete(deliveryQueue).where(eq2(deliveryQueue.id, row.id));
1380
+ await db.delete(deliveryQueue).where(eq3(deliveryQueue.id, row.id));
1334
1381
  continue;
1335
1382
  }
1336
1383
  const channel = payload.channel ?? "unknown";
@@ -1373,7 +1420,7 @@ var SleepManager = class {
1373
1420
  * the real delivery stack. Returns whether the batch was handed off to the mind.
1374
1421
  */
1375
1422
  async deliverQueuedBatch(name, payloads) {
1376
- const { deliverBatch: deliverBatch2 } = await import("./message-delivery-3456PXB7.js");
1423
+ const { deliverBatch: deliverBatch2 } = await import("./message-delivery-JHMAJ2GU.js");
1377
1424
  return deliverBatch2(name, payloads);
1378
1425
  }
1379
1426
  // --- Wake failure handling (#419) ---
@@ -1653,7 +1700,7 @@ var SleepManager = class {
1653
1700
  async buildQueuedSummary(name) {
1654
1701
  try {
1655
1702
  const db = await getDb();
1656
- const rows = await db.select({ channel: deliveryQueue.channel, sender: deliveryQueue.sender }).from(deliveryQueue).where(and(eq2(deliveryQueue.mind, name), eq2(deliveryQueue.status, "sleep-queued"))).all();
1703
+ const rows = await db.select({ channel: deliveryQueue.channel, sender: deliveryQueue.sender }).from(deliveryQueue).where(and2(eq3(deliveryQueue.mind, name), eq3(deliveryQueue.status, "sleep-queued"))).all();
1657
1704
  if (rows.length === 0) return "No messages arrived while you slept.";
1658
1705
  const channelCounts = /* @__PURE__ */ new Map();
1659
1706
  const senders = /* @__PURE__ */ new Set();
@@ -1824,7 +1871,7 @@ async function linkToolResultToTurn(mind, turnId, toolResultContent, toolUseEven
1824
1871
  content: mindHistory.content,
1825
1872
  message_id: mindHistory.message_id,
1826
1873
  turn_id: mindHistory.turn_id
1827
- }).from(mindHistory).where(and2(eq3(mindHistory.id, outboundId), eq3(mindHistory.mind, mind))).limit(1);
1874
+ }).from(mindHistory).where(and3(eq4(mindHistory.id, outboundId), eq4(mindHistory.mind, mind))).limit(1);
1828
1875
  const row = rows[0];
1829
1876
  if (!row) {
1830
1877
  dlog.warn(`outbound marker references missing record: mind=${mind} id=${outboundId}`);
@@ -1832,13 +1879,13 @@ async function linkToolResultToTurn(mind, turnId, toolResultContent, toolUseEven
1832
1879
  }
1833
1880
  const alreadyTagged = row.turn_id != null;
1834
1881
  if (!alreadyTagged) {
1835
- await db.update(mindHistory).set({ turn_id: turnId }).where(eq3(mindHistory.id, outboundId));
1882
+ await db.update(mindHistory).set({ turn_id: turnId }).where(eq4(mindHistory.id, outboundId));
1836
1883
  }
1837
1884
  if (row.message_id) {
1838
1885
  await db.update(messages).set({
1839
1886
  turn_id: turnId,
1840
1887
  ...toolUseEventId != null ? { source_event_id: toolUseEventId } : {}
1841
- }).where(eq3(messages.id, Number(row.message_id)));
1888
+ }).where(eq4(messages.id, Number(row.message_id)));
1842
1889
  }
1843
1890
  if (!alreadyTagged) {
1844
1891
  publish2(mind, {
@@ -2029,9 +2076,7 @@ async function backfillSystemChannelMembers() {
2029
2076
  }
2030
2077
  async function announceToSystem(text) {
2031
2078
  const channelId = await ensureSystemChannel();
2032
- const systemUser = await getOrCreateSystemUser();
2033
- await joinChannel(channelId, systemUser.id);
2034
- await addMessage(channelId, "user", systemUser.username, [{ type: "text", text }]);
2079
+ await addMessage(channelId, "event", null, [{ type: "text", text }]);
2035
2080
  const participants = await getParticipants(channelId);
2036
2081
  const mindParticipants = participants.filter((p) => p.userType === "mind");
2037
2082
  const channel = "#system";
@@ -2040,7 +2085,7 @@ async function announceToSystem(text) {
2040
2085
  content: [{ type: "text", text }],
2041
2086
  channel,
2042
2087
  conversationId: channelId,
2043
- sender: systemUser.username,
2088
+ sender: null,
2044
2089
  participants: participants.map((p) => p.username),
2045
2090
  participantCount: participants.length,
2046
2091
  isDM: false
@@ -2683,7 +2728,7 @@ async function installNpmExtension(pkg) {
2683
2728
  throw new Error(`Extension "${pkg}" is already installed`);
2684
2729
  }
2685
2730
  const dir = ensureExtensionsNpmDir();
2686
- const { exec } = await import("./exec-2H65LMTA.js");
2731
+ const { exec } = await import("./exec-ABD3DYA2.js");
2687
2732
  try {
2688
2733
  await exec("npm", ["install", "--ignore-scripts", pkg], { cwd: dir });
2689
2734
  } catch (err) {
@@ -2704,7 +2749,7 @@ async function uninstallNpmExtension(pkg) {
2704
2749
  packages.splice(idx, 1);
2705
2750
  writeExtensionsConfig(packages);
2706
2751
  try {
2707
- const { exec } = await import("./exec-2H65LMTA.js");
2752
+ const { exec } = await import("./exec-ABD3DYA2.js");
2708
2753
  await exec("npm", ["uninstall", "--ignore-scripts", pkg], { cwd: extensionsNpmDir() });
2709
2754
  } catch (err) {
2710
2755
  logger_default.warn(
@@ -2798,6 +2843,9 @@ function notifyExtensionsMindStop(mindName) {
2798
2843
  }
2799
2844
 
2800
2845
  export {
2846
+ issueApiToken,
2847
+ revokeApiToken,
2848
+ listApiTokens,
2801
2849
  invalidateMindUserCache,
2802
2850
  SESSION_MAX_AGE,
2803
2851
  invalidateSessionCache,