volute 0.42.1 → 0.43.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 (98) hide show
  1. package/dist/{activity-events-KFKVRZE3.js → activity-events-UISXY4Y5.js} +3 -3
  2. package/dist/{ai-service-3GBOJGUA.js → ai-service-FBCCEYF5.js} +8 -4
  3. package/dist/{archive-4PPYF7LL.js → archive-I32WHCJB.js} +3 -3
  4. package/dist/{auth-EAASQPTN.js → auth-3RIG6GKN.js} +4 -4
  5. package/dist/{chat-LYL5CWJV.js → chat-SDSX5TVV.js} +2 -2
  6. package/dist/{chunk-M2YMNTHK.js → chunk-2HIVYZDB.js} +1 -1
  7. package/dist/{chunk-BWZHX46O.js → chunk-3WLWKOGM.js} +21 -6
  8. package/dist/{chunk-QPTQKGGF.js → chunk-57LBQ7JX.js} +1 -1
  9. package/dist/{chunk-43K5UWF7.js → chunk-65OLHBCL.js} +13 -12
  10. package/dist/{chunk-K547QOCM.js → chunk-7G2EEYXX.js} +2 -2
  11. package/dist/{chunk-E7XWFDZL.js → chunk-AD46AYZQ.js} +1 -1
  12. package/dist/{chunk-P6DPB33N.js → chunk-ATEX3ZPG.js} +2 -2
  13. package/dist/{chunk-WR7CSHQ4.js → chunk-C6ESRQZ7.js} +1 -1
  14. package/dist/{chunk-BR6KFHVT.js → chunk-EE7SZJON.js} +2 -2
  15. package/dist/{chunk-5QG36RV7.js → chunk-EFL5UOY4.js} +3 -3
  16. package/dist/{chunk-M3T3Y6FT.js → chunk-FSSUANZJ.js} +5 -5
  17. package/dist/{chunk-JPDN6ZRJ.js → chunk-FZ5TH5PF.js} +8 -8
  18. package/dist/{chunk-OEJFZHZZ.js → chunk-GZJFC76M.js} +1 -1
  19. package/dist/{chunk-KUJVCDWT.js → chunk-JVK6QQIC.js} +2 -2
  20. package/dist/{chunk-YLTIEEVL.js → chunk-K4EHBHPE.js} +3 -3
  21. package/dist/{chunk-MHFAWJCC.js → chunk-L3IV23ZG.js} +12 -1
  22. package/dist/{chunk-VBKRZCR7.js → chunk-M3PGGSBH.js} +1 -1
  23. package/dist/{chunk-NSGIVS5T.js → chunk-O3DXAR44.js} +2 -2
  24. package/dist/{chunk-KZAL7HEQ.js → chunk-ONM2AZFL.js} +1 -1
  25. package/dist/{chunk-6G7R2LPI.js → chunk-PF2AB7VJ.js} +568 -378
  26. package/dist/chunk-PGPLCJPK.js +272 -0
  27. package/dist/{chunk-62ICA2FF.js → chunk-SPLPKBES.js} +17 -1
  28. package/dist/{chunk-BLOAJLGI.js → chunk-XDTKJNW5.js} +1 -1
  29. package/dist/{chunk-IUZ3EV2D.js → chunk-YAMO2U3N.js} +1 -1
  30. package/dist/{chunk-4ZSUUBG5.js → chunk-YSV3VJQK.js} +55 -2
  31. package/dist/{chunk-JU5GWO77.js → chunk-ZO7IQJKO.js} +3 -3
  32. package/dist/cli.js +15 -15
  33. package/dist/{cloud-sync-E3ZL5SZ4.js → cloud-sync-FRYNWKCR.js} +16 -17
  34. package/dist/{conversations-FSZCUUYO.js → conversations-67WWW25C.js} +4 -4
  35. package/dist/{create-ERNQYOCK.js → create-7W3LV3J7.js} +1 -1
  36. package/dist/{daemon-restart-QBZOMILO.js → daemon-restart-5GVZ5V3X.js} +7 -8
  37. package/dist/daemon.js +524 -433
  38. package/dist/{db-ZJJJYJBU.js → db-CZV62BOQ.js} +2 -2
  39. package/dist/delivery-manager-5J2BE22P.js +32 -0
  40. package/dist/{delivery-router-FDBZJQOY.js → delivery-router-HXBHYHJM.js} +7 -5
  41. package/dist/down-RRNYIGCU.js +16 -0
  42. package/dist/echo-text-XKBJM6RV.js +32 -0
  43. package/dist/{exec-ZDD7NEB4.js → exec-SJK7VUTC.js} +3 -4
  44. package/dist/{export-WYI3E3TB.js → export-7LPCIB5J.js} +3 -3
  45. package/dist/{extensions-X6MKBJLB.js → extensions-76EGTMFY.js} +16 -17
  46. package/dist/{files-COTCVBGO.js → files-LV7VAKPC.js} +3 -3
  47. package/dist/{import-EPK6WCYS.js → import-XPECCRQ7.js} +6 -6
  48. package/dist/{isolation-F7ABZMDA.js → isolation-5AYEY55P.js} +5 -3
  49. package/dist/{login-GPBXOU4D.js → login-KJSN4JDA.js} +2 -2
  50. package/dist/{logout-DNNTIYE2.js → logout-TMJGUQV2.js} +2 -2
  51. package/dist/message-delivery-WJBZFALY.js +34 -0
  52. package/dist/{mind-K5PZHPBR.js → mind-QEWCR7FA.js} +5 -5
  53. package/dist/{mind-activity-tracker-HWVFE533.js → mind-activity-tracker-JHAT6456.js} +4 -4
  54. package/dist/mind-service-ZBVUMJ5H.js +36 -0
  55. package/dist/{package-G4AM2IDZ.js → package-ZOI2KAYC.js} +1 -1
  56. package/dist/{registry-MDLNPTTI.js → registry-7LQHTRUX.js} +2 -2
  57. package/dist/{sandbox-E7RWZDVG.js → sandbox-SJH3XOPZ.js} +4 -4
  58. package/dist/scheduler-BR6C6B3E.js +30 -0
  59. package/dist/{schema-HHYJDG7B.js → schema-LRUTFOER.js} +1 -1
  60. package/dist/{seed-SKVSWF7S.js → seed-KOG6KJAO.js} +1 -1
  61. package/dist/{seed-cmd-D2H5VLB4.js → seed-cmd-GJFENEJH.js} +2 -2
  62. package/dist/{seed-create-BUSKQ5II.js → seed-create-YXO6R4KE.js} +3 -3
  63. package/dist/{seed-sprout-R2IZPITD.js → seed-sprout-BBU64QXP.js} +7 -8
  64. package/dist/{send-SLDFRSTL.js → send-ZBZWIH5V.js} +3 -3
  65. package/dist/{service-ZDOSEQCE.js → service-4EVZKGSA.js} +4 -5
  66. package/dist/{service-install-2HZPDHEY.js → service-install-SHRM6NHK.js} +5 -6
  67. package/dist/{setup-IYOUPCIK.js → setup-25MMJ3J4.js} +3 -3
  68. package/dist/{setup-IU3ID7II.js → setup-354PDHUF.js} +8 -10
  69. package/dist/{skills-P6RGGASV.js → skills-DPI3WZPQ.js} +5 -6
  70. package/dist/sleep-manager-BVU5D2AE.js +34 -0
  71. package/dist/{spirit-XLD6QXSS.js → spirit-JQQHUVWR.js} +7 -8
  72. package/dist/{sprout-QV2RJM5W.js → sprout-4WAYBUVV.js} +1 -1
  73. package/dist/{status-QAKAPDYW.js → status-WTU2WHXY.js} +5 -6
  74. package/dist/system-chat-KMQIP7VH.js +34 -0
  75. package/dist/{tailscale-TIRTGM6E.js → tailscale-Q7ZJL7S7.js} +2 -2
  76. package/dist/{turn-tracker-Z5UJC4BY.js → turn-tracker-2GMKJQTN.js} +7 -5
  77. package/dist/up-7RSBEKW6.js +18 -0
  78. package/dist/{update-NY3XBVAJ.js → update-EJ7Q77ZX.js} +5 -6
  79. package/dist/{update-check-OG27AL4Z.js → update-check-ACYYNKTW.js} +3 -3
  80. package/dist/{version-notify-P7UAR7HZ.js → version-notify-DCAIXXZB.js} +17 -18
  81. package/dist/web-assets/assets/{index-CrFAsfiE.js → index-DTDmEfPU.js} +2 -2
  82. package/dist/web-assets/index.html +1 -1
  83. package/drizzle/0010_delivery_queue_redrive.sql +10 -0
  84. package/drizzle/meta/_journal.json +7 -0
  85. package/package.json +1 -1
  86. package/templates/codex/src/server.ts +2 -8
  87. package/dist/chunk-GN52E2DP.js +0 -59
  88. package/dist/chunk-TFBQESH7.js +0 -179
  89. package/dist/delivery-manager-J3BRHXUF.js +0 -33
  90. package/dist/down-DSRWSCXY.js +0 -17
  91. package/dist/echo-text-3HBQUWPX.js +0 -33
  92. package/dist/message-delivery-4ZWFDBAY.js +0 -41
  93. package/dist/mind-manager-6DSY5IJW.js +0 -33
  94. package/dist/mind-service-R2FM53KD.js +0 -37
  95. package/dist/scheduler-2FKQCRCM.js +0 -31
  96. package/dist/sleep-manager-F5KWIFOX.js +0 -35
  97. package/dist/system-chat-EQIH6JVL.js +0 -35
  98. package/dist/up-ZTPGC7HB.js +0 -19
package/dist/daemon.js CHANGED
@@ -6,21 +6,21 @@ import {
6
6
  rejectPending,
7
7
  stageFile,
8
8
  validateFilePath
9
- } from "./chunk-M2YMNTHK.js";
9
+ } from "./chunk-2HIVYZDB.js";
10
10
  import {
11
11
  findOpenClawSession,
12
12
  importOpenClawConnectors,
13
13
  importPiSession,
14
14
  parseNameFromIdentity
15
- } from "./chunk-KUJVCDWT.js";
15
+ } from "./chunk-JVK6QQIC.js";
16
16
  import {
17
17
  checkForUpdate,
18
18
  checkForUpdateCached,
19
19
  getCurrentVersion
20
- } from "./chunk-E7XWFDZL.js";
20
+ } from "./chunk-AD46AYZQ.js";
21
21
  import {
22
22
  isHomeOnlyArchive
23
- } from "./chunk-OEJFZHZZ.js";
23
+ } from "./chunk-GZJFC76M.js";
24
24
  import {
25
25
  computeTemplateHash
26
26
  } from "./chunk-QMJCNZ6C.js";
@@ -30,7 +30,7 @@ import {
30
30
  fixModelEscapes,
31
31
  getPlatformDriver,
32
32
  routeOutboundBridge
33
- } from "./chunk-JPDN6ZRJ.js";
33
+ } from "./chunk-FZ5TH5PF.js";
34
34
  import {
35
35
  getBridgeConfig,
36
36
  readBridgesConfig,
@@ -39,13 +39,15 @@ import {
39
39
  resolveChannelMapping,
40
40
  setBridgeConfig,
41
41
  setChannelMapping
42
- } from "./chunk-QPTQKGGF.js";
42
+ } from "./chunk-57LBQ7JX.js";
43
43
  import {
44
+ MindStartupError,
44
45
  RestartTracker,
45
46
  RotatingLog,
46
47
  SESSION_MAX_AGE,
47
48
  announceToSystem,
48
49
  authMiddleware,
50
+ checkHealth,
49
51
  cleanExpiredSessions,
50
52
  clearDeliveredNotices,
51
53
  createSession,
@@ -94,19 +96,17 @@ import {
94
96
  startMindFull,
95
97
  stopMindFull,
96
98
  syncProviderToMinds,
97
- tagUntaggedInbound,
98
- tagUntaggedOutbound,
99
99
  uninstallNpmExtension,
100
100
  writeSystemsConfig
101
- } from "./chunk-6G7R2LPI.js";
101
+ } from "./chunk-PF2AB7VJ.js";
102
102
  import {
103
103
  loadMergedEnv,
104
104
  mindEnvPath,
105
105
  readEnv,
106
106
  sharedEnvPath,
107
107
  writeEnv
108
- } from "./chunk-BLOAJLGI.js";
109
- import "./chunk-43K5UWF7.js";
108
+ } from "./chunk-XDTKJNW5.js";
109
+ import "./chunk-65OLHBCL.js";
110
110
  import {
111
111
  applyInitFiles,
112
112
  applyTemplateHomeFiles,
@@ -135,7 +135,7 @@ import {
135
135
  syncBuiltinSkills,
136
136
  uninstallSkill,
137
137
  updateSkill
138
- } from "./chunk-M3T3Y6FT.js";
138
+ } from "./chunk-FSSUANZJ.js";
139
139
  import {
140
140
  addMessage,
141
141
  createChannel,
@@ -167,7 +167,7 @@ import {
167
167
  setConversationPrivate,
168
168
  subscribe as subscribe2,
169
169
  updateChannelSettings
170
- } from "./chunk-YLTIEEVL.js";
170
+ } from "./chunk-K4EHBHPE.js";
171
171
  import {
172
172
  readVoluteConfig,
173
173
  writeVoluteConfig
@@ -175,15 +175,15 @@ import {
175
175
  import {
176
176
  isSandboxEnabled,
177
177
  wrapForSandbox
178
- } from "./chunk-WR7CSHQ4.js";
178
+ } from "./chunk-C6ESRQZ7.js";
179
179
  import {
180
180
  getActiveMinds,
181
181
  onMindEvent,
182
182
  stopAll
183
- } from "./chunk-VBKRZCR7.js";
183
+ } from "./chunk-M3PGGSBH.js";
184
184
  import {
185
185
  extractTextContent
186
- } from "./chunk-62ICA2FF.js";
186
+ } from "./chunk-SPLPKBES.js";
187
187
  import {
188
188
  PROMPT_DEFAULTS,
189
189
  PROMPT_KEYS,
@@ -206,21 +206,19 @@ import {
206
206
  summarizeTurn,
207
207
  takeErrored,
208
208
  trackToolUse
209
- } from "./chunk-BWZHX46O.js";
210
- import "./chunk-P6DPB33N.js";
211
- import {
212
- exec,
213
- gitExec,
214
- resolveVoluteBin
215
- } from "./chunk-GN52E2DP.js";
209
+ } from "./chunk-3WLWKOGM.js";
210
+ import "./chunk-ATEX3ZPG.js";
216
211
  import {
217
212
  chownMindDir,
218
213
  createMindUser,
219
214
  deleteMindUser,
220
215
  ensureVoluteGroup,
216
+ exec,
217
+ gitExec,
221
218
  isIsolationEnabled,
219
+ resolveVoluteBin,
222
220
  wrapForIsolation
223
- } from "./chunk-TFBQESH7.js";
221
+ } from "./chunk-PGPLCJPK.js";
224
222
  import {
225
223
  addCustomModel,
226
224
  buildCustomModel,
@@ -241,12 +239,12 @@ import {
241
239
  setProviderRefreshHook,
242
240
  setUtilityModel,
243
241
  unqualifyModelId
244
- } from "./chunk-4ZSUUBG5.js";
242
+ } from "./chunk-YSV3VJQK.js";
245
243
  import {
246
244
  isSetupComplete,
247
245
  readGlobalConfig,
248
246
  writeGlobalConfig
249
- } from "./chunk-KZAL7HEQ.js";
247
+ } from "./chunk-ONM2AZFL.js";
250
248
  import {
251
249
  approveUser,
252
250
  changePassword,
@@ -265,11 +263,11 @@ import {
265
263
  syncMindProfile,
266
264
  updateUserProfile,
267
265
  verifyUser
268
- } from "./chunk-5QG36RV7.js";
266
+ } from "./chunk-EFL5UOY4.js";
269
267
  import {
270
268
  broadcast,
271
269
  subscribe
272
- } from "./chunk-K547QOCM.js";
270
+ } from "./chunk-7G2EEYXX.js";
273
271
  import {
274
272
  logBuffer,
275
273
  logger_default
@@ -301,7 +299,7 @@ import {
301
299
  validateMindName,
302
300
  voluteHome,
303
301
  voluteSystemDir
304
- } from "./chunk-IUZ3EV2D.js";
302
+ } from "./chunk-YAMO2U3N.js";
305
303
  import {
306
304
  activity,
307
305
  channels,
@@ -312,11 +310,11 @@ import {
312
310
  systemPrompts,
313
311
  turns,
314
312
  users
315
- } from "./chunk-MHFAWJCC.js";
313
+ } from "./chunk-L3IV23ZG.js";
316
314
  import "./chunk-K3NQKI34.js";
317
315
 
318
316
  // packages/daemon/src/daemon.ts
319
- import { randomBytes } from "crypto";
317
+ import { randomBytes as randomBytes2 } from "crypto";
320
318
  import { chmodSync, mkdirSync as mkdirSync10, readFileSync as readFileSync10, unlinkSync as unlinkSync2, writeFileSync as writeFileSync9 } from "fs";
321
319
  import { homedir as homedir3 } from "os";
322
320
  import { resolve as resolve17 } from "path";
@@ -1105,17 +1103,21 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1105
1103
  }).get("/ai/models", requireAdmin, async (c) => {
1106
1104
  const models = await getAvailableModels();
1107
1105
  const enabled = new Set(getEnabledModels());
1108
- const customIds = new Set(getCustomModels().map((m) => m.id));
1106
+ const customIds = new Set(getCustomModels().map((m) => `${m.provider}:${m.id}`));
1109
1107
  return c.json(
1110
- models.map((m) => ({
1111
- id: m.id,
1112
- name: m.name,
1113
- provider: m.provider,
1114
- contextWindow: m.contextWindow,
1115
- maxTokens: m.maxTokens,
1116
- enabled: enabled.has(m.id),
1117
- custom: customIds.has(m.id)
1118
- }))
1108
+ models.map((m) => {
1109
+ const qualifiedId = `${m.provider}:${m.id}`;
1110
+ return {
1111
+ id: m.id,
1112
+ qualifiedId,
1113
+ name: m.name,
1114
+ provider: m.provider,
1115
+ contextWindow: m.contextWindow,
1116
+ maxTokens: m.maxTokens,
1117
+ enabled: enabled.has(qualifiedId),
1118
+ custom: customIds.has(qualifiedId)
1119
+ };
1120
+ })
1119
1121
  );
1120
1122
  }).put(
1121
1123
  "/ai/models",
@@ -1141,14 +1143,8 @@ var app = new Hono().post("/restart", requireAdmin, (c) => {
1141
1143
  400
1142
1144
  );
1143
1145
  }
1144
- if (getCustomModels().some((m) => m.id === id && m.provider !== provider)) {
1145
- return c.json(
1146
- { error: `A custom model "${id}" is already registered under another provider` },
1147
- 400
1148
- );
1149
- }
1150
1146
  addCustomModel(provider, id, name);
1151
- setEnabledModels([.../* @__PURE__ */ new Set([...getEnabledModels(), id])]);
1147
+ setEnabledModels([.../* @__PURE__ */ new Set([...getEnabledModels(), `${provider}:${id}`])]);
1152
1148
  return c.json({ ok: true });
1153
1149
  }
1154
1150
  ).delete("/ai/models/custom", requireAdmin, (c) => {
@@ -1850,6 +1846,76 @@ async function findOrCreatePuppet(platform, platformId, displayName) {
1850
1846
  }
1851
1847
  }
1852
1848
 
1849
+ // packages/daemon/src/lib/delivery/fan-out.ts
1850
+ var MAX_CONSECUTIVE_MIND_TURNS = 6;
1851
+ var consecutiveMindTurns = /* @__PURE__ */ new Map();
1852
+ async function emitLoopNotice(conversationId) {
1853
+ const systemUser = await getOrCreateSystemUser();
1854
+ await addMessage(conversationId, "user", systemUser.username, [
1855
+ {
1856
+ type: "text",
1857
+ text: `[System] This exchange has continued for ${MAX_CONSECUTIVE_MIND_TURNS} consecutive automated turns without human input, so automatic delivery between minds has been paused. Send a message to resume.`
1858
+ }
1859
+ ]);
1860
+ }
1861
+ async function fanOutToMinds(opts) {
1862
+ const participants = opts.participants ?? await getParticipants(opts.conversationId);
1863
+ const mindParticipants = participants.filter(
1864
+ (p) => p.userType === "mind" || p.userType === "system"
1865
+ );
1866
+ const participantNames = participants.map((p) => p.username);
1867
+ const isDM = opts.isDM ?? participants.length === 2;
1868
+ if (mindParticipants.length > 1) {
1869
+ if (opts.senderIsMind) {
1870
+ const count = (consecutiveMindTurns.get(opts.conversationId) ?? 0) + 1;
1871
+ consecutiveMindTurns.set(opts.conversationId, count);
1872
+ if (count > MAX_CONSECUTIVE_MIND_TURNS) {
1873
+ if (count === MAX_CONSECUTIVE_MIND_TURNS + 1) {
1874
+ await emitLoopNotice(opts.conversationId).catch(
1875
+ (err) => logger_default.warn(`failed to emit loop notice for ${opts.conversationId}`, logger_default.errorData(err))
1876
+ );
1877
+ }
1878
+ logger_default.warn(
1879
+ `loop protection: paused mind fan-out for ${opts.conversationId} after ${count} consecutive mind turns`
1880
+ );
1881
+ return;
1882
+ }
1883
+ } else {
1884
+ consecutiveMindTurns.delete(opts.conversationId);
1885
+ }
1886
+ }
1887
+ const manager = getMindManager();
1888
+ const sm = getSleepManagerIfReady();
1889
+ const targetMinds = mindParticipants.map((ap) => {
1890
+ const key = opts.targetName ? opts.targetName(ap.username) : ap.username;
1891
+ if (manager.isRunning(key) || sm?.isSleeping(ap.username)) return ap.username;
1892
+ return null;
1893
+ }).filter((n) => n !== null && n !== opts.senderName);
1894
+ for (const mindName of targetMinds) {
1895
+ const target = opts.targetName ? opts.targetName(mindName) : mindName;
1896
+ const channel = buildVoluteSlug({
1897
+ participants,
1898
+ mindUsername: mindName,
1899
+ conversationId: opts.conversationId,
1900
+ ...opts.slugExtra
1901
+ });
1902
+ const typingMap = getTypingMap();
1903
+ const currentlyTyping = typingMap.get(channel).filter((name) => participantNames.includes(name));
1904
+ deliverMessage(target, {
1905
+ content: opts.contentBlocks,
1906
+ channel,
1907
+ conversationId: opts.conversationId,
1908
+ sender: opts.senderName,
1909
+ participants: participantNames,
1910
+ participantCount: participants.length,
1911
+ isDM,
1912
+ ...currentlyTyping.length > 0 ? { typing: currentlyTyping } : {}
1913
+ }).catch((err) => {
1914
+ logger_default.warn(`fan-out delivery failed for ${target}`, logger_default.errorData(err));
1915
+ });
1916
+ }
1917
+ }
1918
+
1853
1919
  // packages/daemon/src/web/api/bridges.ts
1854
1920
  var inboundSchema = z3.object({
1855
1921
  content: z3.array(
@@ -1923,7 +1989,7 @@ var app4 = new Hono4().post("/:platform/inbound", zValidator3("json", inboundSch
1923
1989
  }
1924
1990
  const participants = await getParticipants(channel.id);
1925
1991
  if (!participants.some((p) => p.userId === puppet.id)) {
1926
- const { addParticipant } = await import("./conversations-FSZCUUYO.js");
1992
+ const { addParticipant } = await import("./conversations-67WWW25C.js");
1927
1993
  await addParticipant(channel.id, puppet.id);
1928
1994
  }
1929
1995
  const contentBlocks = body.content;
@@ -2013,34 +2079,13 @@ var app4 = new Hono4().post("/:platform/inbound", zValidator3("json", inboundSch
2013
2079
  return c.json(config2.channelMappings);
2014
2080
  });
2015
2081
  async function fanOutToBridgedMinds(opts) {
2016
- const participants = await getParticipants(opts.conversationId);
2017
- const mindParticipants = participants.filter((p) => p.userType === "mind");
2018
- const participantNames = participants.map((p) => p.username);
2019
- const { getMindManager: getMindManager2 } = await import("./mind-manager-6DSY5IJW.js");
2020
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-F5KWIFOX.js");
2021
- const manager = getMindManager2();
2022
- const sm = getSleepManagerIfReady2();
2023
- const targetMinds = mindParticipants.filter((ap) => {
2024
- return (manager.isRunning(ap.username) || sm?.isSleeping(ap.username)) && ap.username !== opts.senderName;
2025
- }).map((ap) => ap.username);
2026
- for (const mindName of targetMinds) {
2027
- const channel = buildVoluteSlug({
2028
- participants,
2029
- mindUsername: mindName,
2030
- conversationId: opts.conversationId
2031
- });
2032
- deliverMessage(mindName, {
2033
- content: opts.contentBlocks,
2034
- channel,
2035
- conversationId: opts.conversationId,
2036
- sender: opts.senderName,
2037
- participants: participantNames,
2038
- participantCount: participants.length,
2039
- isDM: opts.isDM
2040
- }).catch((err) => {
2041
- logger_default.warn(`bridge fan-out delivery failed for ${mindName}`, logger_default.errorData(err));
2042
- });
2043
- }
2082
+ await fanOutToMinds({
2083
+ conversationId: opts.conversationId,
2084
+ contentBlocks: opts.contentBlocks,
2085
+ senderName: opts.senderName,
2086
+ senderIsMind: false,
2087
+ isDM: opts.isDM
2088
+ });
2044
2089
  }
2045
2090
  var bridges_default = app4;
2046
2091
 
@@ -2083,7 +2128,7 @@ config.get("/models", async (c) => {
2083
2128
  id: m.id,
2084
2129
  name: m.name,
2085
2130
  provider: m.provider,
2086
- enabled: enabled.has(m.id)
2131
+ enabled: enabled.has(`${m.provider}:${m.id}`)
2087
2132
  }));
2088
2133
  return c.json(models);
2089
2134
  });
@@ -2229,7 +2274,7 @@ async function notifyMind(mindName, message) {
2229
2274
  const entry = await findMind(mindName);
2230
2275
  if (!entry) return;
2231
2276
  try {
2232
- const { sendSystemMessage } = await import("./system-chat-EQIH6JVL.js");
2277
+ const { sendSystemMessage } = await import("./system-chat-KMQIP7VH.js");
2233
2278
  await sendSystemMessage(mindName, message);
2234
2279
  } catch (err) {
2235
2280
  logger_default.warn(`[file-sharing] notify mind ${mindName} failed`, logger_default.errorData(err));
@@ -3164,10 +3209,13 @@ import {
3164
3209
  } from "fs";
3165
3210
  import { basename, resolve as resolve11 } from "path";
3166
3211
  import { zValidator as zValidator5 } from "@hono/zod-validator";
3167
- import { and as and4, desc as desc3, eq as eq4, sql as sql2 } from "drizzle-orm";
3212
+ import { and as and5, desc as desc4, eq as eq5, sql as sql3 } from "drizzle-orm";
3168
3213
  import { Hono as Hono15 } from "hono";
3169
3214
  import { z as z5 } from "zod";
3170
3215
 
3216
+ // packages/daemon/src/lib/daemon/turn-lifecycle.ts
3217
+ import { and as and4, desc as desc3, eq as eq4, inArray as inArray2, sql as sql2 } from "drizzle-orm";
3218
+
3171
3219
  // packages/daemon/src/lib/daemon/error-classify.ts
3172
3220
  function classify(raw) {
3173
3221
  const s = (raw ?? "").toLowerCase();
@@ -3205,6 +3253,178 @@ function classify(raw) {
3205
3253
  };
3206
3254
  }
3207
3255
 
3256
+ // packages/daemon/src/lib/daemon/turn-lifecycle.ts
3257
+ var llog = logger_default.child("turn-lifecycle");
3258
+ var SUBSTANTIVE_TYPES = /* @__PURE__ */ new Set(["thinking", "text", "tool_use", "tool_result", "outbound"]);
3259
+ var MARKER_RE = /\[volute:(?:outbound|activity):\d+\]/g;
3260
+ var noticeDrainWatermarks = /* @__PURE__ */ new Map();
3261
+ function setNoticeDrainWatermark(mind, session, id) {
3262
+ noticeDrainWatermarks.set(`${mind}:${session}`, id);
3263
+ }
3264
+ function markDeliveredOnCleanTurn(mind, session) {
3265
+ if (!session) return;
3266
+ const wmKey = `${mind}:${session}`;
3267
+ const watermark = noticeDrainWatermarks.get(wmKey);
3268
+ noticeDrainWatermarks.delete(wmKey);
3269
+ const errored = takeErrored(mind, session);
3270
+ if (!errored && watermark != null) {
3271
+ clearDeliveredNotices(mind, session, watermark).catch(
3272
+ (err) => llog.warn(`failed to clear delivered notices for ${mind}:${session}`, logger_default.errorData(err))
3273
+ );
3274
+ }
3275
+ }
3276
+ async function linkPendingInbound(mind, turnId, channel) {
3277
+ if (!channel) return;
3278
+ const db = await getDb();
3279
+ const recent = await db.select({ id: mindHistory.id }).from(mindHistory).where(
3280
+ and4(
3281
+ eq4(mindHistory.mind, mind),
3282
+ eq4(mindHistory.type, "inbound"),
3283
+ sql2`${mindHistory.turn_id} IS NULL`,
3284
+ eq4(mindHistory.channel, channel)
3285
+ )
3286
+ ).orderBy(desc3(mindHistory.id)).limit(5);
3287
+ if (recent.length === 0) return;
3288
+ const ids = recent.map((r) => r.id);
3289
+ await db.update(mindHistory).set({ turn_id: turnId }).where(inArray2(mindHistory.id, ids));
3290
+ await db.update(turns).set({ trigger_event_id: recent[0].id }).where(eq4(turns.id, turnId));
3291
+ }
3292
+ async function handleMindEvent(mind, event) {
3293
+ if (event.type === "session_start" && event.session) {
3294
+ const activeTurnId = getActiveTurnId(mind);
3295
+ if (activeTurnId) await assignSession(mind, activeTurnId, event.session);
3296
+ }
3297
+ let turnId = getActiveTurnId(mind, event.session);
3298
+ if (!turnId && SUBSTANTIVE_TYPES.has(event.type)) {
3299
+ turnId = await createTurn(mind);
3300
+ if (!turnId) {
3301
+ llog.warn(`skipping turn tracking for ${mind}: createTurn failed`);
3302
+ } else {
3303
+ publish2(mind, { mind, type: "turn_created", turnId });
3304
+ if (event.session) await assignSession(mind, turnId, event.session);
3305
+ try {
3306
+ await linkPendingInbound(mind, turnId, event.channel);
3307
+ } catch (err) {
3308
+ llog.warn(
3309
+ `failed to link trigger inbound for turn ${turnId} (mind: ${mind})`,
3310
+ logger_default.errorData(err)
3311
+ );
3312
+ }
3313
+ }
3314
+ }
3315
+ const cleanContent = event.type === "tool_result" && event.content ? event.content.replace(MARKER_RE, "").trimEnd() : event.content;
3316
+ const db = await getDb();
3317
+ let insertedId;
3318
+ try {
3319
+ const result = await db.insert(mindHistory).values({
3320
+ mind,
3321
+ type: event.type,
3322
+ session: event.session ?? null,
3323
+ channel: event.channel ?? null,
3324
+ message_id: event.messageId ?? null,
3325
+ content: cleanContent ?? null,
3326
+ metadata: event.metadata ? JSON.stringify(event.metadata) : null,
3327
+ turn_id: turnId ?? null
3328
+ }).returning({ id: mindHistory.id });
3329
+ insertedId = result[0]?.id;
3330
+ } catch (err) {
3331
+ llog.error(`failed to persist event for ${mind}`, logger_default.errorData(err));
3332
+ }
3333
+ if (event.type === "tool_use" && insertedId != null) {
3334
+ trackToolUse(mind, event.session, insertedId);
3335
+ }
3336
+ if (event.type === "tool_result" && turnId && event.content) {
3337
+ const toolUseEventId = getLastToolUseEventId(mind, event.session);
3338
+ try {
3339
+ await linkToolResultToTurn(mind, turnId, event.content, toolUseEventId);
3340
+ } catch (err) {
3341
+ llog.error("failed to link tool_result to turn", logger_default.errorData(err));
3342
+ }
3343
+ }
3344
+ publish2(mind, {
3345
+ mind,
3346
+ type: event.type,
3347
+ session: event.session,
3348
+ channel: event.channel,
3349
+ messageId: event.messageId,
3350
+ content: cleanContent,
3351
+ metadata: event.metadata,
3352
+ turnId: turnId ?? void 0
3353
+ });
3354
+ if (event.type === "text" && event.channel && cleanContent) {
3355
+ echoTextToChannel(mind, event.channel, cleanContent, turnId ?? void 0, insertedId).catch(
3356
+ (err) => llog.error(`echo-text failed for ${mind} on ${event.channel}`, logger_default.errorData(err))
3357
+ );
3358
+ }
3359
+ onMindEvent(mind, event.type, event.channel);
3360
+ if ((event.type === "text" || event.type === "outbound") && event.channel) {
3361
+ const map = getTypingMap();
3362
+ publishTypingForChannels(map.deleteSender(mind), map);
3363
+ }
3364
+ if (event.type === "error" && event.session) {
3365
+ markErrored(mind, event.session);
3366
+ const { reason, detail } = classify(event.content ?? "");
3367
+ await recordNotice({
3368
+ mind,
3369
+ session: event.session,
3370
+ kind: "turn_error",
3371
+ reason,
3372
+ detail,
3373
+ raw: event.content ?? null
3374
+ });
3375
+ }
3376
+ if (event.type === "done") {
3377
+ const map = getTypingMap();
3378
+ publishTypingForChannels(map.deleteSender(mind), map);
3379
+ broadcast({ type: "mind_done", mind, summary: "Finished processing" });
3380
+ try {
3381
+ getDeliveryManager().sessionDone(mind, event.session);
3382
+ } catch (err) {
3383
+ if (!(err instanceof Error && err.message.includes("not initialized"))) {
3384
+ llog.error(`delivery manager sessionDone failed for ${mind}`, logger_default.errorData(err));
3385
+ }
3386
+ }
3387
+ await completeTurnAndSummarize(mind, event, insertedId);
3388
+ }
3389
+ if (event.type === "usage" && event.metadata) {
3390
+ const inputTokens = event.metadata.input_tokens ?? 0;
3391
+ const outputTokens = event.metadata.output_tokens ?? 0;
3392
+ const tb = getTokenBudget();
3393
+ tb.recordUsage(mind, inputTokens, outputTokens);
3394
+ if (event.session && tb.noteExceeded(mind)) {
3395
+ void recordNotice({
3396
+ mind,
3397
+ session: event.session,
3398
+ kind: "budget",
3399
+ reason: "token_budget",
3400
+ detail: "You've reached your token budget for this period. Further activity may be paused until the budget resets \u2014 wrap up or prioritize accordingly."
3401
+ });
3402
+ }
3403
+ }
3404
+ return { turnId: turnId ?? void 0, insertedId };
3405
+ }
3406
+ async function completeTurnAndSummarize(mind, event, insertedId) {
3407
+ const finish = async () => {
3408
+ const completedTurnId = await completeTurn(mind, event.session);
3409
+ markDeliveredOnCleanTurn(mind, event.session);
3410
+ if (insertedId != null) {
3411
+ summarizeTurn(mind, event.session, event.channel, insertedId, completedTurnId).catch(
3412
+ (err) => llog.error("turn summarization failed", logger_default.errorData(err))
3413
+ );
3414
+ }
3415
+ };
3416
+ try {
3417
+ const dm = getDeliveryManager();
3418
+ const busy = event.session ? dm.isSessionBusy(mind, event.session) : false;
3419
+ if (!busy) await finish();
3420
+ } catch (err) {
3421
+ if (!(err instanceof Error && err.message.includes("not initialized"))) {
3422
+ llog.error("turn completion check failed", logger_default.errorData(err));
3423
+ }
3424
+ await finish();
3425
+ }
3426
+ }
3427
+
3208
3428
  // packages/daemon/src/lib/mind/consolidate.ts
3209
3429
  import { readdirSync, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
3210
3430
  import { resolve as resolve9 } from "path";
@@ -3276,6 +3496,39 @@ ${content2}`);
3276
3496
  }
3277
3497
  }
3278
3498
 
3499
+ // packages/daemon/src/lib/mind/last-known-good.ts
3500
+ import { randomBytes } from "crypto";
3501
+ var glog = logger_default.child("last-known-good");
3502
+ async function hasSrcChanges(dir, mindName) {
3503
+ const status = (await gitExec(["status", "--porcelain", "--", "src"], { cwd: dir, mindName })).trim();
3504
+ return status.length > 0;
3505
+ }
3506
+ async function rollbackSrcChanges(dir, mindName) {
3507
+ if (!await hasSrcChanges(dir, mindName)) return { parked: false };
3508
+ const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
3509
+ const branch = `broken/${ts}-${randomBytes(4).toString("hex")}`;
3510
+ const opts = { cwd: dir, mindName };
3511
+ await gitExec(["add", "--", "src"], opts);
3512
+ await gitExec(
3513
+ ["commit", "--only", "-m", `Parked broken src changes (${branch})`, "--", "src"],
3514
+ opts
3515
+ );
3516
+ const brokenHash = (await gitExec(["rev-parse", "HEAD"], opts)).trim();
3517
+ await gitExec(["reset", "--mixed", "HEAD~1"], opts);
3518
+ await gitExec(["branch", branch, brokenHash], opts);
3519
+ await gitExec(["checkout", "--", "src"], opts);
3520
+ await gitExec(["clean", "-fdq", "--", "src"], opts);
3521
+ glog.info(`parked broken src changes on ${branch} (${dir})`);
3522
+ return { parked: true, branch };
3523
+ }
3524
+ async function commitSrcChanges(dir, mindName) {
3525
+ if (!await hasSrcChanges(dir, mindName)) return;
3526
+ const opts = { cwd: dir, mindName };
3527
+ await gitExec(["add", "--", "src"], opts);
3528
+ await gitExec(["commit", "--only", "-m", "Update src (self-edit)", "--", "src"], opts);
3529
+ glog.info(`committed known-good src changes (${dir})`);
3530
+ }
3531
+
3279
3532
  // packages/daemon/src/lib/mind/variant-cleanup.ts
3280
3533
  import { existsSync as existsSync6, rmSync as rmSync3 } from "fs";
3281
3534
  async function cleanupVariant(variantName, projectRoot, variantPath, opts) {
@@ -3286,7 +3539,7 @@ async function cleanupVariant(variantName, projectRoot, variantPath, opts) {
3286
3539
  logger_default.warn(`failed to stop variant ${variantName}`, logger_default.errorData(err));
3287
3540
  }
3288
3541
  }
3289
- const { findMind: findMind2 } = await import("./registry-MDLNPTTI.js");
3542
+ const { findMind: findMind2 } = await import("./registry-7LQHTRUX.js");
3290
3543
  const variantEntry = await findMind2(variantName);
3291
3544
  const branchName = variantEntry?.branch ?? variantName;
3292
3545
  if (existsSync6(variantPath)) {
@@ -3313,7 +3566,7 @@ async function cleanupVariant(variantName, projectRoot, variantPath, opts) {
3313
3566
  logger_default.warn(`failed to remove variant ${variantName} from DB`, logger_default.errorData(err));
3314
3567
  }
3315
3568
  try {
3316
- chownMindDir(projectRoot, baseName);
3569
+ await chownMindDir(projectRoot, baseName);
3317
3570
  } catch (err) {
3318
3571
  logger_default.error(
3319
3572
  `failed to fix ownership during variant cleanup for ${variantName}`,
@@ -3508,42 +3761,14 @@ function convertAssistantContent(content) {
3508
3761
  return result;
3509
3762
  }
3510
3763
 
3511
- // packages/daemon/src/lib/util/health.ts
3512
- async function checkHealth(port) {
3513
- try {
3514
- const res = await fetch(`http://127.0.0.1:${port}/health`, {
3515
- signal: AbortSignal.timeout(2e3)
3516
- });
3517
- if (!res.ok) return { ok: false };
3518
- const data = await res.json();
3519
- return { ok: true, name: data.name };
3520
- } catch {
3521
- return { ok: false };
3522
- }
3523
- }
3524
-
3525
3764
  // packages/daemon/src/web/api/minds.ts
3526
- var SUBSTANTIVE_TYPES = /* @__PURE__ */ new Set(["thinking", "text", "tool_use", "tool_result", "outbound"]);
3527
- var noticeDrainWatermarks = /* @__PURE__ */ new Map();
3528
- function markDeliveredOnCleanTurn(mind, session) {
3529
- if (!session) return;
3530
- const wmKey = `${mind}:${session}`;
3531
- const watermark = noticeDrainWatermarks.get(wmKey);
3532
- noticeDrainWatermarks.delete(wmKey);
3533
- const errored = takeErrored(mind, session);
3534
- if (!errored && watermark != null) {
3535
- clearDeliveredNotices(mind, session, watermark).catch(
3536
- (err) => logger_default.warn(`failed to clear delivered notices for ${mind}:${session}`, logger_default.errorData(err))
3537
- );
3538
- }
3539
- }
3540
3765
  var _lastActiveCache = { map: /* @__PURE__ */ new Map(), ts: 0 };
3541
3766
  var _LAST_ACTIVE_TTL = 6e4;
3542
3767
  async function getMindStatus(name, port, registryRunning) {
3543
3768
  const manager = getMindManager();
3544
3769
  let status = "stopped";
3545
3770
  try {
3546
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-F5KWIFOX.js");
3771
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-BVU5D2AE.js");
3547
3772
  if (getSleepManagerIfReady2()?.isSleeping(name)) {
3548
3773
  status = "sleeping";
3549
3774
  }
@@ -3704,7 +3929,7 @@ async function mergeUpgradeAndRestart(mindName, dir, worktreeDir, upgradeVariant
3704
3929
  cwd: dir
3705
3930
  });
3706
3931
  }
3707
- chownMindDir(dir, mindName);
3932
+ await chownMindDir(dir, mindName);
3708
3933
  switchWarning = `Switched ${oldTemplate}\u2192${template}: config reset to ${template} defaults, mechanics doc replaced, conversation starts fresh (sessions aren't portable across runtimes).`;
3709
3934
  } catch (err) {
3710
3935
  logger_default.warn(`failed to swap template home files for ${mindName}`, logger_default.errorData(err));
@@ -3786,7 +4011,7 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
3786
4011
  const homeDir = resolve11(dest, "home");
3787
4012
  ensureVoluteGroup();
3788
4013
  createMindUser(name, homeDir);
3789
- chownMindDir(dest, name);
4014
+ await chownMindDir(dest, name);
3790
4015
  await npmInstallAsMind(dest, name);
3791
4016
  await importHistoryFromArchive(name, tempDir);
3792
4017
  importSessionsFromArchive(dest, tempDir);
@@ -3802,7 +4027,7 @@ async function importFromFullArchive(c, tempDir, extractedMindDir, nameOverride,
3802
4027
  rmSync4(resolve11(dest, ".git"), { recursive: true, force: true });
3803
4028
  }
3804
4029
  }
3805
- chownMindDir(dest, name);
4030
+ await chownMindDir(dest, name);
3806
4031
  rmSync4(tempDir, { recursive: true, force: true });
3807
4032
  return c.json({ ok: true, name, port, message: `Imported mind: ${name} (port ${port})` });
3808
4033
  } catch (err) {
@@ -3864,7 +4089,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
3864
4089
  const homeDir = resolve11(dest, "home");
3865
4090
  ensureVoluteGroup();
3866
4091
  createMindUser(name, homeDir);
3867
- chownMindDir(dest, name);
4092
+ await chownMindDir(dest, name);
3868
4093
  await npmInstallAsMind(dest, name);
3869
4094
  let gitWarning;
3870
4095
  try {
@@ -3889,7 +4114,7 @@ async function importFromHomeOnlyArchive(c, tempDir, extractedMindDir, nameOverr
3889
4114
  }
3890
4115
  await importHistoryFromArchive(name, tempDir);
3891
4116
  importSessionsFromArchive(dest, tempDir);
3892
- chownMindDir(dest, name);
4117
+ await chownMindDir(dest, name);
3893
4118
  publishPublicKey(name, publicKeyPem).catch(
3894
4119
  (err) => logger_default.warn(`failed to publish key for ${name}`, { error: err.message })
3895
4120
  );
@@ -4038,7 +4263,7 @@ var app13 = new Hono15().post("/", requireAdminOrSystem, zValidator5("json", cre
4038
4263
  applyInitFiles(dest);
4039
4264
  const { publicKeyPem } = generateIdentity(dest);
4040
4265
  {
4041
- const { readGlobalConfig: readGlobal } = await import("./setup-IYOUPCIK.js");
4266
+ const { readGlobalConfig: readGlobal } = await import("./setup-25MMJ3J4.js");
4042
4267
  const mindDefaults = readGlobal().mindDefaults;
4043
4268
  const config2 = readVoluteConfig(dest);
4044
4269
  if (!config2) throw new Error("Failed to read volute.json after identity generation");
@@ -4105,7 +4330,7 @@ var app13 = new Hono15().post("/", requireAdminOrSystem, zValidator5("json", cre
4105
4330
  const homeDir = resolve11(dest, "home");
4106
4331
  ensureVoluteGroup();
4107
4332
  createMindUser(name, homeDir);
4108
- chownMindDir(dest, name);
4333
+ await chownMindDir(dest, name);
4109
4334
  await npmInstallAsMind(dest, name);
4110
4335
  let gitWarning;
4111
4336
  try {
@@ -4118,7 +4343,7 @@ var app13 = new Hono15().post("/", requireAdminOrSystem, zValidator5("json", cre
4118
4343
  rmSync4(resolve11(dest, ".git"), { recursive: true, force: true });
4119
4344
  gitWarning = "Git setup failed \u2014 variants and upgrades won't be available until git is initialized.";
4120
4345
  }
4121
- chownMindDir(dest, name);
4346
+ await chownMindDir(dest, name);
4122
4347
  if (body.stage === "seed") {
4123
4348
  const descLine = body.description ? `
4124
4349
  The human who planted you described you as: "${body.description}"
@@ -4129,7 +4354,7 @@ The human who planted you described you as: "${body.description}"
4129
4354
  }
4130
4355
  let skillSet = body.skills ?? (body.stage === "seed" ? SEED_SKILLS : getStandardSkillsWithExtensions());
4131
4356
  if (body.stage === "seed" && !body.skills) {
4132
- const { isImagegenEnabled } = await import("./setup-IYOUPCIK.js");
4357
+ const { isImagegenEnabled } = await import("./setup-25MMJ3J4.js");
4133
4358
  if (isImagegenEnabled()) {
4134
4359
  skillSet = [...skillSet, "imagegen"];
4135
4360
  }
@@ -4147,7 +4372,7 @@ The human who planted you described you as: "${body.description}"
4147
4372
  try {
4148
4373
  const spiritEntry = await findMind("volute");
4149
4374
  if (spiritEntry) {
4150
- const { spiritDir } = await import("./spirit-XLD6QXSS.js");
4375
+ const { spiritDir } = await import("./spirit-JQQHUVWR.js");
4151
4376
  const sDir = spiritEntry.dir ?? spiritDir();
4152
4377
  const spiritConfig = readVoluteConfig(sDir) ?? {};
4153
4378
  const schedules = spiritConfig.schedules ?? [];
@@ -4162,7 +4387,7 @@ The human who planted you described you as: "${body.description}"
4162
4387
  });
4163
4388
  spiritConfig.schedules = schedules;
4164
4389
  writeVoluteConfig(sDir, spiritConfig);
4165
- const { getScheduler: getScheduler2 } = await import("./scheduler-2FKQCRCM.js");
4390
+ const { getScheduler: getScheduler2 } = await import("./scheduler-BR6C6B3E.js");
4166
4391
  getScheduler2().loadSchedules("volute", sDir);
4167
4392
  }
4168
4393
  }
@@ -4292,7 +4517,7 @@ ${user.trimEnd()}
4292
4517
  const homeDir = resolve11(dest, "home");
4293
4518
  ensureVoluteGroup();
4294
4519
  createMindUser(name, homeDir);
4295
- chownMindDir(dest, name);
4520
+ await chownMindDir(dest, name);
4296
4521
  await npmInstallAsMind(dest, name);
4297
4522
  if (!hasMemory && dailyLogCount > 0) {
4298
4523
  await consolidateMemory(dest);
@@ -4314,7 +4539,7 @@ ${user.trimEnd()}
4314
4539
  }
4315
4540
  }
4316
4541
  importOpenClawConnectors(name, dest);
4317
- chownMindDir(dest, name);
4542
+ await chownMindDir(dest, name);
4318
4543
  publishPublicKey(name, importPublicKey).catch(
4319
4544
  (err) => logger_default.warn(`failed to publish key for ${name}`, { error: err.message })
4320
4545
  );
@@ -4340,7 +4565,7 @@ ${user.trimEnd()}
4340
4565
  const db = await getDb();
4341
4566
  const lastActiveRows = await db.select({
4342
4567
  mind: mindHistory.mind,
4343
- lastActiveAt: sql2`MAX(${mindHistory.created_at})`
4568
+ lastActiveAt: sql3`MAX(${mindHistory.created_at})`
4344
4569
  }).from(mindHistory).groupBy(mindHistory.mind);
4345
4570
  lastActiveMap = new Map(lastActiveRows.map((r) => [r.mind, r.lastActiveAt]));
4346
4571
  _lastActiveCache.map = lastActiveMap;
@@ -4427,7 +4652,7 @@ ${user.trimEnd()}
4427
4652
  const manager = getMindManager();
4428
4653
  try {
4429
4654
  if (context?.type === "reload") {
4430
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-F5KWIFOX.js");
4655
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-BVU5D2AE.js");
4431
4656
  const sleepState = getSleepManagerIfReady2()?.getState(name);
4432
4657
  if (sleepState?.sleeping) {
4433
4658
  logger_default.info(`skipping reload for ${name} during sleep \u2014 will apply on next wake`);
@@ -4499,7 +4724,39 @@ ${user.trimEnd()}
4499
4724
  logger_default.error(`failed to inject sprouted message for ${baseName}`, logger_default.errorData(err));
4500
4725
  }
4501
4726
  }
4502
- await startMindFull(name);
4727
+ const repoDir = entry.parent ? entry.dir : mindDir(name);
4728
+ try {
4729
+ await startMindFull(name);
4730
+ } catch (startErr) {
4731
+ const stderr = startErr instanceof MindStartupError ? startErr.stderr : void 0;
4732
+ let rollback = { parked: false };
4733
+ try {
4734
+ rollback = await rollbackSrcChanges(repoDir, name);
4735
+ } catch (rbErr) {
4736
+ logger_default.error(`failed to roll back src changes for ${name}`, logger_default.errorData(rbErr));
4737
+ }
4738
+ if (!rollback.parked) throw startErr;
4739
+ await startMindFull(name);
4740
+ const startMsg = startErr instanceof Error ? startErr.message : String(startErr);
4741
+ const errLine = (stderr ?? startMsg).trim().split("\n").filter(Boolean).pop() ?? "";
4742
+ await recordNotice({
4743
+ mind: name,
4744
+ session: "main",
4745
+ kind: "startup",
4746
+ reason: "startup_failed",
4747
+ detail: `Your last change to src/ broke startup, so it was rolled back and your previous working code was restored. The broken change is preserved on branch \`${rollback.branch}\` \u2014 check it out to inspect and fix it. Error: ${errLine}`,
4748
+ raw: stderr ?? null
4749
+ });
4750
+ return c.json({
4751
+ ok: true,
4752
+ recovered: true,
4753
+ brokenBranch: rollback.branch,
4754
+ port: targetPort
4755
+ });
4756
+ }
4757
+ await commitSrcChanges(repoDir, name).catch(
4758
+ (e) => logger_default.error(`failed to commit known-good src for ${name}`, logger_default.errorData(e))
4759
+ );
4503
4760
  return c.json({ ok: true, port: targetPort });
4504
4761
  } catch (err) {
4505
4762
  return c.json({ error: err instanceof Error ? err.message : "Failed to restart mind" }, 500);
@@ -4522,7 +4779,7 @@ ${user.trimEnd()}
4522
4779
  const name = c.req.param("name");
4523
4780
  const entry = await findMind(name);
4524
4781
  if (!entry) return c.json({ error: "Mind not found" }, 404);
4525
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-F5KWIFOX.js");
4782
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-BVU5D2AE.js");
4526
4783
  const sm = getSleepManagerIfReady2();
4527
4784
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
4528
4785
  return c.json(sm.getState(name));
@@ -4530,7 +4787,7 @@ ${user.trimEnd()}
4530
4787
  const name = c.req.param("name");
4531
4788
  const entry = await findMind(name);
4532
4789
  if (!entry) return c.json({ error: "Mind not found" }, 404);
4533
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-F5KWIFOX.js");
4790
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-BVU5D2AE.js");
4534
4791
  const sm = getSleepManagerIfReady2();
4535
4792
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
4536
4793
  if (sm.isSleeping(name)) return c.json({ error: "Mind is already sleeping" }, 409);
@@ -4550,7 +4807,7 @@ ${user.trimEnd()}
4550
4807
  const name = c.req.param("name");
4551
4808
  const entry = await findMind(name);
4552
4809
  if (!entry) return c.json({ error: "Mind not found" }, 404);
4553
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-F5KWIFOX.js");
4810
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-BVU5D2AE.js");
4554
4811
  const sm = getSleepManagerIfReady2();
4555
4812
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
4556
4813
  const sleepState = sm.getState(name);
@@ -4565,7 +4822,7 @@ ${user.trimEnd()}
4565
4822
  const name = c.req.param("name");
4566
4823
  const entry = await findMind(name);
4567
4824
  if (!entry) return c.json({ error: "Mind not found" }, 404);
4568
- const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-F5KWIFOX.js");
4825
+ const { getSleepManagerIfReady: getSleepManagerIfReady2 } = await import("./sleep-manager-BVU5D2AE.js");
4569
4826
  const sm = getSleepManagerIfReady2();
4570
4827
  if (!sm) return c.json({ error: "Sleep manager not initialized" }, 503);
4571
4828
  const flushed = await sm.flushQueuedMessages(name);
@@ -4583,7 +4840,7 @@ ${user.trimEnd()}
4583
4840
  if (body.avatar !== void 0) profile.avatar = basename(body.avatar);
4584
4841
  config2.profile = profile;
4585
4842
  writeVoluteConfig(dir, config2);
4586
- const { syncMindProfile: syncMindProfile2 } = await import("./auth-EAASQPTN.js");
4843
+ const { syncMindProfile: syncMindProfile2 } = await import("./auth-3RIG6GKN.js");
4587
4844
  await syncMindProfile2(name, profile);
4588
4845
  broadcast({ type: "profile_updated", mind: name, summary: `${name} profile updated` });
4589
4846
  return c.json({ ok: true });
@@ -4599,21 +4856,21 @@ ${user.trimEnd()}
4599
4856
  const rawSpirit = Number(process.env.VOLUTE_NURTURE_SPIRIT_MINUTES);
4600
4857
  const spiritThreshold = Number.isNaN(rawSpirit) ? 15 : rawSpirit;
4601
4858
  const lastCreatorMsg = await db.select({ created_at: mindHistory.created_at }).from(mindHistory).where(
4602
- and4(
4603
- eq4(mindHistory.mind, name),
4604
- eq4(mindHistory.type, "inbound"),
4605
- sql2`${mindHistory.sender} != 'volute'`,
4606
- sql2`${mindHistory.sender} != ${name}`,
4607
- sql2`${mindHistory.sender} IS NOT NULL`
4859
+ and5(
4860
+ eq5(mindHistory.mind, name),
4861
+ eq5(mindHistory.type, "inbound"),
4862
+ sql3`${mindHistory.sender} != 'volute'`,
4863
+ sql3`${mindHistory.sender} != ${name}`,
4864
+ sql3`${mindHistory.sender} IS NOT NULL`
4608
4865
  )
4609
- ).orderBy(desc3(mindHistory.created_at)).limit(1);
4866
+ ).orderBy(desc4(mindHistory.created_at)).limit(1);
4610
4867
  const lastSpiritMsg = await db.select({ created_at: mindHistory.created_at }).from(mindHistory).where(
4611
- and4(
4612
- eq4(mindHistory.mind, name),
4613
- eq4(mindHistory.type, "inbound"),
4614
- eq4(mindHistory.sender, "volute")
4868
+ and5(
4869
+ eq5(mindHistory.mind, name),
4870
+ eq5(mindHistory.type, "inbound"),
4871
+ eq5(mindHistory.sender, "volute")
4615
4872
  )
4616
- ).orderBy(desc3(mindHistory.created_at)).limit(1);
4873
+ ).orderBy(desc4(mindHistory.created_at)).limit(1);
4617
4874
  const now = Date.now();
4618
4875
  const creatorTime = lastCreatorMsg[0] ? new Date(lastCreatorMsg[0].created_at).getTime() : 0;
4619
4876
  const spiritTime = lastSpiritMsg[0] ? new Date(lastSpiritMsg[0].created_at).getTime() : 0;
@@ -4630,7 +4887,7 @@ ${user.trimEnd()}
4630
4887
  const config2 = readVoluteConfig(dir);
4631
4888
  const displayNameSet = !!config2?.profile?.displayName;
4632
4889
  const avatarSet = !!config2?.profile?.avatar;
4633
- const { isImagegenEnabled } = await import("./setup-IYOUPCIK.js");
4890
+ const { isImagegenEnabled } = await import("./setup-25MMJ3J4.js");
4634
4891
  const imagegenEnabled = isImagegenEnabled();
4635
4892
  const done = [];
4636
4893
  const remaining = [];
@@ -4668,7 +4925,7 @@ ${user.trimEnd()}
4668
4925
  try {
4669
4926
  const spiritEntry = await findMind("volute");
4670
4927
  if (spiritEntry) {
4671
- const { spiritDir } = await import("./spirit-XLD6QXSS.js");
4928
+ const { spiritDir } = await import("./spirit-JQQHUVWR.js");
4672
4929
  const sDir = spiritEntry.dir ?? spiritDir();
4673
4930
  const spiritConfig = readVoluteConfig(sDir);
4674
4931
  if (spiritConfig?.schedules) {
@@ -4676,7 +4933,7 @@ ${user.trimEnd()}
4676
4933
  spiritConfig.schedules = spiritConfig.schedules.filter((s) => s.id !== nurtureId);
4677
4934
  if (spiritConfig.schedules.length === 0) spiritConfig.schedules = void 0;
4678
4935
  writeVoluteConfig(sDir, spiritConfig);
4679
- const { getScheduler: getScheduler2 } = await import("./scheduler-2FKQCRCM.js");
4936
+ const { getScheduler: getScheduler2 } = await import("./scheduler-BR6C6B3E.js");
4680
4937
  getScheduler2().loadSchedules("volute", sDir);
4681
4938
  }
4682
4939
  }
@@ -4793,7 +5050,7 @@ ${user.trimEnd()}
4793
5050
  cwd: worktreeDir
4794
5051
  });
4795
5052
  }
4796
- chownMindDir(dir, mindName);
5053
+ await chownMindDir(dir, mindName);
4797
5054
  try {
4798
5055
  const result = await mergeUpgradeAndRestart(
4799
5056
  mindName,
@@ -4859,7 +5116,7 @@ ${user.trimEnd()}
4859
5116
  await configureGitIdentity(mindName, { cwd: dir, mindName, env });
4860
5117
  await gitExec(["add", "-A"], { cwd: dir, mindName, env });
4861
5118
  await gitExec(["commit", "-m", "initial commit"], { cwd: dir, mindName, env });
4862
- chownMindDir(dir, mindName);
5119
+ await chownMindDir(dir, mindName);
4863
5120
  } catch (err) {
4864
5121
  rmSync4(resolve11(dir, ".git"), { recursive: true, force: true });
4865
5122
  return c.json(
@@ -4918,7 +5175,7 @@ ${user.trimEnd()}
4918
5175
  });
4919
5176
  }
4920
5177
  }
4921
- chownMindDir(dir, mindName);
5178
+ await chownMindDir(dir, mindName);
4922
5179
  if (hasConflicts) {
4923
5180
  return c.json({
4924
5181
  ok: false,
@@ -5111,7 +5368,7 @@ ${user.trimEnd()}
5111
5368
  }
5112
5369
  writeVoluteConfig(dir, existing);
5113
5370
  if (body.unescapeNewlines !== void 0) {
5114
- const { clearEchoTextCache } = await import("./echo-text-3HBQUWPX.js");
5371
+ const { clearEchoTextCache } = await import("./echo-text-XKBJM6RV.js");
5115
5372
  clearEchoTextCache(name);
5116
5373
  }
5117
5374
  const needsConfigJson = body.model !== void 0 || body.thinkingLevel !== void 0 || body.maxThinkingTokens !== void 0 || body.compaction !== void 0;
@@ -5188,7 +5445,7 @@ ${user.trimEnd()}
5188
5445
  }
5189
5446
  if (entry.mindType === "spirit" && body.model !== void 0) {
5190
5447
  try {
5191
- const { readGlobalConfig: readGlobalConfig2, writeGlobalConfig: writeGlobalConfig2 } = await import("./setup-IYOUPCIK.js");
5448
+ const { readGlobalConfig: readGlobalConfig2, writeGlobalConfig: writeGlobalConfig2 } = await import("./setup-25MMJ3J4.js");
5192
5449
  const globalConfig = readGlobalConfig2();
5193
5450
  globalConfig.spiritModel = body.model;
5194
5451
  writeGlobalConfig2(globalConfig);
@@ -5216,7 +5473,7 @@ ${user.trimEnd()}
5216
5473
  if (!body.systemPrompt || !body.message) {
5217
5474
  return c.json({ error: "systemPrompt and message required" }, 400);
5218
5475
  }
5219
- const { aiComplete: aiCompleteFn, isAiConfigured } = await import("./ai-service-3GBOJGUA.js");
5476
+ const { aiComplete: aiCompleteFn, isAiConfigured } = await import("./ai-service-FBCCEYF5.js");
5220
5477
  if (!isAiConfigured()) {
5221
5478
  return c.json({ error: "AI service not configured" }, 503);
5222
5479
  }
@@ -5237,166 +5494,7 @@ ${user.trimEnd()}
5237
5494
  if (!body.type) {
5238
5495
  return c.json({ error: "type required" }, 400);
5239
5496
  }
5240
- if (body.type === "session_start" && body.session) {
5241
- const activeTurnId = getActiveTurnId(baseName);
5242
- if (activeTurnId) {
5243
- await assignSession(baseName, activeTurnId, body.session);
5244
- }
5245
- }
5246
- let turnId = getActiveTurnId(baseName, body.session);
5247
- if (!turnId && SUBSTANTIVE_TYPES.has(body.type)) {
5248
- turnId = await createTurn(baseName);
5249
- if (!turnId) {
5250
- logger_default.warn(`skipping turn tracking for ${baseName}: createTurn failed`);
5251
- } else {
5252
- publish2(baseName, { mind: baseName, type: "turn_created", turnId });
5253
- if (body.session) {
5254
- await assignSession(baseName, turnId, body.session);
5255
- }
5256
- try {
5257
- await tagUntaggedInbound(baseName, turnId, {
5258
- setTrigger: true,
5259
- channel: body.channel
5260
- });
5261
- } catch (err) {
5262
- logger_default.warn(
5263
- `failed to link trigger/tag inbounds for turn ${turnId} (mind: ${baseName})`,
5264
- logger_default.errorData(err)
5265
- );
5266
- }
5267
- }
5268
- }
5269
- const MARKER_RE = /\[volute:(?:outbound|activity):\d+\]/g;
5270
- const cleanContent = body.type === "tool_result" && body.content ? body.content.replace(MARKER_RE, "").trimEnd() : body.content;
5271
- const db = await getDb();
5272
- let insertedId;
5273
- try {
5274
- const result = await db.insert(mindHistory).values({
5275
- mind: baseName,
5276
- type: body.type,
5277
- session: body.session ?? null,
5278
- channel: body.channel ?? null,
5279
- message_id: body.messageId ?? null,
5280
- content: cleanContent ?? null,
5281
- metadata: body.metadata ? JSON.stringify(body.metadata) : null,
5282
- turn_id: turnId ?? null
5283
- }).returning({ id: mindHistory.id });
5284
- insertedId = result[0]?.id;
5285
- } catch (err) {
5286
- logger_default.error(`failed to persist event for ${baseName}`, logger_default.errorData(err));
5287
- }
5288
- if (body.type === "tool_use" && insertedId != null) {
5289
- trackToolUse(baseName, body.session, insertedId);
5290
- }
5291
- if (body.type === "tool_result" && turnId && body.content) {
5292
- const toolUseEventId = getLastToolUseEventId(baseName, body.session);
5293
- try {
5294
- await linkToolResultToTurn(baseName, turnId, body.content, toolUseEventId);
5295
- } catch (err) {
5296
- logger_default.error("failed to link outbound to turn", logger_default.errorData(err));
5297
- }
5298
- }
5299
- publish2(baseName, {
5300
- mind: baseName,
5301
- type: body.type,
5302
- session: body.session,
5303
- channel: body.channel,
5304
- messageId: body.messageId,
5305
- content: cleanContent,
5306
- metadata: body.metadata,
5307
- turnId: turnId ?? void 0
5308
- });
5309
- if (body.type === "text" && body.channel && cleanContent) {
5310
- echoTextToChannel(
5311
- baseName,
5312
- body.channel,
5313
- cleanContent,
5314
- turnId ?? void 0,
5315
- insertedId
5316
- ).catch(
5317
- (err) => logger_default.error(`echo-text failed for ${baseName} on ${body.channel}`, logger_default.errorData(err))
5318
- );
5319
- }
5320
- onMindEvent(baseName, body.type, body.channel);
5321
- if ((body.type === "text" || body.type === "outbound") && body.channel) {
5322
- const map = getTypingMap();
5323
- const affected = map.deleteSender(baseName);
5324
- publishTypingForChannels(affected, map);
5325
- }
5326
- if (body.type === "error" && body.session) {
5327
- markErrored(baseName, body.session);
5328
- const { reason, detail } = classify(body.content ?? "");
5329
- await recordNotice({
5330
- mind: baseName,
5331
- session: body.session,
5332
- kind: "turn_error",
5333
- reason,
5334
- detail,
5335
- raw: body.content ?? null
5336
- });
5337
- }
5338
- if (body.type === "done") {
5339
- const map = getTypingMap();
5340
- const affected = map.deleteSender(baseName);
5341
- publishTypingForChannels(affected, map);
5342
- broadcast({ type: "mind_done", mind: baseName, summary: "Finished processing" });
5343
- try {
5344
- getDeliveryManager().sessionDone(baseName, body.session);
5345
- } catch (err) {
5346
- if (!(err instanceof Error && err.message.includes("not initialized"))) {
5347
- logger_default.error(`delivery manager sessionDone failed for ${baseName}`, logger_default.errorData(err));
5348
- }
5349
- }
5350
- try {
5351
- const dm = getDeliveryManager();
5352
- const busy = body.session ? dm.isSessionBusy(baseName, body.session) : false;
5353
- if (!busy) {
5354
- const completedTurnId = await completeTurn(baseName, body.session);
5355
- if (completedTurnId) {
5356
- tagUntaggedOutbound(baseName, completedTurnId).catch(
5357
- (err) => logger_default.warn("failed to tag orphaned outbound records", logger_default.errorData(err))
5358
- );
5359
- }
5360
- markDeliveredOnCleanTurn(baseName, body.session);
5361
- if (insertedId != null) {
5362
- summarizeTurn(baseName, body.session, body.channel, insertedId, completedTurnId).catch(
5363
- (err) => logger_default.error("turn summarization failed", logger_default.errorData(err))
5364
- );
5365
- }
5366
- }
5367
- } catch (err) {
5368
- if (!(err instanceof Error && err.message.includes("not initialized"))) {
5369
- logger_default.error("turn completion check failed", logger_default.errorData(err));
5370
- }
5371
- const completedTurnId = await completeTurn(baseName, body.session);
5372
- if (completedTurnId) {
5373
- tagUntaggedOutbound(baseName, completedTurnId).catch(
5374
- (err2) => logger_default.warn("failed to tag orphaned outbound records", logger_default.errorData(err2))
5375
- );
5376
- }
5377
- markDeliveredOnCleanTurn(baseName, body.session);
5378
- if (insertedId != null) {
5379
- summarizeTurn(baseName, body.session, body.channel, insertedId, completedTurnId).catch(
5380
- (err2) => logger_default.error("turn summarization failed", logger_default.errorData(err2))
5381
- );
5382
- }
5383
- }
5384
- }
5385
- if (body.type === "usage" && body.metadata) {
5386
- const inputTokens = body.metadata.input_tokens ?? 0;
5387
- const outputTokens = body.metadata.output_tokens ?? 0;
5388
- const tb = getTokenBudget();
5389
- tb.recordUsage(baseName, inputTokens, outputTokens);
5390
- if (body.session && tb.noteExceeded(baseName)) {
5391
- void recordNotice({
5392
- mind: baseName,
5393
- session: body.session,
5394
- kind: "budget",
5395
- reason: "token_budget",
5396
- detail: "You've reached your token budget for this period. Further activity may be paused until the budget resets \u2014 wrap up or prioritize accordingly."
5397
- });
5398
- }
5399
- }
5497
+ await handleMindEvent(baseName, body);
5400
5498
  return c.json({ ok: true });
5401
5499
  }).get("/:name/events", requireSelf(), async (c) => {
5402
5500
  const name = c.req.param("name");
@@ -5485,32 +5583,32 @@ ${user.trimEnd()}
5485
5583
  const db = await getDb();
5486
5584
  const rows = await db.select({
5487
5585
  session: mindHistory.session,
5488
- started_at: sql2`MIN(${mindHistory.created_at})`,
5489
- event_count: sql2`COUNT(*)`,
5490
- message_count: sql2`SUM(CASE WHEN ${mindHistory.type} IN ('inbound','outbound') THEN 1 ELSE 0 END)`,
5491
- tool_count: sql2`SUM(CASE WHEN ${mindHistory.type}='tool_use' THEN 1 ELSE 0 END)`
5492
- }).from(mindHistory).where(and4(eq4(mindHistory.mind, name), sql2`${mindHistory.session} IS NOT NULL`)).groupBy(mindHistory.session).orderBy(sql2`MIN(${mindHistory.created_at}) DESC`);
5586
+ started_at: sql3`MIN(${mindHistory.created_at})`,
5587
+ event_count: sql3`COUNT(*)`,
5588
+ message_count: sql3`SUM(CASE WHEN ${mindHistory.type} IN ('inbound','outbound') THEN 1 ELSE 0 END)`,
5589
+ tool_count: sql3`SUM(CASE WHEN ${mindHistory.type}='tool_use' THEN 1 ELSE 0 END)`
5590
+ }).from(mindHistory).where(and5(eq5(mindHistory.mind, name), sql3`${mindHistory.session} IS NOT NULL`)).groupBy(mindHistory.session).orderBy(sql3`MIN(${mindHistory.created_at}) DESC`);
5493
5591
  return c.json(rows);
5494
5592
  }).get("/:name/history/channels", requireSelf(), async (c) => {
5495
5593
  const name = c.req.param("name");
5496
5594
  const db = await getDb();
5497
- const rows = await db.selectDistinct({ channel: mindHistory.channel }).from(mindHistory).where(eq4(mindHistory.mind, name));
5595
+ const rows = await db.selectDistinct({ channel: mindHistory.channel }).from(mindHistory).where(eq5(mindHistory.mind, name));
5498
5596
  return c.json(rows.map((r) => r.channel));
5499
5597
  }).get("/:name/history/export", requireSelf(), async (c) => {
5500
5598
  const name = c.req.param("name");
5501
5599
  if (!await findMind(name)) return c.json({ error: "Mind not found" }, 404);
5502
5600
  const db = await getDb();
5503
- const rows = await db.select().from(mindHistory).where(eq4(mindHistory.mind, name));
5601
+ const rows = await db.select().from(mindHistory).where(eq5(mindHistory.mind, name));
5504
5602
  return c.json(rows);
5505
5603
  }).get("/:name/history/turn", requireSelf(), async (c) => {
5506
5604
  const name = c.req.param("name");
5507
5605
  const turnId = c.req.query("turn_id");
5508
5606
  const detail = c.req.query("detail") === "1";
5509
5607
  const db = await getDb();
5510
- const typeFilter = detail ? void 0 : sql2`${mindHistory.type} IN ('inbound','outbound','tool_use','tool_result','text','thinking','activity')`;
5608
+ const typeFilter = detail ? void 0 : sql3`${mindHistory.type} IN ('inbound','outbound','tool_use','tool_result','text','thinking','activity')`;
5511
5609
  let rows;
5512
5610
  if (turnId) {
5513
- rows = await db.select().from(mindHistory).where(and4(eq4(mindHistory.mind, name), eq4(mindHistory.turn_id, turnId), typeFilter)).orderBy(mindHistory.id);
5611
+ rows = await db.select().from(mindHistory).where(and5(eq5(mindHistory.mind, name), eq5(mindHistory.turn_id, turnId), typeFilter)).orderBy(mindHistory.id);
5514
5612
  } else {
5515
5613
  const session = c.req.query("session");
5516
5614
  const fromId = parseInt(c.req.query("from_id") ?? "", 10);
@@ -5519,11 +5617,11 @@ ${user.trimEnd()}
5519
5617
  return c.json({ error: "turn_id, or session with from_id and to_id, required" }, 400);
5520
5618
  }
5521
5619
  rows = await db.select().from(mindHistory).where(
5522
- and4(
5523
- eq4(mindHistory.mind, name),
5524
- eq4(mindHistory.session, session),
5525
- sql2`${mindHistory.id} >= ${fromId}`,
5526
- sql2`${mindHistory.id} <= ${toId}`,
5620
+ and5(
5621
+ eq5(mindHistory.mind, name),
5622
+ eq5(mindHistory.session, session),
5623
+ sql3`${mindHistory.id} >= ${fromId}`,
5624
+ sql3`${mindHistory.id} <= ${toId}`,
5527
5625
  typeFilter
5528
5626
  )
5529
5627
  ).orderBy(mindHistory.id);
@@ -5536,14 +5634,14 @@ ${user.trimEnd()}
5536
5634
  let sinceTimestamp = null;
5537
5635
  if (currentSession) {
5538
5636
  const lastTurn = await db.select({ turn_id: mindHistory.turn_id }).from(mindHistory).where(
5539
- and4(
5540
- eq4(mindHistory.mind, name),
5541
- eq4(mindHistory.session, currentSession),
5542
- sql2`${mindHistory.turn_id} IS NOT NULL`
5637
+ and5(
5638
+ eq5(mindHistory.mind, name),
5639
+ eq5(mindHistory.session, currentSession),
5640
+ sql3`${mindHistory.turn_id} IS NOT NULL`
5543
5641
  )
5544
- ).orderBy(desc3(mindHistory.created_at)).limit(1);
5642
+ ).orderBy(desc4(mindHistory.created_at)).limit(1);
5545
5643
  if (lastTurn.length > 0 && lastTurn[0].turn_id) {
5546
- const firstEvent = await db.select({ created_at: mindHistory.created_at }).from(mindHistory).where(eq4(mindHistory.turn_id, lastTurn[0].turn_id)).orderBy(mindHistory.created_at).limit(1);
5644
+ const firstEvent = await db.select({ created_at: mindHistory.created_at }).from(mindHistory).where(eq5(mindHistory.turn_id, lastTurn[0].turn_id)).orderBy(mindHistory.created_at).limit(1);
5547
5645
  if (firstEvent.length > 0) {
5548
5646
  sinceTimestamp = firstEvent[0].created_at;
5549
5647
  }
@@ -5553,18 +5651,18 @@ ${user.trimEnd()}
5553
5651
  sinceTimestamp = new Date(Date.now() - 36e5).toISOString().replace("T", " ").slice(0, 19);
5554
5652
  }
5555
5653
  const conditions = [
5556
- eq4(summaries.mind, name),
5557
- eq4(summaries.period, "turn"),
5558
- sql2`${summaries.created_at} > ${sinceTimestamp}`
5654
+ eq5(summaries.mind, name),
5655
+ eq5(summaries.period, "turn"),
5656
+ sql3`${summaries.created_at} > ${sinceTimestamp}`
5559
5657
  ];
5560
5658
  if (currentSession) {
5561
- conditions.push(sql2`${turns.session} != ${currentSession}`);
5659
+ conditions.push(sql3`${turns.session} != ${currentSession}`);
5562
5660
  }
5563
5661
  const rows = await db.select({
5564
5662
  session: turns.session,
5565
5663
  content: summaries.content,
5566
5664
  created_at: summaries.created_at
5567
- }).from(summaries).innerJoin(turns, eq4(turns.id, summaries.period_key)).where(and4(...conditions)).orderBy(desc3(summaries.created_at)).limit(50);
5665
+ }).from(summaries).innerJoin(turns, eq5(turns.id, summaries.period_key)).where(and5(...conditions)).orderBy(desc4(summaries.created_at)).limit(50);
5568
5666
  if (rows.length === 0) {
5569
5667
  return c.json({ context: null });
5570
5668
  }
@@ -5583,7 +5681,7 @@ ${lines.join("\n")}` });
5583
5681
  const notices = await drainNotices(baseName, session);
5584
5682
  if (notices.length === 0) return c.json({ context: null, notices: [] });
5585
5683
  const maxId = notices.reduce((m, n) => Math.max(m, n.id), 0);
5586
- noticeDrainWatermarks.set(`${baseName}:${session}`, maxId);
5684
+ setNoticeDrainWatermark(baseName, session, maxId);
5587
5685
  return c.json({ context: formatNotices(notices), notices });
5588
5686
  }).get("/:name/history", requireSelf(), async (c) => {
5589
5687
  const name = c.req.param("name");
@@ -5594,18 +5692,18 @@ ${lines.join("\n")}` });
5594
5692
  const limit = Math.min(Math.max(parseInt(c.req.query("limit") ?? "50", 10) || 50, 1), 200);
5595
5693
  const offset = Math.max(parseInt(c.req.query("offset") ?? "0", 10) || 0, 0);
5596
5694
  const db = await getDb();
5597
- const conditions = [eq4(mindHistory.mind, name)];
5695
+ const conditions = [eq5(mindHistory.mind, name)];
5598
5696
  if (channel) {
5599
- conditions.push(eq4(mindHistory.channel, channel));
5697
+ conditions.push(eq5(mindHistory.channel, channel));
5600
5698
  }
5601
5699
  if (session) {
5602
- conditions.push(eq4(mindHistory.session, session));
5700
+ conditions.push(eq5(mindHistory.session, session));
5603
5701
  }
5604
5702
  const effectivePreset = full ? "all" : preset;
5605
5703
  if (!effectivePreset || effectivePreset === "summary") {
5606
- const sumConditions = [eq4(summaries.mind, name), eq4(summaries.period, "turn")];
5704
+ const sumConditions = [eq5(summaries.mind, name), eq5(summaries.period, "turn")];
5607
5705
  if (session) {
5608
- sumConditions.push(eq4(turns.session, session));
5706
+ sumConditions.push(eq5(turns.session, session));
5609
5707
  const sumRows2 = await db.select({
5610
5708
  id: summaries.id,
5611
5709
  mind: summaries.mind,
@@ -5615,7 +5713,7 @@ ${lines.join("\n")}` });
5615
5713
  metadata: summaries.metadata,
5616
5714
  created_at: summaries.created_at,
5617
5715
  session: turns.session
5618
- }).from(summaries).innerJoin(turns, eq4(turns.id, summaries.period_key)).where(and4(...sumConditions)).orderBy(desc3(summaries.created_at)).limit(limit).offset(offset);
5716
+ }).from(summaries).innerJoin(turns, eq5(turns.id, summaries.period_key)).where(and5(...sumConditions)).orderBy(desc4(summaries.created_at)).limit(limit).offset(offset);
5619
5717
  return c.json(
5620
5718
  sumRows2.map((r) => ({
5621
5719
  id: r.id,
@@ -5632,7 +5730,7 @@ ${lines.join("\n")}` });
5632
5730
  }))
5633
5731
  );
5634
5732
  }
5635
- const sumRows = await db.select().from(summaries).where(and4(...sumConditions)).orderBy(desc3(summaries.created_at)).limit(limit).offset(offset);
5733
+ const sumRows = await db.select().from(summaries).where(and5(...sumConditions)).orderBy(desc4(summaries.created_at)).limit(limit).offset(offset);
5636
5734
  return c.json(
5637
5735
  sumRows.map((r) => ({
5638
5736
  id: r.id,
@@ -5653,22 +5751,22 @@ ${lines.join("\n")}` });
5653
5751
  case "all":
5654
5752
  break;
5655
5753
  case "conversation":
5656
- conditions.push(sql2`${mindHistory.type} IN ('inbound','outbound','tool_use')`);
5754
+ conditions.push(sql3`${mindHistory.type} IN ('inbound','outbound','tool_use')`);
5657
5755
  break;
5658
5756
  case "detailed":
5659
5757
  conditions.push(
5660
- sql2`${mindHistory.type} IN ('inbound','outbound','tool_use','tool_result','text','thinking')`
5758
+ sql3`${mindHistory.type} IN ('inbound','outbound','tool_use','tool_result','text','thinking')`
5661
5759
  );
5662
5760
  break;
5663
5761
  }
5664
- const rows = await db.select().from(mindHistory).where(and4(...conditions)).orderBy(desc3(mindHistory.created_at)).limit(limit).offset(offset);
5762
+ const rows = await db.select().from(mindHistory).where(and5(...conditions)).orderBy(desc4(mindHistory.created_at)).limit(limit).offset(offset);
5665
5763
  return c.json(rows);
5666
5764
  });
5667
5765
  var minds_default = app13;
5668
5766
 
5669
5767
  // packages/daemon/src/web/api/prompts.ts
5670
5768
  import { zValidator as zValidator6 } from "@hono/zod-validator";
5671
- import { eq as eq5, sql as sql3 } from "drizzle-orm";
5769
+ import { eq as eq6, sql as sql4 } from "drizzle-orm";
5672
5770
  import { Hono as Hono16 } from "hono";
5673
5771
  import { z as z6 } from "zod";
5674
5772
  var app14 = new Hono16().get("/", async (c) => {
@@ -5701,9 +5799,9 @@ var app14 = new Hono16().get("/", async (c) => {
5701
5799
  }
5702
5800
  const { content } = c.req.valid("json");
5703
5801
  const db = await getDb();
5704
- await db.insert(systemPrompts).values({ key, content, updated_at: sql3`(datetime('now'))` }).onConflictDoUpdate({
5802
+ await db.insert(systemPrompts).values({ key, content, updated_at: sql4`(datetime('now'))` }).onConflictDoUpdate({
5705
5803
  target: systemPrompts.key,
5706
- set: { content, updated_at: sql3`(datetime('now'))` }
5804
+ set: { content, updated_at: sql4`(datetime('now'))` }
5707
5805
  });
5708
5806
  return c.json({ ok: true });
5709
5807
  }).delete("/:key", requireAdmin, async (c) => {
@@ -5712,7 +5810,7 @@ var app14 = new Hono16().get("/", async (c) => {
5712
5810
  return c.json({ error: "Unknown prompt key" }, 404);
5713
5811
  }
5714
5812
  const db = await getDb();
5715
- await db.delete(systemPrompts).where(eq5(systemPrompts.key, key));
5813
+ await db.delete(systemPrompts).where(eq6(systemPrompts.key, key));
5716
5814
  return c.json({ ok: true });
5717
5815
  });
5718
5816
  var prompts_default = app14;
@@ -5960,7 +6058,7 @@ var app15 = new Hono17().get("/:name/clock/status", requireSelf(), async (c) =>
5960
6058
  const body = await c.req.text();
5961
6059
  const message = `[webhook: ${event}] ${body}`;
5962
6060
  try {
5963
- const { sendSystemMessage } = await import("./system-chat-EQIH6JVL.js");
6061
+ const { sendSystemMessage } = await import("./system-chat-KMQIP7VH.js");
5964
6062
  await sendSystemMessage(name, message);
5965
6063
  return c.json({ ok: true });
5966
6064
  } catch (err) {
@@ -6014,7 +6112,7 @@ setup.get("/status", async (c) => {
6014
6112
  let hasAccount = false;
6015
6113
  if (hasSystem) {
6016
6114
  try {
6017
- const { listUsersByType: listUsersByType2 } = await import("./auth-EAASQPTN.js");
6115
+ const { listUsersByType: listUsersByType2 } = await import("./auth-3RIG6GKN.js");
6018
6116
  const brains = await listUsersByType2("brain");
6019
6117
  hasAccount = brains.length > 0;
6020
6118
  } catch (err) {
@@ -6225,7 +6323,7 @@ setup.post("/account", async (c) => {
6225
6323
  }
6226
6324
  }
6227
6325
  try {
6228
- const { createUser: createUser2, updateUserProfile: updateUserProfile2 } = await import("./auth-EAASQPTN.js");
6326
+ const { createUser: createUser2, updateUserProfile: updateUserProfile2 } = await import("./auth-3RIG6GKN.js");
6229
6327
  const user = await createUser2(body.username.trim(), body.password);
6230
6328
  if (body.displayName?.trim()) {
6231
6329
  await updateUserProfile2(user.id, { display_name: body.displayName.trim() });
@@ -6271,7 +6369,7 @@ setup.post("/models", async (c) => {
6271
6369
  return c.json({ error: "Spirit model is required" }, 400);
6272
6370
  }
6273
6371
  try {
6274
- const { setEnabledModels: setEnabledModels3, setUtilityModel: setUtilityModel2 } = await import("./ai-service-3GBOJGUA.js");
6372
+ const { setEnabledModels: setEnabledModels3, setUtilityModel: setUtilityModel2 } = await import("./ai-service-FBCCEYF5.js");
6275
6373
  setEnabledModels3(body.models);
6276
6374
  const config2 = readGlobalConfig();
6277
6375
  config2.spiritModel = body.spiritModel.trim();
@@ -6289,15 +6387,15 @@ setup.post("/complete", async (c) => {
6289
6387
  return c.json({ error: "Setup already complete" }, 400);
6290
6388
  }
6291
6389
  try {
6292
- const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-XLD6QXSS.js");
6293
- const { startSpiritFull } = await import("./mind-service-R2FM53KD.js");
6390
+ const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-JQQHUVWR.js");
6391
+ const { startSpiritFull } = await import("./mind-service-ZBVUMJ5H.js");
6294
6392
  await ensureSpiritProject();
6295
6393
  await syncSpiritTemplate();
6296
6394
  const warnings = [];
6297
6395
  const config2 = readGlobalConfig();
6298
6396
  if (config2.setup?.service && config2.setup?.type === "local") {
6299
6397
  try {
6300
- const { installUserService } = await import("./service-install-2HZPDHEY.js");
6398
+ const { installUserService } = await import("./service-install-SHRM6NHK.js");
6301
6399
  const installed = await installUserService(config2.port, config2.hostname);
6302
6400
  if (!installed) {
6303
6401
  warnings.push("Service installation is not supported on this platform.");
@@ -6325,8 +6423,8 @@ setup.post("/complete", async (c) => {
6325
6423
  }
6326
6424
  let spiritConversationId;
6327
6425
  try {
6328
- const { getOrCreateMindUser: getOrCreateMindUser2, listUsersByType: listUsersByType2 } = await import("./auth-EAASQPTN.js");
6329
- const { createConversation: createConversation2, findDMConversation: findDMConversation2 } = await import("./conversations-FSZCUUYO.js");
6426
+ const { getOrCreateMindUser: getOrCreateMindUser2, listUsersByType: listUsersByType2 } = await import("./auth-3RIG6GKN.js");
6427
+ const { createConversation: createConversation2, findDMConversation: findDMConversation2 } = await import("./conversations-67WWW25C.js");
6330
6428
  const spiritUser = await getOrCreateMindUser2("volute");
6331
6429
  const brains = await listUsersByType2("brain");
6332
6430
  const admin2 = brains.find((u) => u.role === "admin");
@@ -6348,8 +6446,8 @@ setup.post("/complete", async (c) => {
6348
6446
  logger_default.info("setup complete state", { spiritConversationId, spiritStarted });
6349
6447
  if (spiritConversationId && spiritStarted) {
6350
6448
  try {
6351
- const { deliverMessage: deliverMessage2 } = await import("./message-delivery-4ZWFDBAY.js");
6352
- const { listUsersByType: listUsers2 } = await import("./auth-EAASQPTN.js");
6449
+ const { deliverMessage: deliverMessage2 } = await import("./message-delivery-WJBZFALY.js");
6450
+ const { listUsersByType: listUsers2 } = await import("./auth-3RIG6GKN.js");
6353
6451
  const admins = await listUsers2("brain");
6354
6452
  const admin2 = admins.find((u) => u.role === "admin");
6355
6453
  const adminName = admin2?.display_name || admin2?.username || "the admin";
@@ -6668,7 +6766,7 @@ var app19 = new Hono22().use("*", authMiddleware).get("/", async (c) => {
6668
6766
  var conversations_default = app19;
6669
6767
 
6670
6768
  // packages/daemon/src/web/api/v1/events.ts
6671
- import { desc as desc4 } from "drizzle-orm";
6769
+ import { desc as desc5, eq as eq7 } from "drizzle-orm";
6672
6770
  import { Hono as Hono23 } from "hono";
6673
6771
  import { streamSSE as streamSSE4 } from "hono/streaming";
6674
6772
 
@@ -6711,16 +6809,23 @@ function bufferEvent(data) {
6711
6809
  function nextEventId() {
6712
6810
  return nextId++;
6713
6811
  }
6714
- function getEventsSince(sinceId, allowedConversationIds) {
6812
+ function getEventsSince(sinceId, allowedConversationIds, allowedActivityMind) {
6715
6813
  const now = Date.now();
6716
6814
  const startIdx = buffer.findIndex((e) => e.id > sinceId);
6717
6815
  if (startIdx === -1) return [];
6718
6816
  return buffer.slice(startIdx).filter((e) => {
6719
6817
  if (now - e.timestamp >= MAX_AGE_MS) return false;
6720
6818
  const data = e.data;
6721
- if (data.event === "snapshot") return false;
6722
- if (data.event === "conversation") return allowedConversationIds.has(data.conversationId);
6723
- return true;
6819
+ switch (data.event) {
6820
+ case "snapshot":
6821
+ return false;
6822
+ case "conversation":
6823
+ return allowedConversationIds.has(data.conversationId);
6824
+ case "activity":
6825
+ return allowedActivityMind === void 0 || data.mind === allowedActivityMind;
6826
+ default:
6827
+ return false;
6828
+ }
6724
6829
  });
6725
6830
  }
6726
6831
 
@@ -6729,6 +6834,8 @@ var app20 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
6729
6834
  const user = c.get("user");
6730
6835
  const since = c.req.query("since");
6731
6836
  const sinceId = since ? Number(since) : 0;
6837
+ const privileged = user.role === "admin" || user.role === "system";
6838
+ const activityMind = privileged ? void 0 : await getBaseName(user.username);
6732
6839
  return streamSSE4(c, async (stream) => {
6733
6840
  const cleanups = [];
6734
6841
  if (user.user_type === "brain") {
@@ -6751,7 +6858,7 @@ var app20 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
6751
6858
  }
6752
6859
  const allowedConversationIds = new Set(conversations2.map((c2) => c2.id));
6753
6860
  if (sinceId > 0) {
6754
- const missed = getEventsSince(sinceId, allowedConversationIds);
6861
+ const missed = getEventsSince(sinceId, allowedConversationIds, activityMind);
6755
6862
  for (const event of missed) {
6756
6863
  await stream.writeSSE({
6757
6864
  id: String(event.id),
@@ -6762,7 +6869,7 @@ var app20 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
6762
6869
  let recentActivity = [];
6763
6870
  try {
6764
6871
  const db = await getDb();
6765
- recentActivity = await db.select().from(activity).orderBy(desc4(activity.created_at)).limit(50);
6872
+ recentActivity = await db.select().from(activity).where(activityMind ? eq7(activity.mind, activityMind) : void 0).orderBy(desc5(activity.created_at)).limit(50);
6766
6873
  recentActivity = recentActivity.map((row) => ({
6767
6874
  ...row,
6768
6875
  metadata: row.metadata ? JSON.parse(row.metadata) : null
@@ -6789,6 +6896,7 @@ var app20 = new Hono23().use("*", authMiddleware).get("/", async (c) => {
6789
6896
  metadata: event.metadata ?? null
6790
6897
  };
6791
6898
  const eventId = bufferEvent(data);
6899
+ if (activityMind !== void 0 && event.mind !== activityMind) return;
6792
6900
  stream.writeSSE({
6793
6901
  id: String(eventId),
6794
6902
  data: JSON.stringify(data)
@@ -7014,7 +7122,7 @@ var app21 = new Hono24().get("/:name/variants", async (c) => {
7014
7122
  const msg = e instanceof Error ? e.message : String(e);
7015
7123
  return c.json({ error: `Failed to create worktree: ${msg}` }, 500);
7016
7124
  }
7017
- chownMindDir(projectRoot, mindName);
7125
+ await chownMindDir(projectRoot, mindName);
7018
7126
  try {
7019
7127
  if (isIsolationEnabled()) {
7020
7128
  const [cmd, args] = await wrapForIsolation("npm", ["install"], mindName);
@@ -7130,7 +7238,7 @@ var app21 = new Hono24().get("/:name/variants", async (c) => {
7130
7238
  if (variantName.endsWith("-upgrade") || variantName === "upgrade") {
7131
7239
  try {
7132
7240
  const { computeTemplateHash: computeTemplateHash2 } = await import("./template-hash-446HHH5V.js");
7133
- const { setMindTemplateHash: setMindTemplateHash2 } = await import("./registry-MDLNPTTI.js");
7241
+ const { setMindTemplateHash: setMindTemplateHash2 } = await import("./registry-7LQHTRUX.js");
7134
7242
  const tmpl = parentEntry.template ?? "claude";
7135
7243
  await setMindTemplateHash2(mindName, computeTemplateHash2(tmpl));
7136
7244
  } catch (err) {
@@ -7304,47 +7412,6 @@ import { zValidator as zValidator10 } from "@hono/zod-validator";
7304
7412
  import { Hono as Hono26 } from "hono";
7305
7413
  import { streamSSE as streamSSE5 } from "hono/streaming";
7306
7414
  import { z as z10 } from "zod";
7307
- async function fanOutToMinds(opts) {
7308
- const participants = opts.participants;
7309
- const mindParticipants = participants.filter(
7310
- (p) => p.userType === "mind" || p.userType === "system"
7311
- );
7312
- const participantNames = participants.map((p) => p.username);
7313
- const isDM = opts.isDM ?? participants.length === 2;
7314
- const manager = getMindManager();
7315
- const sm = getSleepManagerIfReady();
7316
- const targetMinds = mindParticipants.map((ap) => {
7317
- const key = opts.targetName ? opts.targetName(ap.username) : ap.username;
7318
- if (manager.isRunning(key) || sm?.isSleeping(ap.username)) return ap.username;
7319
- return null;
7320
- }).filter((n) => n !== null && n !== opts.senderName);
7321
- function slugForMind(mindUsername) {
7322
- return buildVoluteSlug({
7323
- participants,
7324
- mindUsername,
7325
- conversationId: opts.conversationId,
7326
- ...opts.slugExtra
7327
- });
7328
- }
7329
- for (const mindName of targetMinds) {
7330
- const target = opts.targetName ? opts.targetName(mindName) : mindName;
7331
- const channel = slugForMind(mindName);
7332
- const typingMap = getTypingMap();
7333
- const currentlyTyping = typingMap.get(channel).filter((name) => participantNames.includes(name));
7334
- deliverMessage(target, {
7335
- content: opts.contentBlocks,
7336
- channel,
7337
- conversationId: opts.conversationId,
7338
- sender: opts.senderName,
7339
- participants: participantNames,
7340
- participantCount: participants.length,
7341
- isDM,
7342
- ...currentlyTyping.length > 0 ? { typing: currentlyTyping } : {}
7343
- }).catch((err) => {
7344
- logger_default.warn(`fan-out delivery failed for ${target}`, logger_default.errorData(err));
7345
- });
7346
- }
7347
- }
7348
7415
  var fileSchema = z10.object({
7349
7416
  filename: z10.string(),
7350
7417
  data: z10.string()
@@ -7506,7 +7573,15 @@ var unifiedChatApp = new Hono26().post(
7506
7573
  logger_default.warn("failed to look up channel char_limit, skipping enforcement", logger_default.errorData(err));
7507
7574
  }
7508
7575
  }
7509
- const message = await addMessage(conversationId, "user", senderName, contentBlocks);
7576
+ let outboundTurnId;
7577
+ let senderBase;
7578
+ if (senderIsMind) {
7579
+ senderBase = await getBaseName(senderName);
7580
+ outboundTurnId = getActiveTurnId(senderBase, c.get("mindSession"));
7581
+ }
7582
+ const message = await addMessage(conversationId, "user", senderName, contentBlocks, {
7583
+ turnId: outboundTurnId
7584
+ });
7510
7585
  let outboundId;
7511
7586
  if (senderIsMind) {
7512
7587
  routeOutboundBridge(conversationId, senderName, contentBlocks).catch((err) => {
@@ -7519,10 +7594,22 @@ var unifiedChatApp = new Hono26().post(
7519
7594
  convType: conv.type,
7520
7595
  convName
7521
7596
  });
7597
+ const text = extractTextContent(contentBlocks);
7522
7598
  try {
7523
- outboundId = await recordOutbound(senderName, channel, extractTextContent(contentBlocks), {
7524
- messageId: message?.id != null ? String(message.id) : void 0
7599
+ outboundId = await recordOutbound(senderName, channel, text, {
7600
+ messageId: message?.id != null ? String(message.id) : void 0,
7601
+ turnId: outboundTurnId
7525
7602
  });
7603
+ if (outboundTurnId) {
7604
+ const mindKey = senderBase ?? senderName;
7605
+ publish2(mindKey, {
7606
+ mind: mindKey,
7607
+ type: "outbound",
7608
+ channel,
7609
+ content: text,
7610
+ turnId: outboundTurnId
7611
+ });
7612
+ }
7526
7613
  } catch (err) {
7527
7614
  logger_default.warn(`recordOutbound failed for ${senderName}`, logger_default.errorData(err));
7528
7615
  }
@@ -7532,6 +7619,7 @@ var unifiedChatApp = new Hono26().post(
7532
7619
  conversationId,
7533
7620
  contentBlocks,
7534
7621
  senderName,
7622
+ senderIsMind: !!senderIsMind,
7535
7623
  participants,
7536
7624
  isDM,
7537
7625
  slugExtra: { convType: conv.type, convName },
@@ -7920,19 +8008,21 @@ async function startDaemon(opts) {
7920
8008
  const DAEMON_TOKEN_PATH = resolve17(systemDir, "daemon-token");
7921
8009
  mkdirSync10(home, { recursive: true });
7922
8010
  ensureSystemDir();
7923
- const { migrateConfigSecrets, migrateSetupCompleted } = await import("./setup-IYOUPCIK.js");
8011
+ const { migrateConfigSecrets, migrateSetupCompleted } = await import("./setup-25MMJ3J4.js");
7924
8012
  migrateConfigSecrets();
7925
8013
  migrateSetupCompleted();
7926
- await (await import("./db-ZJJJYJBU.js")).getDb();
8014
+ const { migrateAiModelQualification } = await import("./ai-service-FBCCEYF5.js");
8015
+ migrateAiModelQualification();
8016
+ await (await import("./db-CZV62BOQ.js")).getDb();
7927
8017
  try {
7928
- const { eq: eq6, and: and5 } = await import("drizzle-orm");
7929
- const { users: users2 } = await import("./schema-HHYJDG7B.js");
7930
- const db = await (await import("./db-ZJJJYJBU.js")).getDb();
7931
- await db.update(users2).set({ role: "system" }).where(and5(eq6(users2.user_type, "system"), eq6(users2.role, "user")));
8018
+ const { eq: eq8, and: and6 } = await import("drizzle-orm");
8019
+ const { users: users2 } = await import("./schema-LRUTFOER.js");
8020
+ const db = await (await import("./db-CZV62BOQ.js")).getDb();
8021
+ await db.update(users2).set({ role: "system" }).where(and6(eq8(users2.user_type, "system"), eq8(users2.role, "user")));
7932
8022
  } catch (err) {
7933
8023
  logger_default.warn("failed to migrate system user role", logger_default.errorData(err));
7934
8024
  }
7935
- const { initSandbox } = await import("./sandbox-E7RWZDVG.js");
8025
+ const { initSandbox } = await import("./sandbox-SJH3XOPZ.js");
7936
8026
  await initSandbox();
7937
8027
  try {
7938
8028
  await syncBuiltinSkills();
@@ -7959,7 +8049,7 @@ async function startDaemon(opts) {
7959
8049
  logger_default.warn("failed to ensure #system channel", logger_default.errorData(err));
7960
8050
  }
7961
8051
  try {
7962
- const { getOrCreateSystemUser: getOrCreateSystemUser2 } = await import("./auth-EAASQPTN.js");
8052
+ const { getOrCreateSystemUser: getOrCreateSystemUser2 } = await import("./auth-3RIG6GKN.js");
7963
8053
  await getOrCreateSystemUser2();
7964
8054
  } catch (err) {
7965
8055
  logger_default.warn(
@@ -7967,13 +8057,13 @@ async function startDaemon(opts) {
7967
8057
  logger_default.errorData(err)
7968
8058
  );
7969
8059
  }
7970
- const token = process.env.VOLUTE_DAEMON_TOKEN || randomBytes(32).toString("hex");
7971
- const { readGlobalConfig: readGlobalConfig2 } = await import("./setup-IYOUPCIK.js");
8060
+ const token = process.env.VOLUTE_DAEMON_TOKEN || randomBytes2(32).toString("hex");
8061
+ const { readGlobalConfig: readGlobalConfig2 } = await import("./setup-25MMJ3J4.js");
7972
8062
  const globalCfg = readGlobalConfig2();
7973
8063
  let tls;
7974
8064
  if (opts.tailscale || globalCfg.tailscale) {
7975
8065
  try {
7976
- const { getTailscaleTls } = await import("./tailscale-TIRTGM6E.js");
8066
+ const { getTailscaleTls } = await import("./tailscale-Q7ZJL7S7.js");
7977
8067
  const tlsConfig = await getTailscaleTls();
7978
8068
  tls = { key: tlsConfig.key, cert: tlsConfig.cert };
7979
8069
  logger_default.info("Tailscale HTTPS enabled", { hostname: tlsConfig.hostname });
@@ -8051,10 +8141,10 @@ async function startDaemon(opts) {
8051
8141
  await Promise.all(workers);
8052
8142
  }
8053
8143
  try {
8054
- const { isSetupComplete: isSetupComplete2 } = await import("./setup-IYOUPCIK.js");
8144
+ const { isSetupComplete: isSetupComplete2 } = await import("./setup-25MMJ3J4.js");
8055
8145
  if (isSetupComplete2()) {
8056
- const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-XLD6QXSS.js");
8057
- const { startSpiritFull } = await import("./mind-service-R2FM53KD.js");
8146
+ const { ensureSpiritProject, syncSpiritTemplate } = await import("./spirit-JQQHUVWR.js");
8147
+ const { startSpiritFull } = await import("./mind-service-ZBVUMJ5H.js");
8058
8148
  await ensureSpiritProject();
8059
8149
  await syncSpiritTemplate();
8060
8150
  const spiritEntry = await findMind("volute");
@@ -8071,7 +8161,7 @@ async function startDaemon(opts) {
8071
8161
  bridgeManager.startBridges(daemonPort).catch((err) => {
8072
8162
  logger_default.warn("failed to start bridges", logger_default.errorData(err));
8073
8163
  });
8074
- import("./cloud-sync-E3ZL5SZ4.js").then(
8164
+ import("./cloud-sync-FRYNWKCR.js").then(
8075
8165
  ({ consumeQueuedMessages }) => consumeQueuedMessages().catch((err) => {
8076
8166
  logger_default.warn("failed to consume queued cloud messages", logger_default.errorData(err));
8077
8167
  })
@@ -8079,7 +8169,7 @@ async function startDaemon(opts) {
8079
8169
  logger_default.warn("failed to load cloud-sync module", logger_default.errorData(err));
8080
8170
  });
8081
8171
  try {
8082
- const { backfillTemplateHashes, notifyVersionUpdate } = await import("./version-notify-P7UAR7HZ.js");
8172
+ const { backfillTemplateHashes, notifyVersionUpdate } = await import("./version-notify-DCAIXXZB.js");
8083
8173
  backfillTemplateHashes();
8084
8174
  notifyVersionUpdate().catch((err) => {
8085
8175
  logger_default.warn("failed to send version update notifications", logger_default.errorData(err));
@@ -8090,6 +8180,7 @@ async function startDaemon(opts) {
8090
8180
  delivery.restoreFromDb().catch((err) => {
8091
8181
  logger_default.warn("failed to restore delivery queue", logger_default.errorData(err));
8092
8182
  });
8183
+ delivery.startRedrive();
8093
8184
  cleanExpiredSessions().catch((err) => {
8094
8185
  logger_default.warn("failed to clean expired sessions", logger_default.errorData(err));
8095
8186
  });