@rubytech/create-maxy 1.0.877 → 1.0.878

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 (37) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/graph-trash/dist/index.js +1 -1
  3. package/payload/platform/lib/graph-trash/dist/index.js.map +1 -1
  4. package/payload/platform/lib/graph-trash/src/index.ts +1 -1
  5. package/payload/platform/plugins/admin/hooks/__tests__/pre-tool-use-base64-guard.test.sh +204 -0
  6. package/payload/platform/plugins/admin/hooks/pre-tool-use.sh +96 -0
  7. package/payload/platform/plugins/docs/references/platform.md +3 -1
  8. package/payload/platform/templates/agents/admin/IDENTITY.md +4 -0
  9. package/payload/server/chunk-INI2ED6U.js +2277 -0
  10. package/payload/server/chunk-JTZYXIUW.js +1373 -0
  11. package/payload/server/chunk-LQDUG4II.js +11336 -0
  12. package/payload/server/chunk-RP25NRQY.js +660 -0
  13. package/payload/server/client-pool-AIZ5QKFD.js +34 -0
  14. package/payload/server/cloudflare-task-tracker-B6FXP3HI.js +20 -0
  15. package/payload/server/maxy-edge.js +3 -3
  16. package/payload/server/public/assets/{Checkbox-m3yLBLrp.js → Checkbox-CqsIsmEi.js} +1 -1
  17. package/payload/server/public/assets/admin-CZlNLb9T.js +352 -0
  18. package/payload/server/public/assets/data-CH-nQ7oX.js +1 -0
  19. package/payload/server/public/assets/graph-labels-D0qUVHtZ.js +1 -0
  20. package/payload/server/public/assets/graph-mpWDe4rf.js +1 -0
  21. package/payload/server/public/assets/{jsx-runtime-DJwgVAMg.css → jsx-runtime-Cy_HdZWV.css} +1 -1
  22. package/payload/server/public/assets/page-CnyySOZF.js +1 -0
  23. package/payload/server/public/assets/{page-BLRjaAoU.js → page-DcK36vDf.js} +2 -2
  24. package/payload/server/public/assets/public-SXA00FTv.js +5 -0
  25. package/payload/server/public/assets/{useVoiceRecorder-JwwBC5pd.js → useVoiceRecorder-DcByEBLy.js} +1 -1
  26. package/payload/server/public/data.html +5 -5
  27. package/payload/server/public/graph.html +6 -6
  28. package/payload/server/public/index.html +8 -8
  29. package/payload/server/public/public.html +5 -5
  30. package/payload/server/server.js +520 -438
  31. package/payload/server/public/assets/admin-DEm0CCga.js +0 -352
  32. package/payload/server/public/assets/data-BkbjVYwP.js +0 -1
  33. package/payload/server/public/assets/graph-Cic-rDfg.js +0 -1
  34. package/payload/server/public/assets/graph-labels-C13OVh5P.js +0 -1
  35. package/payload/server/public/assets/page-p-Fj8Guk.js +0 -1
  36. package/payload/server/public/assets/public-4udeVi_T.js +0 -5
  37. /package/payload/server/public/assets/{jsx-runtime-Bd3TJ8Bg.js → jsx-runtime-BEjEWeaF.js} +0 -0
@@ -75,7 +75,7 @@ import {
75
75
  vncLog,
76
76
  waitForExit,
77
77
  writeChromiumWrapper
78
- } from "./chunk-I4AQMEJA.js";
78
+ } from "./chunk-LQDUG4II.js";
79
79
  import {
80
80
  CDP_PORT,
81
81
  COMMERCIAL_MODE,
@@ -102,7 +102,7 @@ import {
102
102
  getVisitorIdForSession,
103
103
  interruptClient,
104
104
  listAdminSessionsInProgress,
105
- mintAdminSessionKey,
105
+ mintAdminSessionToken,
106
106
  preConversationLogStream,
107
107
  registerGrantSession,
108
108
  registerResumedSession,
@@ -114,7 +114,7 @@ import {
114
114
  sigtermFlushStreamLogs,
115
115
  unregisterSession,
116
116
  validateSession
117
- } from "./chunk-GOZP57CX.js";
117
+ } from "./chunk-JTZYXIUW.js";
118
118
  import {
119
119
  CLOUDFLARE_TASK_DIAGNOSTICS,
120
120
  appendCloudflareSteps,
@@ -122,10 +122,11 @@ import {
122
122
  openCloudflareTask,
123
123
  readTunnelState,
124
124
  resolveUnitGoneVerdict
125
- } from "./chunk-RRVBWC66.js";
125
+ } from "./chunk-RP25NRQY.js";
126
126
  import {
127
127
  GREETING_DIRECTIVE,
128
128
  HAIKU_MODEL,
129
+ backfillConversationChannelAddress,
129
130
  backfillNullUserIdConversations,
130
131
  bindVisitorToGroup,
131
132
  checkGroupMembership,
@@ -154,8 +155,9 @@ import {
154
155
  runAdminUserSelfHeal,
155
156
  verifyAndGetConversationUpdatedAt,
156
157
  verifyConversationOwnership,
157
- writeAdminUserAndPerson
158
- } from "./chunk-LU6TUP3E.js";
158
+ writeAdminUserAndPerson,
159
+ writeTurnFailure
160
+ } from "./chunk-INI2ED6U.js";
159
161
  import {
160
162
  __commonJS,
161
163
  __toESM
@@ -180,7 +182,7 @@ var require_dist = __commonJS({
180
182
  Event: ["eventId"],
181
183
  KnowledgeDocument: ["attachmentId"],
182
184
  DigitalDocument: ["attachmentId"],
183
- Conversation: ["conversationId", "sessionKey"],
185
+ Conversation: ["conversationId"],
184
186
  Message: ["messageId"],
185
187
  OnboardingState: ["accountId"],
186
188
  Workflow: ["workflowId"],
@@ -2087,22 +2089,22 @@ async function persistWhatsAppMessage(input) {
2087
2089
  const messageId = `whatsapp-live:${input.accountId}:${input.remoteJid}:${input.msgKeyId}`;
2088
2090
  const senderTelephone = input.fromMe && input.selfPhone ? input.selfPhone : input.senderPhone;
2089
2091
  const dateSentIso = new Date(input.timestamp * 1e3).toISOString();
2090
- const scope = deriveScope(input.sessionKey);
2092
+ const scope = deriveScope(input.cacheKey);
2091
2093
  const { givenName, familyName } = splitName(input.pushName);
2092
- const prev = sessionWriteLocks.get(input.sessionKey);
2094
+ const prev = sessionWriteLocks.get(input.cacheKey);
2093
2095
  let release;
2094
2096
  const mine = new Promise((resolve22) => {
2095
2097
  release = resolve22;
2096
2098
  });
2097
2099
  const chained = (prev ?? Promise.resolve()).then(() => mine);
2098
- sessionWriteLocks.set(input.sessionKey, chained);
2100
+ sessionWriteLocks.set(input.cacheKey, chained);
2099
2101
  await prev;
2100
2102
  const t0 = Date.now();
2101
2103
  let session = null;
2102
2104
  try {
2103
2105
  session = getSession();
2104
2106
  const cypher = `
2105
- MATCH (c:Conversation {sessionKey: $sessionKey})
2107
+ MATCH (c:Conversation {conversationId: $conversationId})
2106
2108
  OPTIONAL MATCH (existingS:Person {telephone: $senderTelephone})
2107
2109
  OPTIONAL MATCH (existingM:Message {messageId: $messageId})
2108
2110
  WITH c,
@@ -2166,7 +2168,7 @@ async function persistWhatsAppMessage(input) {
2166
2168
  senderReused AS senderReused
2167
2169
  `;
2168
2170
  const params = {
2169
- sessionKey: input.sessionKey,
2171
+ conversationId: input.conversationId,
2170
2172
  messageId,
2171
2173
  platformAccountId: input.platformAccountId,
2172
2174
  senderTelephone,
@@ -2185,7 +2187,7 @@ async function persistWhatsAppMessage(input) {
2185
2187
  const result = await session.run(cypher, params);
2186
2188
  const ms = Date.now() - t0;
2187
2189
  if (result.records.length === 0) {
2188
- console.error(`${TAG7} skip reason=conversation-not-found accountId=${input.platformAccountId} sessionKey=${input.sessionKey} messageId=${messageId}`);
2190
+ console.error(`${TAG7} skip reason=conversation-not-found accountId=${input.platformAccountId} cacheKey=${input.cacheKey} messageId=${messageId}`);
2189
2191
  return null;
2190
2192
  }
2191
2193
  const rec = result.records[0];
@@ -2206,7 +2208,7 @@ async function persistWhatsAppMessage(input) {
2206
2208
  console.error(`${TAG7} next-skip reason=no-prior msgId=${messageId}`);
2207
2209
  }
2208
2210
  console.error(
2209
- `${TAG7} write messageId=${messageId} accountId=${input.platformAccountId} sessionKey=${input.sessionKey} fromMe=${input.fromMe} dateSent=${dateSentIso} bodyBytes=${Buffer.byteLength(input.body, "utf8")} ms=${ms}`
2211
+ `${TAG7} write messageId=${messageId} accountId=${input.platformAccountId} cacheKey=${input.cacheKey} fromMe=${input.fromMe} dateSent=${dateSentIso} bodyBytes=${Buffer.byteLength(input.body, "utf8")} ms=${ms}`
2210
2212
  );
2211
2213
  return { messageId, created: true, senderElementId };
2212
2214
  } catch (err) {
@@ -2218,14 +2220,14 @@ async function persistWhatsAppMessage(input) {
2218
2220
  return null;
2219
2221
  } finally {
2220
2222
  release();
2221
- if (sessionWriteLocks.get(input.sessionKey) === chained) {
2222
- sessionWriteLocks.delete(input.sessionKey);
2223
+ if (sessionWriteLocks.get(input.cacheKey) === chained) {
2224
+ sessionWriteLocks.delete(input.cacheKey);
2223
2225
  }
2224
2226
  if (session) await session.close();
2225
2227
  }
2226
2228
  }
2227
- function deriveScope(sessionKey) {
2228
- const segments = sessionKey.split(":");
2229
+ function deriveScope(cacheKey) {
2230
+ const segments = cacheKey.split(":");
2229
2231
  return segments.length <= 2 ? "admin" : "public";
2230
2232
  }
2231
2233
  function splitName(pushName) {
@@ -2253,24 +2255,24 @@ async function ensureWhatsAppConversation(input) {
2253
2255
  const result = await ensureConversation(
2254
2256
  input.platformAccountId,
2255
2257
  input.agentType,
2256
- input.sessionKey
2258
+ input.cacheKey
2257
2259
  );
2258
2260
  const ms = Date.now() - t0;
2259
2261
  if (!result.conversationId) {
2260
2262
  console.error(
2261
- `${TAG8} conversation-merged FAIL sessionKey=${input.sessionKey} reason=null-conversationId ms=${ms}`
2263
+ `${TAG8} conversation-merged FAIL cacheKey=${input.cacheKey} reason=null-conversationId ms=${ms}`
2262
2264
  );
2263
2265
  return null;
2264
2266
  }
2265
2267
  console.error(
2266
- `${TAG8} conversation-merged sessionKey=${input.sessionKey} agentType=${input.agentType} channel=whatsapp created=${result.created} ms=${ms}`
2268
+ `${TAG8} conversation-merged cacheKey=${input.cacheKey} agentType=${input.agentType} channel=whatsapp created=${result.created} ms=${ms}`
2267
2269
  );
2268
2270
  return { conversationId: result.conversationId, created: result.created };
2269
2271
  } catch (err) {
2270
2272
  const ms = Date.now() - t0;
2271
2273
  const reason = err instanceof Error ? `${err.name}:${err.message.slice(0, 200)}` : String(err).slice(0, 200);
2272
2274
  console.error(
2273
- `${TAG8} conversation-merged FAIL sessionKey=${input.sessionKey} reason=${reason} ms=${ms}`
2275
+ `${TAG8} conversation-merged FAIL cacheKey=${input.cacheKey} reason=${reason} ms=${ms}`
2274
2276
  );
2275
2277
  return null;
2276
2278
  }
@@ -2781,14 +2783,14 @@ function storeMessage(storeKey, entry) {
2781
2783
  console.error(`${TAG13} message store trimmed for ${storeKey}: ${trimmed} oldest messages removed`);
2782
2784
  }
2783
2785
  }
2784
- function deriveSessionKey(input) {
2786
+ function deriveCacheKey(input) {
2785
2787
  if (input.isOwnerMirror || input.agentType === "admin") {
2786
2788
  return `whatsapp:${input.accountId}`;
2787
2789
  }
2788
2790
  if (input.isGroup) {
2789
2791
  if (!input.groupJid) {
2790
2792
  throw new Error(
2791
- `deriveSessionKey: isGroup=true requires groupJid (accountId=${input.accountId}, senderPhone=${input.senderPhone})`
2793
+ `deriveCacheKey: isGroup=true requires groupJid (accountId=${input.accountId}, senderPhone=${input.senderPhone})`
2792
2794
  );
2793
2795
  }
2794
2796
  return `whatsapp:${input.accountId}:group:${input.groupJid}`;
@@ -3267,14 +3269,14 @@ function monitorInbound(conn) {
3267
3269
  jid: remoteJid
3268
3270
  });
3269
3271
  const fromMe = Boolean(msg.key.fromMe);
3270
- const sessionKeyAgentType = isGroup ? "public" : fromMe ? "admin" : checkDmAccess({
3272
+ const cacheKeyAgentType = isGroup ? "public" : fromMe ? "admin" : checkDmAccess({
3271
3273
  senderPhone,
3272
3274
  selfPhone: conn.selfPhone ?? "",
3273
3275
  config: whatsAppConfig,
3274
3276
  accountConfig: whatsAppConfig.accounts?.[conn.accountId]
3275
3277
  }).agentType;
3276
- const sessionKey = deriveSessionKey({
3277
- agentType: sessionKeyAgentType,
3278
+ const cacheKey = deriveCacheKey({
3279
+ agentType: cacheKeyAgentType,
3278
3280
  accountId: conn.accountId,
3279
3281
  senderPhone,
3280
3282
  isGroup,
@@ -3285,8 +3287,8 @@ function monitorInbound(conn) {
3285
3287
  const merged = await ensureWhatsAppConversation({
3286
3288
  accountId: conn.accountId,
3287
3289
  platformAccountId: conn.platformAccountId,
3288
- sessionKey,
3289
- agentType: sessionKeyAgentType,
3290
+ cacheKey,
3291
+ agentType: cacheKeyAgentType,
3290
3292
  groupJid: isGroup ? remoteJid : void 0
3291
3293
  });
3292
3294
  if (merged) {
@@ -3294,7 +3296,8 @@ function monitorInbound(conn) {
3294
3296
  accountId: conn.accountId,
3295
3297
  platformAccountId: conn.platformAccountId,
3296
3298
  remoteJid,
3297
- sessionKey,
3299
+ cacheKey,
3300
+ conversationId: merged.conversationId,
3298
3301
  msgKeyId: msg.key.id,
3299
3302
  fromMe,
3300
3303
  senderPhone,
@@ -3376,7 +3379,7 @@ async function handleInboundMessage(conn, msg) {
3376
3379
  isGroup: isGroup2,
3377
3380
  groupJid: isGroup2 ? remoteJid : void 0,
3378
3381
  reply: reply2,
3379
- sessionKey: deriveSessionKey({
3382
+ cacheKey: deriveCacheKey({
3380
3383
  agentType: "admin",
3381
3384
  accountId: conn.accountId,
3382
3385
  senderPhone: senderPhone2,
@@ -3470,7 +3473,7 @@ async function handleInboundMessage(conn, msg) {
3470
3473
  if (!currentSock) throw new Error("WhatsApp disconnected \u2014 cannot reply");
3471
3474
  await sendTextMessage(currentSock, remoteJid, text, { accountId: conn.accountId });
3472
3475
  };
3473
- const sessionKey = deriveSessionKey({
3476
+ const cacheKey = deriveCacheKey({
3474
3477
  agentType: accessResult.agentType,
3475
3478
  accountId: conn.accountId,
3476
3479
  senderPhone,
@@ -3487,7 +3490,7 @@ async function handleInboundMessage(conn, msg) {
3487
3490
  groupJid: isGroup ? remoteJid : void 0,
3488
3491
  groupSubject,
3489
3492
  reply,
3490
- sessionKey,
3493
+ cacheKey,
3491
3494
  mediaPath: mediaResult?.path,
3492
3495
  mediaMimetype: mediaResult?.mimetype,
3493
3496
  mediaType: extracted.mediaType,
@@ -3832,9 +3835,9 @@ app2.post("/", async (c) => {
3832
3835
  ...m.role === "user" ? { senderName: m.senderName } : {}
3833
3836
  });
3834
3837
  }
3835
- const sessionKey2 = crypto.randomUUID();
3836
- registerResumedSession(sessionKey2, accountId, agentSlug, groupInfo.conversationId, agentMessages);
3837
- setGroupContextForSession(sessionKey2, {
3838
+ const cacheKey2 = crypto.randomUUID();
3839
+ registerResumedSession(cacheKey2, accountId, agentSlug, groupInfo.conversationId, agentMessages);
3840
+ setGroupContextForSession(cacheKey2, {
3838
3841
  groupSlug,
3839
3842
  groupName: groupInfo.groupName,
3840
3843
  conversationId: groupInfo.conversationId,
@@ -3846,7 +3849,7 @@ app2.post("/", async (c) => {
3846
3849
  console.log(`[session] cold-resume group=${groupSlug} visitor=${visitorId.slice(0, 8)}\u2026 messages=${uiMessages.length}`);
3847
3850
  return withVisitorCookie(
3848
3851
  Response.json({
3849
- session_key: sessionKey2,
3852
+ session_key: cacheKey2,
3850
3853
  agent_id: agentSlug,
3851
3854
  resumed,
3852
3855
  ...resumed ? { messages: uiMessages } : {},
@@ -3889,13 +3892,13 @@ app2.post("/", async (c) => {
3889
3892
  content: m.content,
3890
3893
  timestamp: new Date(m.createdAt).getTime() || Date.now()
3891
3894
  }));
3892
- const sessionKey2 = crypto.randomUUID();
3893
- registerResumedSession(sessionKey2, accountId, agentSlug, recent.conversationId, agentMessages);
3895
+ const cacheKey2 = crypto.randomUUID();
3896
+ registerResumedSession(cacheKey2, accountId, agentSlug, recent.conversationId, agentMessages);
3894
3897
  const resumed = uiMessages.length > 0;
3895
3898
  console.log(`[session] cold-resume visitor=${visitorId.slice(0, 8)}\u2026 conversation=${recent.conversationId.slice(0, 8)}\u2026 messages=${uiMessages.length}`);
3896
3899
  return withVisitorCookie(
3897
3900
  Response.json({
3898
- session_key: sessionKey2,
3901
+ session_key: cacheKey2,
3899
3902
  agent_id: agentSlug,
3900
3903
  resumed,
3901
3904
  ...resumed ? { messages: uiMessages } : {},
@@ -3907,13 +3910,13 @@ app2.post("/", async (c) => {
3907
3910
  }
3908
3911
  }
3909
3912
  const newVisitorId = visitorId ?? crypto.randomUUID();
3910
- const sessionKey = crypto.randomUUID();
3911
- registerSession(sessionKey, "public", accountId, agentSlug);
3913
+ const cacheKey = crypto.randomUUID();
3914
+ registerSession(cacheKey, "public", accountId, agentSlug);
3912
3915
  const hasImage = agentConfig?.image ? "yes" : "no";
3913
- console.log(`[session] new-session visitor=${newVisitorId.slice(0, 8)}\u2026 session=${sessionKey.slice(0, 8)}\u2026 agent=${agentSlug} image=${hasImage} showAgentName=${agentConfig?.showAgentName ?? false}`);
3916
+ console.log(`[session] new-session visitor=${newVisitorId.slice(0, 8)}\u2026 session=${cacheKey.slice(0, 8)}\u2026 agent=${agentSlug} image=${hasImage} showAgentName=${agentConfig?.showAgentName ?? false}`);
3914
3917
  return withVisitorCookie(
3915
3918
  Response.json({
3916
- session_key: sessionKey,
3919
+ session_key: cacheKey,
3917
3920
  agent_id: agentSlug,
3918
3921
  ...branding ? { branding } : {},
3919
3922
  ...agentIdentity
@@ -4506,9 +4509,9 @@ var chat_default = app3;
4506
4509
  // server/routes/group.ts
4507
4510
  var app4 = new Hono();
4508
4511
  app4.get("/messages", async (c) => {
4509
- const sessionKey = c.req.query("session_key");
4512
+ const cacheKey = c.req.query("session_key");
4510
4513
  const since = c.req.query("since");
4511
- if (!sessionKey) {
4514
+ if (!cacheKey) {
4512
4515
  return c.json({ error: "session_key required" }, 400);
4513
4516
  }
4514
4517
  if (!since) {
@@ -4518,18 +4521,18 @@ app4.get("/messages", async (c) => {
4518
4521
  if (isNaN(sinceDate.getTime())) {
4519
4522
  return c.json({ error: "Invalid since parameter \u2014 expected ISO 8601" }, 400);
4520
4523
  }
4521
- if (!validateSession(sessionKey, "public").ok) {
4524
+ if (!validateSession(cacheKey, "public").ok) {
4522
4525
  return c.json({ error: "Session expired" }, 401);
4523
4526
  }
4524
- const groupSlug = getGroupSlugForSession(sessionKey);
4527
+ const groupSlug = getGroupSlugForSession(cacheKey);
4525
4528
  if (!groupSlug) {
4526
4529
  return c.json({ error: "Not a group session" }, 400);
4527
4530
  }
4528
- const conversationId = getConversationIdForSession(sessionKey);
4531
+ const conversationId = getConversationIdForSession(cacheKey);
4529
4532
  if (!conversationId) {
4530
4533
  return c.json({ error: "No conversation bound" }, 400);
4531
4534
  }
4532
- const visitorId = getVisitorIdForSession(sessionKey);
4535
+ const visitorId = getVisitorIdForSession(cacheKey);
4533
4536
  const POLL_LIMIT = 100;
4534
4537
  try {
4535
4538
  const messages = await getMessagesSince(conversationId, since, POLL_LIMIT);
@@ -5026,8 +5029,8 @@ app5.post("/verify-token", async (c) => {
5026
5029
  }
5027
5030
  await consumeMagicToken(result.grantId);
5028
5031
  const accountId = result.accountId;
5029
- const sessionKey = crypto.randomUUID();
5030
- registerGrantSession(sessionKey, accountId, agentSlug, {
5032
+ const cacheKey = crypto.randomUUID();
5033
+ registerGrantSession(cacheKey, accountId, agentSlug, {
5031
5034
  grantId: result.grantId,
5032
5035
  grantExpiresAt: result.expiresAt ?? void 0,
5033
5036
  grantStatus: result.status,
@@ -5038,7 +5041,7 @@ app5.post("/verify-token", async (c) => {
5038
5041
  clearAccessRateLimit(clientIp, agentSlug);
5039
5042
  console.error(`[access-gate] verify-token ip=${clientIp} agent=${agentSlug} contact=${maskContact(result.contactValue)} result=success`);
5040
5043
  return c.json({
5041
- session_key: sessionKey,
5044
+ session_key: cacheKey,
5042
5045
  grant: {
5043
5046
  displayName: result.displayName,
5044
5047
  contactValue: result.contactValue,
@@ -5106,8 +5109,8 @@ app5.post("/verify-otp", async (c) => {
5106
5109
  }, remaining > 0 ? 401 : 429);
5107
5110
  }
5108
5111
  await consumeOtp(grant.grantId);
5109
- const sessionKey = crypto.randomUUID();
5110
- registerGrantSession(sessionKey, account.accountId, agentSlug, {
5112
+ const cacheKey = crypto.randomUUID();
5113
+ registerGrantSession(cacheKey, account.accountId, agentSlug, {
5111
5114
  grantId: grant.grantId,
5112
5115
  grantExpiresAt: grant.expiresAt ?? void 0,
5113
5116
  grantStatus: grant.status,
@@ -5118,7 +5121,7 @@ app5.post("/verify-otp", async (c) => {
5118
5121
  clearAccessRateLimit(clientIp, agentSlug);
5119
5122
  console.error(`[access-gate] verify-otp ip=${clientIp} agent=${agentSlug} phone=${maskContact(phone)} result=success`);
5120
5123
  return c.json({
5121
- session_key: sessionKey,
5124
+ session_key: cacheKey,
5122
5125
  grant: {
5123
5126
  displayName: grant.displayName,
5124
5127
  contactValue: grant.contactValue,
@@ -5216,8 +5219,8 @@ app5.post("/login", async (c) => {
5216
5219
  console.error(`[access-gate] login ip=${clientIp} agent=${agentSlug} contact=${maskContact(contact)} result=wrong_password`);
5217
5220
  return c.json({ error: "Invalid credentials" }, 401);
5218
5221
  }
5219
- const sessionKey = crypto.randomUUID();
5220
- registerGrantSession(sessionKey, account.accountId, agentSlug, {
5222
+ const cacheKey = crypto.randomUUID();
5223
+ registerGrantSession(cacheKey, account.accountId, agentSlug, {
5221
5224
  grantId: grant.grantId,
5222
5225
  grantExpiresAt: grant.expiresAt ?? void 0,
5223
5226
  grantStatus: grant.status,
@@ -5227,7 +5230,7 @@ app5.post("/login", async (c) => {
5227
5230
  });
5228
5231
  clearAccessRateLimit(clientIp, agentSlug);
5229
5232
  console.error(`[access-gate] login ip=${clientIp} agent=${agentSlug} contact=${maskContact(contact)} result=success`);
5230
- return c.json({ session_key: sessionKey, agent_id: agentSlug });
5233
+ return c.json({ session_key: cacheKey, agent_id: agentSlug });
5231
5234
  } catch (err) {
5232
5235
  console.error(`[access-gate] login ip=${clientIp} agent=${agentSlug} error=${err instanceof Error ? err.message : String(err)}`);
5233
5236
  return c.json({ error: "Internal server error" }, 500);
@@ -5386,11 +5389,11 @@ function verifyWebhookSecret(headerValue, storedSecret) {
5386
5389
  }
5387
5390
  async function handleInbound(params) {
5388
5391
  const { chatId, senderId, text, botType, botToken, accountId, agentType } = params;
5389
- const sessionKey = `telegram:${chatId}`;
5392
+ const cacheKey = `telegram:${chatId}`;
5390
5393
  const gatewayChannel = agentType === "admin" ? "telegram-admin" : "telegram-dm";
5391
- registerSession(sessionKey, agentType, accountId);
5394
+ registerSession(cacheKey, agentType, accountId);
5392
5395
  console.error(
5393
- `${TAG16} session registered: sessionKey=${sessionKey} agentType=${agentType} botType=${botType} senderId=${senderId} accountId=${accountId.slice(0, 8)}\u2026`
5396
+ `${TAG16} session registered: cacheKey=${cacheKey} agentType=${agentType} botType=${botType} senderId=${senderId} accountId=${accountId.slice(0, 8)}\u2026`
5394
5397
  );
5395
5398
  const gatewayResult = await processInbound(text, gatewayChannel);
5396
5399
  if (gatewayResult.screening.verdict === "discard") {
@@ -5404,7 +5407,7 @@ async function handleInbound(params) {
5404
5407
  for await (const event of invokeAgent(
5405
5408
  { type: agentType },
5406
5409
  gatewayResult.processedText,
5407
- sessionKey,
5410
+ cacheKey,
5408
5411
  [],
5409
5412
  void 0,
5410
5413
  gatewayResult
@@ -6220,19 +6223,25 @@ app7.get("/messages", (c) => {
6220
6223
  });
6221
6224
  app7.get("/conversation-graph-state", async (c) => {
6222
6225
  try {
6223
- const directSessionKey = c.req.query("sessionKey");
6226
+ const directCacheKey = c.req.query("cacheKey");
6224
6227
  const jid = c.req.query("jid");
6225
6228
  const accountIdQuery = c.req.query("accountId");
6226
- let sessionKey;
6227
- if (directSessionKey) {
6228
- sessionKey = directSessionKey;
6229
+ let cacheKey;
6230
+ let accountId;
6231
+ if (directCacheKey) {
6232
+ cacheKey = directCacheKey;
6233
+ const m = directCacheKey.match(/^whatsapp:([^:]+)/);
6234
+ if (!m) {
6235
+ return c.json({ error: `cacheKey=${directCacheKey} not a recognised whatsapp cacheKey shape.` }, 400);
6236
+ }
6237
+ accountId = validateAccountId(m[1] ?? accountIdQuery ?? null);
6229
6238
  } else {
6230
6239
  if (!jid) {
6231
- return c.json({ error: "Provide either sessionKey or jid (with optional accountId)." }, 400);
6240
+ return c.json({ error: "Provide either cacheKey or jid (with optional accountId)." }, 400);
6232
6241
  }
6233
- const accountId = validateAccountId(accountIdQuery ?? null);
6242
+ accountId = validateAccountId(accountIdQuery ?? null);
6234
6243
  if (isGroupJid(jid)) {
6235
- sessionKey = deriveSessionKey({
6244
+ cacheKey = deriveCacheKey({
6236
6245
  agentType: "public",
6237
6246
  accountId,
6238
6247
  senderPhone: "",
@@ -6242,9 +6251,9 @@ app7.get("/conversation-graph-state", async (c) => {
6242
6251
  } else {
6243
6252
  const e164 = jid.replace(/^(\d+)(?::\d+)?@s\.whatsapp\.net$/i, "$1");
6244
6253
  if (!/^\d+$/.test(e164)) {
6245
- return c.json({ error: `Cannot derive sessionKey from jid=${jid} \u2014 not a recognised user JID. Pass sessionKey explicitly.` }, 400);
6254
+ return c.json({ error: `Cannot derive cacheKey from jid=${jid} \u2014 not a recognised user JID. Pass cacheKey explicitly.` }, 400);
6246
6255
  }
6247
- sessionKey = deriveSessionKey({
6256
+ cacheKey = deriveCacheKey({
6248
6257
  agentType: "public",
6249
6258
  accountId,
6250
6259
  senderPhone: e164,
@@ -6252,8 +6261,9 @@ app7.get("/conversation-graph-state", async (c) => {
6252
6261
  });
6253
6262
  }
6254
6263
  }
6264
+ const channelAddress = cacheKey.startsWith("whatsapp:") ? cacheKey.slice("whatsapp:".length) : cacheKey;
6255
6265
  const cypher = `
6256
- MATCH (c:Conversation {sessionKey: $sessionKey})
6266
+ MATCH (c:Conversation {accountId: $accountId, channel: 'whatsapp', channelAddress: $channelAddress})
6257
6267
  OPTIONAL MATCH (m:Message:WhatsAppMessage)-[:PART_OF]->(c)
6258
6268
  RETURN
6259
6269
  c.conversationId AS conversationId,
@@ -6270,7 +6280,7 @@ app7.get("/conversation-graph-state", async (c) => {
6270
6280
  const rows = [];
6271
6281
  try {
6272
6282
  session = getSession();
6273
- const result = await session.run(cypher, { sessionKey });
6283
+ const result = await session.run(cypher, { accountId, channelAddress });
6274
6284
  for (const rec of result.records) {
6275
6285
  if (conversationId === null) {
6276
6286
  conversationId = rec.get("conversationId") ?? null;
@@ -6288,13 +6298,13 @@ app7.get("/conversation-graph-state", async (c) => {
6288
6298
  }
6289
6299
  } catch (err) {
6290
6300
  const msg = err instanceof Error ? err.message : String(err);
6291
- console.error(`${TAG18} conversation-graph-state ERR sessionKey=${sessionKey} reason=${msg}`);
6292
- return c.json({ error: `Graph query failed: ${msg}`, sessionKey, cypher: cypher.trim() }, 500);
6301
+ console.error(`${TAG18} conversation-graph-state ERR cacheKey=${cacheKey} reason=${msg}`);
6302
+ return c.json({ error: `Graph query failed: ${msg}`, cacheKey, cypher: cypher.trim() }, 500);
6293
6303
  }
6294
6304
  const ms = Date.now() - t0;
6295
- console.error(`[mcp:whatsapp] tool=whatsapp-conversation-graph-state sessionKey=${sessionKey} graphRows=${rows.length} conversationId=${conversationId ?? "null"} ms=${ms}`);
6305
+ console.error(`[mcp:whatsapp] tool=whatsapp-conversation-graph-state cacheKey=${cacheKey} graphRows=${rows.length} conversationId=${conversationId ?? "null"} ms=${ms}`);
6296
6306
  return c.json({
6297
- sessionKey,
6307
+ cacheKey,
6298
6308
  conversationId,
6299
6309
  graphRows: rows,
6300
6310
  cypher: cypher.trim(),
@@ -7005,9 +7015,9 @@ async function resolveUserIdentity(accountId, userId) {
7005
7015
  async function createAdminSession(accountId, thinkingView, userId, userName, role, avatar) {
7006
7016
  const account = resolveAccount();
7007
7017
  const effectiveThinkingView = thinkingView ?? account?.config.thinkingView ?? "default";
7008
- const sessionKey = userId ? mintAdminSessionKey({ accountId, userId }) : crypto.randomUUID();
7009
- registerSession(sessionKey, "admin", accountId, void 0, userId, userName, role);
7010
- if (userId) setWantsPriorConversation(sessionKey);
7018
+ const cacheKey = userId ? mintAdminSessionToken({ accountId, userId }) : crypto.randomUUID();
7019
+ registerSession(cacheKey, "admin", accountId, void 0, userId, userName, role);
7020
+ if (userId) setWantsPriorConversation(cacheKey);
7011
7021
  let onboardingComplete = true;
7012
7022
  try {
7013
7023
  const step = await loadOnboardingStep(accountId);
@@ -7021,10 +7031,27 @@ async function createAdminSession(accountId, thinkingView, userId, userName, rol
7021
7031
  businessName = branding?.name || void 0;
7022
7032
  } catch {
7023
7033
  }
7024
- console.log(`[session] ${(/* @__PURE__ */ new Date()).toISOString()} admin session created: userId=${userId ?? "\u2013"} userName=${userName ?? "\u2013"} accountId=${accountId} conversationId=deferred sessionKey=${sessionKey.slice(0, 8)}`);
7025
- console.log(`[admin-session] role=${role ?? "null"} sessionKey=${sessionKey.slice(0, 8)} phase=create`);
7034
+ let initialConversationId = null;
7035
+ try {
7036
+ const ensured = await ensureConversation(
7037
+ accountId,
7038
+ "admin",
7039
+ cacheKey,
7040
+ void 0,
7041
+ void 0,
7042
+ userId
7043
+ );
7044
+ if (ensured.conversationId) {
7045
+ initialConversationId = ensured.conversationId;
7046
+ setConversationIdForSession(cacheKey, ensured.conversationId);
7047
+ }
7048
+ } catch (err) {
7049
+ console.error(`[session] eager-ensureConversation FAILED at boot: ${err instanceof Error ? err.message : String(err)}`);
7050
+ }
7051
+ console.log(`[session] ${(/* @__PURE__ */ new Date()).toISOString()} admin session created: userId=${userId ?? "\u2013"} userName=${userName ?? "\u2013"} accountId=${accountId} conversationId=${initialConversationId ?? "unbound"} cacheKey=${cacheKey.slice(0, 8)}`);
7052
+ console.log(`[admin-session] role=${role ?? "null"} cacheKey=${cacheKey.slice(0, 8)} phase=create eager-ensured=${initialConversationId ? "true" : "false"}`);
7026
7053
  return {
7027
- session_key: sessionKey,
7054
+ session_key: cacheKey,
7028
7055
  agent_id: "admin",
7029
7056
  userId,
7030
7057
  userName,
@@ -7033,16 +7060,16 @@ async function createAdminSession(accountId, thinkingView, userId, userName, rol
7033
7060
  thinkingView: effectiveThinkingView,
7034
7061
  onboardingComplete,
7035
7062
  businessName,
7036
- conversationId: null
7063
+ conversationId: initialConversationId
7037
7064
  };
7038
7065
  }
7039
7066
  var app10 = new Hono();
7040
7067
  app10.get("/", async (c) => {
7041
- const sessionKey = c.req.query("session_key");
7042
- if (!sessionKey || !validateSession(sessionKey, "admin").ok) {
7068
+ const cacheKey = c.req.query("session_key");
7069
+ if (!cacheKey || !validateSession(cacheKey, "admin").ok) {
7043
7070
  return c.json({ error: "Invalid or expired admin session" }, 401);
7044
7071
  }
7045
- const accountId = getAccountIdForSession(sessionKey);
7072
+ const accountId = getAccountIdForSession(cacheKey);
7046
7073
  if (!accountId) {
7047
7074
  return c.json({ error: "Session has no account binding" }, 401);
7048
7075
  }
@@ -7061,21 +7088,21 @@ app10.get("/", async (c) => {
7061
7088
  businessName = branding?.name || void 0;
7062
7089
  } catch {
7063
7090
  }
7064
- const role = getRoleForSession(sessionKey);
7065
- console.log(`[admin-session] role=${role ?? "null"} sessionKey=${sessionKey.slice(0, 8)} phase=restore`);
7066
- const restoredUserId = getUserIdForSession(sessionKey);
7067
- let restoredUserName = getUserNameForSession(sessionKey);
7091
+ const role = getRoleForSession(cacheKey);
7092
+ console.log(`[admin-session] role=${role ?? "null"} cacheKey=${cacheKey.slice(0, 8)} phase=restore`);
7093
+ const restoredUserId = getUserIdForSession(cacheKey);
7094
+ let restoredUserName = getUserNameForSession(cacheKey);
7068
7095
  let restoredAvatar = null;
7069
7096
  if (restoredUserId) {
7070
7097
  const resolved = await resolveUserIdentity(accountId, restoredUserId);
7071
7098
  restoredUserName = resolved.userName;
7072
7099
  restoredAvatar = resolved.avatar;
7073
7100
  if (resolved.userName !== void 0) {
7074
- registerSession(sessionKey, "admin", accountId, void 0, restoredUserId, resolved.userName, role ?? void 0);
7101
+ registerSession(cacheKey, "admin", accountId, void 0, restoredUserId, resolved.userName, role ?? void 0);
7075
7102
  }
7076
7103
  }
7077
7104
  return c.json({
7078
- session_key: sessionKey,
7105
+ session_key: cacheKey,
7079
7106
  agent_id: "admin",
7080
7107
  userId: restoredUserId,
7081
7108
  userName: restoredUserName,
@@ -7084,7 +7111,7 @@ app10.get("/", async (c) => {
7084
7111
  thinkingView,
7085
7112
  onboardingComplete,
7086
7113
  businessName,
7087
- conversationId: getConversationIdForSession(sessionKey) ?? null
7114
+ conversationId: getConversationIdForSession(cacheKey) ?? null
7088
7115
  });
7089
7116
  });
7090
7117
  app10.post("/", async (c) => {
@@ -7435,7 +7462,7 @@ ${inner.content}`;
7435
7462
  }
7436
7463
  function chatReject(status, error, sk) {
7437
7464
  const keyPrefix = sk ? sk.slice(0, 8) + "\u2026" : "none";
7438
- console.log(`[session] chat-reject status=${status} reason="${error}" sessionKey=${keyPrefix}`);
7465
+ console.log(`[session] chat-reject status=${status} reason="${error}" cacheKey=${keyPrefix}`);
7439
7466
  return new Response(JSON.stringify({ error }), {
7440
7467
  status,
7441
7468
  headers: { "Content-Type": "application/json" }
@@ -7443,9 +7470,9 @@ function chatReject(status, error, sk) {
7443
7470
  }
7444
7471
  var app11 = new Hono();
7445
7472
  app11.post("/cancel", requireAdminSession, async (c) => {
7446
- const session_key = c.var.sessionKey;
7473
+ const session_key = c.var.cacheKey;
7447
7474
  try {
7448
- const { interruptClient: interruptClient2 } = await import("./client-pool-VYDOIFG7.js");
7475
+ const { interruptClient: interruptClient2 } = await import("./client-pool-AIZ5QKFD.js");
7449
7476
  await interruptClient2(session_key);
7450
7477
  return c.json({ ok: true });
7451
7478
  } catch (err) {
@@ -7453,7 +7480,7 @@ app11.post("/cancel", requireAdminSession, async (c) => {
7453
7480
  }
7454
7481
  });
7455
7482
  app11.post("/", requireAdminSession, async (c) => {
7456
- const session_key = c.var.sessionKey;
7483
+ const session_key = c.var.cacheKey;
7457
7484
  const contentType = c.req.header("content-type") ?? "";
7458
7485
  let message;
7459
7486
  let userTimestamp;
@@ -7534,7 +7561,7 @@ app11.post("/", requireAdminSession, async (c) => {
7534
7561
  try {
7535
7562
  const authResult = await ensureAuth();
7536
7563
  if (authResult.status === "dead" || authResult.status === "missing") {
7537
- console.log(`[session] chat-reject status=401 reason="auth_expired" sessionKey=${session_key.slice(0, 8)}\u2026 auth_status=${authResult.status}`);
7564
+ console.log(`[session] chat-reject status=401 reason="auth_expired" cacheKey=${session_key.slice(0, 8)}\u2026 auth_status=${authResult.status}`);
7538
7565
  return new Response(
7539
7566
  JSON.stringify({ error: "auth_expired", auth_status: authResult.status }),
7540
7567
  { status: 401, headers: { "Content-Type": "application/json" } }
@@ -7622,10 +7649,10 @@ app11.post("/", requireAdminSession, async (c) => {
7622
7649
  setAgentSessionId(session_key, prior.agentSessionId);
7623
7650
  resumedAgentSessionId = prior.agentSessionId;
7624
7651
  acquireReason = "pin-rebind";
7625
- console.log(`[chat-route] resume-from-prior conversationId=${conversationId.slice(0, 8)}\u2026 priorAgentSessionId=${prior.agentSessionId.slice(0, 8)}\u2026 sessionKey=${session_key.slice(0, 12)}\u2026`);
7652
+ console.log(`[chat-route] resume-from-prior conversationId=${conversationId.slice(0, 8)}\u2026 priorAgentSessionId=${prior.agentSessionId.slice(0, 8)}\u2026 cacheKey=${session_key.slice(0, 12)}\u2026`);
7626
7653
  } else {
7627
7654
  if (wantsPrior) {
7628
- console.log(`[chat-route] prior-conversation-not-found accountId=${accountId.slice(0, 8)}\u2026 userId=${userId.slice(0, 8)}\u2026 sessionKey=${session_key.slice(0, 12)}\u2026 \u2014 cold-minting`);
7655
+ console.log(`[chat-route] prior-conversation-not-found accountId=${accountId.slice(0, 8)}\u2026 userId=${userId.slice(0, 8)}\u2026 cacheKey=${session_key.slice(0, 12)}\u2026 \u2014 cold-minting`);
7629
7656
  }
7630
7657
  const minted = randomUUID6();
7631
7658
  const created = await createNewAdminConversation(userId, accountId, session_key, userName, minted);
@@ -7635,7 +7662,7 @@ app11.post("/", requireAdminSession, async (c) => {
7635
7662
  conversationId = created;
7636
7663
  setConversationIdForSession(session_key, conversationId);
7637
7664
  acquireReason = "cold";
7638
- console.log(`[chat-route] new conversation conversationId=${conversationId} sessionKey=${session_key.slice(0, 12)}\u2026`);
7665
+ console.log(`[chat-route] new conversation conversationId=${conversationId} cacheKey=${session_key.slice(0, 12)}\u2026`);
7639
7666
  }
7640
7667
  }
7641
7668
  const encoder = new TextEncoder();
@@ -7643,12 +7670,12 @@ app11.post("/", requireAdminSession, async (c) => {
7643
7670
  const sk = conversationId.slice(0, 8);
7644
7671
  const teeStreamLogPath = resolve8(account.accountDir, "logs", `claude-agent-stream-${conversationId}.log`);
7645
7672
  try {
7646
- appendFileSync3(teeStreamLogPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [chat-route-version=task965-mint-at-entry] sessionKey=${session_key.slice(0, 12)}\u2026 conversationId=${conversationId}
7673
+ appendFileSync3(teeStreamLogPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [chat-route-version=task965-mint-at-entry] cacheKey=${session_key.slice(0, 12)}\u2026 conversationId=${conversationId}
7647
7674
  `);
7648
7675
  } catch {
7649
7676
  }
7650
7677
  try {
7651
- appendFileSync3(teeStreamLogPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [client-acquire] sessionKey=${session_key.slice(0, 12)}\u2026 resume=${resumedAgentSessionId ? resumedAgentSessionId.slice(0, 8) + "\u2026" : "none"} reason=${acquireReason}
7678
+ appendFileSync3(teeStreamLogPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [client-acquire] cacheKey=${session_key.slice(0, 12)}\u2026 resume=${resumedAgentSessionId ? resumedAgentSessionId.slice(0, 8) + "\u2026" : "none"} reason=${acquireReason}
7652
7679
  `);
7653
7680
  } catch {
7654
7681
  }
@@ -7657,12 +7684,12 @@ app11.post("/", requireAdminSession, async (c) => {
7657
7684
  incoming.on("close", () => {
7658
7685
  const tsClose = (/* @__PURE__ */ new Date()).toISOString();
7659
7686
  try {
7660
- appendFileSync3(teeStreamLogPath, `[${tsClose}] [incoming-close] sessionKey=${session_key.slice(0, 12)}\u2026 complete=${incoming.complete}
7687
+ appendFileSync3(teeStreamLogPath, `[${tsClose}] [incoming-close] cacheKey=${session_key.slice(0, 12)}\u2026 complete=${incoming.complete}
7661
7688
  `);
7662
7689
  } catch {
7663
7690
  }
7664
7691
  if (incoming.complete === false) {
7665
- console.error(`[${tsClose}] [incoming-close] DISCONNECT sessionKey=${session_key.slice(0, 12)}\u2026`);
7692
+ console.error(`[${tsClose}] [incoming-close] DISCONNECT cacheKey=${session_key.slice(0, 12)}\u2026`);
7666
7693
  interruptClient(session_key).catch((err) => {
7667
7694
  try {
7668
7695
  appendFileSync3(teeStreamLogPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [incoming-close] interrupt-failed: ${err instanceof Error ? err.message : String(err)}
@@ -7718,7 +7745,7 @@ app11.post("/", requireAdminSession, async (c) => {
7718
7745
  const reasonStr = typeof reason === "string" ? reason : reason instanceof Error ? reason.message : "consumer-cancelled";
7719
7746
  sseLog.write(`[${ts}] [${sk}] admin: CANCEL [stream-cancel] reason=${reasonStr}
7720
7747
  `);
7721
- console.error(`[${(/* @__PURE__ */ new Date()).toISOString()}] [stream-cancel-callback] sessionKey=${session_key.slice(0, 12)}\u2026 reason=${JSON.stringify(reasonStr)}`);
7748
+ console.error(`[${(/* @__PURE__ */ new Date()).toISOString()}] [stream-cancel-callback] cacheKey=${session_key.slice(0, 12)}\u2026 reason=${JSON.stringify(reasonStr)}`);
7722
7749
  interruptClient(session_key).catch((err) => {
7723
7750
  sseLog.write(`[${ts}] [${sk}] admin: ABORT [interrupt-failed] ${err instanceof Error ? err.message : String(err)}
7724
7751
  `);
@@ -7726,7 +7753,7 @@ app11.post("/", requireAdminSession, async (c) => {
7726
7753
  },
7727
7754
  async start(controller) {
7728
7755
  let controllerOpen = true;
7729
- const sseEntry = { controller, conversationId, sessionKey: session_key };
7756
+ const sseEntry = { controller, conversationId, cacheKey: session_key };
7730
7757
  try {
7731
7758
  const reqSignal = c.req.raw?.signal;
7732
7759
  if (reqSignal) {
@@ -7840,14 +7867,89 @@ app11.post("/", requireAdminSession, async (c) => {
7840
7867
  });
7841
7868
  var chat_default2 = app11;
7842
7869
 
7843
- // server/routes/admin/compact.ts
7870
+ // server/routes/admin/chat-failure.ts
7871
+ var MODE_VALUES = /* @__PURE__ */ new Set(["server-shutdown", "client-network-drop"]);
7872
+ var UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
7873
+ var ISO_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/;
7874
+ function validate(body) {
7875
+ if (typeof body.mode !== "string" || !MODE_VALUES.has(body.mode)) {
7876
+ return { ok: false, reason: "mode" };
7877
+ }
7878
+ if (typeof body.at !== "string" || !ISO_RE.test(body.at)) {
7879
+ return { ok: false, reason: "at" };
7880
+ }
7881
+ if (body.conversationId !== null && (typeof body.conversationId !== "string" || !UUID_RE3.test(body.conversationId))) {
7882
+ return { ok: false, reason: "conversationId" };
7883
+ }
7884
+ if (typeof body.prior_event_count !== "number" || !Number.isInteger(body.prior_event_count) || body.prior_event_count < 0) {
7885
+ return { ok: false, reason: "prior_event_count" };
7886
+ }
7887
+ return {
7888
+ ok: true,
7889
+ value: {
7890
+ mode: body.mode,
7891
+ at: body.at,
7892
+ conversationId: body.conversationId,
7893
+ priorEventCount: body.prior_event_count
7894
+ }
7895
+ };
7896
+ }
7844
7897
  var app12 = new Hono();
7845
7898
  app12.post("/", requireAdminSession, async (c) => {
7846
- const sessionKey = c.var.sessionKey;
7899
+ let body;
7900
+ try {
7901
+ body = await c.req.json();
7902
+ } catch {
7903
+ console.error(`[admin-chat-failure] persist kind=? conversationId=? writer=clientReport outcome=fail reason=body-not-json`);
7904
+ return c.json({ ok: false, reason: "body-not-json" }, 400);
7905
+ }
7906
+ const v = validate(body);
7907
+ if (!v.ok) {
7908
+ console.error(`[admin-chat-failure] persist kind=? conversationId=? writer=clientReport outcome=fail reason=schema:${v.reason}`);
7909
+ return c.json({ ok: false, reason: `schema:${v.reason}` }, 400);
7910
+ }
7911
+ const cacheKey = c.var.cacheKey;
7912
+ const accountId = getAccountIdForSession(cacheKey);
7913
+ if (!accountId) {
7914
+ console.error(`[admin-chat-failure] persist kind=${v.value.mode} conversationId=? writer=clientReport outcome=fail reason=no-account`);
7915
+ return c.json({ ok: false, reason: "no-account" }, 401);
7916
+ }
7917
+ const cid = v.value.conversationId;
7918
+ const cidTag = cid ? cid : "null";
7919
+ console.error(`[admin-chat] stream-drop kind=${v.value.mode} conversationId=${cidTag} at=${v.value.at}`);
7920
+ if (cid === null) {
7921
+ console.error(`[admin-chat-failure] persist kind=${v.value.mode} conversationId=null writer=clientReport outcome=skip reason=no-cid`);
7922
+ return c.body(null, 204);
7923
+ }
7924
+ const result = await writeTurnFailure({
7925
+ conversationId: cid,
7926
+ accountId,
7927
+ mode: v.value.mode,
7928
+ at: v.value.at,
7929
+ cacheKey,
7930
+ priorEventCount: v.value.priorEventCount
7931
+ });
7932
+ if (!result.ok) {
7933
+ if (result.reason === "cid-not-found") {
7934
+ console.error(`[admin-chat-failure] persist kind=${v.value.mode} conversationId=${cid} writer=clientReport outcome=skip reason=cid-not-found`);
7935
+ return c.body(null, 204);
7936
+ }
7937
+ console.error(`[admin-chat-failure] persist kind=${v.value.mode} conversationId=${cid} writer=clientReport outcome=fail reason=${result.reason}`);
7938
+ return c.json({ ok: false, reason: result.reason }, 500);
7939
+ }
7940
+ console.error(`[admin-chat-failure] persist kind=${v.value.mode} conversationId=${cid} writer=clientReport outcome=ok at=${result.at}`);
7941
+ return c.json({ ok: true, at: result.at });
7942
+ });
7943
+ var chat_failure_default = app12;
7944
+
7945
+ // server/routes/admin/compact.ts
7946
+ var app13 = new Hono();
7947
+ app13.post("/", requireAdminSession, async (c) => {
7948
+ const cacheKey = c.var.cacheKey;
7847
7949
  const encoder = new TextEncoder();
7848
7950
  const stream = new ReadableStream({
7849
7951
  async start(controller) {
7850
- const iter = compactSession(sessionKey);
7952
+ const iter = compactSession(cacheKey);
7851
7953
  let step = await iter.next();
7852
7954
  while (!step.done) {
7853
7955
  controller.enqueue(encoder.encode(`data: ${JSON.stringify({ type: "status", message: step.value })}
@@ -7869,18 +7971,18 @@ app12.post("/", requireAdminSession, async (c) => {
7869
7971
  }
7870
7972
  });
7871
7973
  });
7872
- var compact_default = app12;
7974
+ var compact_default = app13;
7873
7975
 
7874
7976
  // server/routes/admin/logs.ts
7875
7977
  import { existsSync as existsSync12, readdirSync as readdirSync5, readFileSync as readFileSync11, statSync as statSync6 } from "fs";
7876
7978
  import { resolve as resolve9, basename as basename2 } from "path";
7877
7979
  var TAIL_BYTES = 8192;
7878
- var app13 = new Hono();
7879
- app13.get("/", async (c) => {
7980
+ var app14 = new Hono();
7981
+ app14.get("/", async (c) => {
7880
7982
  const fileParam = c.req.query("file");
7881
7983
  const typeParam = c.req.query("type");
7882
7984
  const conversationIdParam = c.req.query("conversationId");
7883
- const sessionKeyParam = c.req.query("sessionKey");
7985
+ const cacheKeyParam = c.req.query("cacheKey");
7884
7986
  const download = c.req.query("download") === "1";
7885
7987
  const account = resolveAccount();
7886
7988
  const accountLogDir = account ? resolve9(account.accountDir, "logs") : null;
@@ -7927,56 +8029,29 @@ app13.get("/", async (c) => {
7927
8029
  400
7928
8030
  );
7929
8031
  }
7930
- if (!conversationIdParam && !sessionKeyParam) {
8032
+ if (!conversationIdParam && !cacheKeyParam) {
7931
8033
  console.warn(`[admin/logs] rejected type=${typeParam} reason=no-id`);
7932
8034
  return c.json(
7933
- { error: `type=${typeParam} requires conversationId or sessionKey`, code: "ID_REQUIRED" },
8035
+ { error: `type=${typeParam} requires conversationId or cacheKey`, code: "ID_REQUIRED" },
7934
8036
  400
7935
8037
  );
7936
8038
  }
7937
- let sessionKey = sessionKeyParam ?? null;
7938
- let conversationId = conversationIdParam ?? null;
7939
- if (!sessionKey && conversationId || sessionKey && !conversationId) {
7940
- const neoSession = getSession();
7941
- try {
7942
- if (!sessionKey && conversationId) {
7943
- const result = await neoSession.run(
7944
- `MATCH (c:Conversation {conversationId: $conversationId})
7945
- RETURN c.sessionKey AS sessionKey LIMIT 1`,
7946
- { conversationId }
7947
- );
7948
- const sk = result.records[0]?.get("sessionKey");
7949
- if (typeof sk === "string" && sk.length > 0) sessionKey = sk;
7950
- } else if (sessionKey && !conversationId) {
7951
- const result = await neoSession.run(
7952
- `MATCH (c:Conversation {sessionKey: $sessionKey})
7953
- RETURN c.conversationId AS conversationId LIMIT 1`,
7954
- { sessionKey }
7955
- );
7956
- const cid = result.records[0]?.get("conversationId");
7957
- if (typeof cid === "string" && cid.length > 0) conversationId = cid;
7958
- }
7959
- } catch (err) {
7960
- const reason2 = err instanceof Error ? err.message : String(err);
7961
- console.warn(`[admin/logs] id-lookup-neo4j-error sessionKey=${sessionKey ?? "none"} conversationId=${conversationId ?? "none"} reason=${reason2}`);
7962
- } finally {
7963
- await neoSession.close();
7964
- }
7965
- }
8039
+ const cacheKey = cacheKeyParam ?? null;
8040
+ const conversationId = conversationIdParam ?? null;
7966
8041
  const MISSING_SENTINEL = ".task702-identifier-not-supplied.log";
7967
8042
  const fullFilename = conversationId ? `${prefix}-${conversationId}.log` : MISSING_SENTINEL;
7968
- const preflushFilename = sessionKey && typeParam === "public" ? `${prefix}-${preflushSliceOf(sessionKey)}.log` : MISSING_SENTINEL;
8043
+ const preflushFilename = cacheKey && typeParam === "public" ? `${prefix}-${preflushSliceOf(cacheKey)}.log` : MISSING_SENTINEL;
7969
8044
  const { hits, stalePreflushPaths, tried } = resolveConversationLogPaths(
7970
8045
  fullFilename,
7971
8046
  preflushFilename,
7972
8047
  logDirs
7973
8048
  );
7974
8049
  const stalePreflushCount = stalePreflushPaths.length;
7975
- const sessionKeySlice = sessionKey ? sessionKey.slice(0, 12) : "none";
8050
+ const cacheKeySlice = cacheKey ? cacheKey.slice(0, 12) : "none";
7976
8051
  const conversationIdSlice = conversationId ? conversationId.slice(0, 12) : "none";
7977
8052
  if (hits.length > 0) {
7978
8053
  const hit = hits[0];
7979
- console.info(`[admin/logs] resolved sessionKey=${sessionKeySlice} conversationId=${conversationIdSlice} shape=${hit.shape} stalePreflushCount=${stalePreflushCount}`);
8054
+ console.info(`[admin/logs] resolved cacheKey=${cacheKeySlice} conversationId=${conversationIdSlice} shape=${hit.shape} stalePreflushCount=${stalePreflushCount}`);
7980
8055
  try {
7981
8056
  const filename = basename2(hit.path);
7982
8057
  if (stalePreflushCount > 0 && !download) {
@@ -8006,14 +8081,14 @@ app13.get("/", async (c) => {
8006
8081
  );
8007
8082
  }
8008
8083
  }
8009
- const reason = !conversationId ? "no preflush log on disk for sessionKey and conversationId not derivable" : !sessionKey ? "no full log on disk and sessionKey not derivable" : "no preflush log, no full log";
8010
- console.warn(`[admin/logs] not-found tried=[${tried.join(",")}] sessionKey=${sessionKeySlice} conversationId=${conversationIdSlice} reason=${JSON.stringify(reason)}`);
8084
+ const reason = !conversationId ? "no preflush log on disk for cacheKey and conversationId not derivable" : !cacheKey ? "no full log on disk and cacheKey not derivable" : "no preflush log, no full log";
8085
+ console.warn(`[admin/logs] not-found tried=[${tried.join(",")}] cacheKey=${cacheKeySlice} conversationId=${conversationIdSlice} reason=${JSON.stringify(reason)}`);
8011
8086
  return c.json(
8012
8087
  {
8013
8088
  error: reason,
8014
8089
  code: "NOT_FOUND",
8015
8090
  reason,
8016
- sessionKey: sessionKey ?? null,
8091
+ cacheKey: cacheKey ?? null,
8017
8092
  conversationId: conversationId ?? null
8018
8093
  },
8019
8094
  404
@@ -8046,12 +8121,12 @@ app13.get("/", async (c) => {
8046
8121
  }
8047
8122
  return c.json({ logs });
8048
8123
  });
8049
- var logs_default = app13;
8124
+ var logs_default = app14;
8050
8125
 
8051
8126
  // server/routes/admin/claude-info.ts
8052
8127
  import { execFileSync as execFileSync2 } from "child_process";
8053
- var app14 = new Hono();
8054
- app14.get("/", (c) => {
8128
+ var app15 = new Hono();
8129
+ app15.get("/", (c) => {
8055
8130
  let version = "unknown";
8056
8131
  try {
8057
8132
  const raw = execFileSync2("claude", ["--version"], { encoding: "utf-8", timeout: 5e3 });
@@ -8069,17 +8144,17 @@ app14.get("/", (c) => {
8069
8144
  const thinkingView = resolvedAccount?.config.thinkingView ?? "default";
8070
8145
  return c.json({ version, account, model, thinkingView });
8071
8146
  });
8072
- var claude_info_default = app14;
8147
+ var claude_info_default = app15;
8073
8148
 
8074
8149
  // server/routes/admin/attachment.ts
8075
8150
  import { readFile as readFile2, readdir } from "fs/promises";
8076
8151
  import { existsSync as existsSync13 } from "fs";
8077
8152
  import { resolve as resolve10 } from "path";
8078
- var app15 = new Hono();
8079
- app15.get("/:attachmentId", requireAdminSession, async (c) => {
8153
+ var app16 = new Hono();
8154
+ app16.get("/:attachmentId", requireAdminSession, async (c) => {
8080
8155
  const attachmentId = c.req.param("attachmentId");
8081
- const sessionKey = c.var.sessionKey;
8082
- const accountId = getAccountIdForSession(sessionKey);
8156
+ const cacheKey = c.var.cacheKey;
8157
+ const accountId = getAccountIdForSession(cacheKey);
8083
8158
  if (!accountId) {
8084
8159
  return new Response("Unauthorized", { status: 401 });
8085
8160
  }
@@ -8115,13 +8190,13 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
8115
8190
  }
8116
8191
  });
8117
8192
  });
8118
- var attachment_default = app15;
8193
+ var attachment_default = app16;
8119
8194
 
8120
8195
  // server/routes/admin/agents.ts
8121
8196
  import { resolve as resolve11 } from "path";
8122
8197
  import { readdirSync as readdirSync6, readFileSync as readFileSync12, existsSync as existsSync14, rmSync } from "fs";
8123
- var app16 = new Hono();
8124
- app16.get("/", (c) => {
8198
+ var app17 = new Hono();
8199
+ app17.get("/", (c) => {
8125
8200
  const account = resolveAccount();
8126
8201
  if (!account) return c.json({ agents: [] });
8127
8202
  const agentsDir = resolve11(account.accountDir, "agents");
@@ -8151,7 +8226,7 @@ app16.get("/", (c) => {
8151
8226
  }
8152
8227
  return c.json({ agents });
8153
8228
  });
8154
- app16.delete("/:slug", async (c) => {
8229
+ app17.delete("/:slug", async (c) => {
8155
8230
  const slug = c.req.param("slug");
8156
8231
  const account = resolveAccount();
8157
8232
  if (!account) return c.json({ error: "No account resolved" }, 400);
@@ -8181,7 +8256,7 @@ app16.delete("/:slug", async (c) => {
8181
8256
  return c.json({ error: "Failed to delete agent" }, 500);
8182
8257
  }
8183
8258
  });
8184
- app16.post("/:slug/project", async (c) => {
8259
+ app17.post("/:slug/project", async (c) => {
8185
8260
  const slug = c.req.param("slug");
8186
8261
  const account = resolveAccount();
8187
8262
  if (!account) return c.json({ error: "No account resolved" }, 400);
@@ -8203,7 +8278,7 @@ app16.post("/:slug/project", async (c) => {
8203
8278
  return c.json({ error: `Projection failed: ${msg}` }, 500);
8204
8279
  }
8205
8280
  });
8206
- var agents_default = app16;
8281
+ var agents_default = app17;
8207
8282
 
8208
8283
  // server/routes/admin/sessions.ts
8209
8284
  import crypto2 from "crypto";
@@ -8471,19 +8546,19 @@ function formatAge(updatedAtStr) {
8471
8546
  return "unknown";
8472
8547
  }
8473
8548
  }
8474
- var app17 = new Hono();
8475
- app17.get("/", requireAdminSession, async (c) => {
8476
- const sessionKey = c.var.sessionKey;
8477
- const accountId = getAccountIdForSession(sessionKey);
8549
+ var app18 = new Hono();
8550
+ app18.get("/", requireAdminSession, async (c) => {
8551
+ const cacheKey = c.var.cacheKey;
8552
+ const accountId = getAccountIdForSession(cacheKey);
8478
8553
  if (!accountId) return c.json({ error: "Account not found for session" }, 401);
8479
- const userId = getUserIdForSession(sessionKey);
8554
+ const userId = getUserIdForSession(cacheKey);
8480
8555
  if (!userId) return c.json({ error: "User identity required \u2014 authenticate with users.json PIN" }, 401);
8481
8556
  try {
8482
8557
  const flushed = await listAdminSessions(accountId, userId, 20);
8483
8558
  const inProgressRaw = listAdminSessionsInProgress(accountId, userId);
8484
8559
  const flushedRows = flushed.map((r) => ({
8485
8560
  conversationId: r.conversationId,
8486
- sessionKey: null,
8561
+ cacheKey: null,
8487
8562
  name: r.name,
8488
8563
  updatedAt: r.updatedAt,
8489
8564
  phase: "flushed",
@@ -8491,7 +8566,7 @@ app17.get("/", requireAdminSession, async (c) => {
8491
8566
  }));
8492
8567
  const inProgressRows = inProgressRaw.map((r) => ({
8493
8568
  conversationId: null,
8494
- sessionKey: r.sessionKey,
8569
+ cacheKey: r.cacheKey,
8495
8570
  name: null,
8496
8571
  updatedAt: new Date(r.createdAt).toISOString(),
8497
8572
  phase: "pre-flush",
@@ -8512,52 +8587,52 @@ app17.get("/", requireAdminSession, async (c) => {
8512
8587
  return c.json({ error: "Failed to fetch sessions" }, 500);
8513
8588
  }
8514
8589
  });
8515
- app17.post("/new", requireAdminSession, async (c) => {
8516
- const oldSessionKey = c.var.sessionKey;
8517
- const accountId = getAccountIdForSession(oldSessionKey);
8518
- const userId = getUserIdForSession(oldSessionKey);
8590
+ app18.post("/new", requireAdminSession, async (c) => {
8591
+ const oldCacheKey = c.var.cacheKey;
8592
+ const accountId = getAccountIdForSession(oldCacheKey);
8593
+ const userId = getUserIdForSession(oldCacheKey);
8519
8594
  if (!accountId || !userId) {
8520
8595
  return c.json({ error: "Session missing account or user context" }, 400);
8521
8596
  }
8522
- const newSessionKey = crypto2.randomUUID();
8523
- const userName = getUserNameForSession(oldSessionKey);
8524
- registerSession(newSessionKey, "admin", accountId, void 0, userId, userName);
8525
- const previousConversationId = clearSessionHistory(oldSessionKey);
8526
- unregisterSession(oldSessionKey);
8527
- console.log(`[session] ${(/* @__PURE__ */ new Date()).toISOString()} session reset for new conversation: oldSessionKey=${oldSessionKey.slice(0, 8)}\u2026 newSessionKey=${newSessionKey.slice(0, 8)}\u2026 previousConversationId=${previousConversationId?.slice(0, 8) ?? "none"}\u2026 newConversationId=deferred`);
8528
- return c.json({ session_key: newSessionKey, conversationId: null });
8597
+ const newCacheKey = crypto2.randomUUID();
8598
+ const userName = getUserNameForSession(oldCacheKey);
8599
+ registerSession(newCacheKey, "admin", accountId, void 0, userId, userName);
8600
+ const previousConversationId = clearSessionHistory(oldCacheKey);
8601
+ unregisterSession(oldCacheKey);
8602
+ console.log(`[session] ${(/* @__PURE__ */ new Date()).toISOString()} session reset for new conversation: oldCacheKey=${oldCacheKey.slice(0, 8)}\u2026 newCacheKey=${newCacheKey.slice(0, 8)}\u2026 previousConversationId=${previousConversationId?.slice(0, 8) ?? "none"}\u2026 newConversationId=deferred`);
8603
+ return c.json({ session_key: newCacheKey, conversationId: null });
8529
8604
  });
8530
- app17.post("/switch", requireAdminSession, async (c) => {
8531
- const sessionKey = c.var.sessionKey;
8532
- const accountId = getAccountIdForSession(sessionKey);
8533
- const userId = getUserIdForSession(sessionKey);
8605
+ app18.post("/switch", requireAdminSession, async (c) => {
8606
+ const cacheKey = c.var.cacheKey;
8607
+ const accountId = getAccountIdForSession(cacheKey);
8608
+ const userId = getUserIdForSession(cacheKey);
8534
8609
  if (!accountId || !userId) {
8535
8610
  return c.json({ error: "Session missing account or user context" }, 400);
8536
8611
  }
8537
8612
  const body = await c.req.json().catch(() => ({}));
8538
- const targetSessionKey = typeof body.target_session_key === "string" ? body.target_session_key : "";
8539
- if (!targetSessionKey) {
8613
+ const targetCacheKey = typeof body.target_session_key === "string" ? body.target_session_key : "";
8614
+ if (!targetCacheKey) {
8540
8615
  return c.json({ error: "target_session_key required" }, 400);
8541
8616
  }
8542
- const targetCheck = validateSession(targetSessionKey, "admin");
8617
+ const targetCheck = validateSession(targetCacheKey, "admin");
8543
8618
  if (!targetCheck.ok) {
8544
- console.error(`[session-switch] reject reason=${targetCheck.reason} from=${sessionKey.slice(0, 8)} target=${targetSessionKey.slice(0, 8)}`);
8619
+ console.error(`[session-switch] reject reason=${targetCheck.reason} from=${cacheKey.slice(0, 8)} target=${targetCacheKey.slice(0, 8)}`);
8545
8620
  return c.json({ error: "Target session not registered or wrong agent type", code: targetCheck.reason }, 404);
8546
8621
  }
8547
- const targetAccountId = getAccountIdForSession(targetSessionKey);
8548
- const targetUserId = getUserIdForSession(targetSessionKey);
8622
+ const targetAccountId = getAccountIdForSession(targetCacheKey);
8623
+ const targetUserId = getUserIdForSession(targetCacheKey);
8549
8624
  if (targetAccountId !== accountId || targetUserId !== userId) {
8550
- console.error(`[session-switch] reject reason=ownership-mismatch from=${sessionKey.slice(0, 8)} target=${targetSessionKey.slice(0, 8)} accountMatch=${targetAccountId === accountId} userMatch=${targetUserId === userId}`);
8625
+ console.error(`[session-switch] reject reason=ownership-mismatch from=${cacheKey.slice(0, 8)} target=${targetCacheKey.slice(0, 8)} accountMatch=${targetAccountId === accountId} userMatch=${targetUserId === userId}`);
8551
8626
  return c.json({ error: "Target session not owned by current operator" }, 403);
8552
8627
  }
8553
- const targetConversationId = getConversationIdForSession(targetSessionKey) ?? null;
8554
- console.log(`[session-switch] from=${sessionKey.slice(0, 8)} to=${targetSessionKey.slice(0, 8)} targetConvId=${targetConversationId?.slice(0, 8) ?? "none"} accountId=${accountId.slice(0, 8)}`);
8555
- return c.json({ session_key: targetSessionKey, conversationId: targetConversationId });
8628
+ const targetConversationId = getConversationIdForSession(targetCacheKey) ?? null;
8629
+ console.log(`[session-switch] from=${cacheKey.slice(0, 8)} to=${targetCacheKey.slice(0, 8)} targetConvId=${targetConversationId?.slice(0, 8) ?? "none"} accountId=${accountId.slice(0, 8)}`);
8630
+ return c.json({ session_key: targetCacheKey, conversationId: targetConversationId });
8556
8631
  });
8557
- app17.delete("/:id", requireAdminSession, async (c) => {
8632
+ app18.delete("/:id", requireAdminSession, async (c) => {
8558
8633
  const conversationId = c.req.param("id");
8559
- const sessionKey = c.var.sessionKey;
8560
- const accountId = getAccountIdForSession(sessionKey);
8634
+ const cacheKey = c.var.cacheKey;
8635
+ const accountId = getAccountIdForSession(cacheKey);
8561
8636
  if (!accountId) return c.json({ error: "Account not found for session" }, 401);
8562
8637
  const owned = await verifyConversationOwnership(conversationId, accountId);
8563
8638
  if (!owned) return c.json({ error: "Conversation not found" }, 404);
@@ -8569,42 +8644,42 @@ app17.delete("/:id", requireAdminSession, async (c) => {
8569
8644
  return c.json({ error: "Failed to delete session" }, 500);
8570
8645
  }
8571
8646
  });
8572
- app17.post("/:id/resume", async (c) => {
8647
+ app18.post("/:id/resume", async (c) => {
8573
8648
  const conversationId = c.req.param("id");
8574
- const sessionKey = c.req.query("session_key") ?? "";
8575
- if (!sessionKey) {
8649
+ const cacheKey = c.req.query("session_key") ?? "";
8650
+ if (!cacheKey) {
8576
8651
  console.error(`[session] middleware-reject status=400 code=session-missing reason="session_key required" path=${c.req.path}`);
8577
8652
  return c.json({ error: "session_key required", code: "session-missing" }, 400);
8578
8653
  }
8579
8654
  let bridged = false;
8580
- let result = validateSession(sessionKey, "admin");
8655
+ let result = validateSession(cacheKey, "admin");
8581
8656
  if (!result.ok) {
8582
8657
  if (result.reason === "session-not-registered") {
8583
- const bridge = await tryCookieBridgeForConversation(c, sessionKey, conversationId);
8658
+ const bridge = await tryCookieBridgeForConversation(c, cacheKey, conversationId);
8584
8659
  if (!bridge.ok) {
8585
8660
  if (bridge.reason === "conversation-not-found") {
8586
8661
  return c.json({ error: "Conversation not found" }, 404);
8587
8662
  }
8588
- const tail = sessionKey.slice(0, 8);
8589
- console.error(`[session] middleware-reject status=401 code=session-not-registered reason="cookie-bridge-rejected:${bridge.reason}" path=${c.req.path} sessionKey=${tail}\u2026`);
8663
+ const tail = cacheKey.slice(0, 8);
8664
+ console.error(`[session] middleware-reject status=401 code=session-not-registered reason="cookie-bridge-rejected:${bridge.reason}" path=${c.req.path} cacheKey=${tail}\u2026`);
8590
8665
  return c.json({ error: "Invalid or expired admin session", code: "session-not-registered" }, 401);
8591
8666
  }
8592
8667
  bridged = true;
8593
- result = validateSession(sessionKey, "admin");
8668
+ result = validateSession(cacheKey, "admin");
8594
8669
  if (!result.ok) {
8595
- const tail = sessionKey.slice(0, 8);
8596
- console.error(`[session] middleware-reject status=401 code=session-not-registered reason="post-bridge re-validate failed" path=${c.req.path} sessionKey=${tail}\u2026`);
8670
+ const tail = cacheKey.slice(0, 8);
8671
+ console.error(`[session] middleware-reject status=401 code=session-not-registered reason="post-bridge re-validate failed" path=${c.req.path} cacheKey=${tail}\u2026`);
8597
8672
  return c.json({ error: "Invalid or expired admin session", code: "session-not-registered" }, 401);
8598
8673
  }
8599
8674
  } else {
8600
- const tail = sessionKey.slice(0, 8);
8675
+ const tail = cacheKey.slice(0, 8);
8601
8676
  const wireCode = result.reason === "agent-type-mismatch" ? "session-not-registered" : result.reason;
8602
- console.error(`[session] middleware-reject status=401 code=${wireCode} reason="invalid or expired admin session" path=${c.req.path} sessionKey=${tail}\u2026`);
8677
+ console.error(`[session] middleware-reject status=401 code=${wireCode} reason="invalid or expired admin session" path=${c.req.path} cacheKey=${tail}\u2026`);
8603
8678
  return c.json({ error: "Invalid or expired admin session", code: wireCode }, 401);
8604
8679
  }
8605
8680
  }
8606
- const accountId = getAccountIdForSession(sessionKey);
8607
- const userId = getUserIdForSession(sessionKey);
8681
+ const accountId = getAccountIdForSession(cacheKey);
8682
+ const userId = getUserIdForSession(cacheKey);
8608
8683
  if (!accountId) {
8609
8684
  return c.json({ error: "Session missing account context" }, 400);
8610
8685
  }
@@ -8614,9 +8689,9 @@ app17.post("/:id/resume", async (c) => {
8614
8689
  const updatedAt = await verifyAndGetConversationUpdatedAt(conversationId, accountId);
8615
8690
  if (updatedAt === null) return c.json({ error: "Conversation not found" }, 404);
8616
8691
  const persistedAgentSessionId = await getAgentSessionIdForConversation(conversationId);
8617
- setConversationIdForSession(sessionKey, conversationId);
8692
+ setConversationIdForSession(cacheKey, conversationId);
8618
8693
  if (persistedAgentSessionId) {
8619
- setAgentSessionId(sessionKey, persistedAgentSessionId);
8694
+ setAgentSessionId(cacheKey, persistedAgentSessionId);
8620
8695
  }
8621
8696
  const streamLogPath = resolvePath(ACCOUNTS_DIR, accountId, "logs", `claude-agent-stream-${conversationId}.log`);
8622
8697
  const tag = persistedAgentSessionId ? `agentSessionId=${persistedAgentSessionId.slice(0, 8)}\u2026` : "agentSessionId=missing";
@@ -8641,7 +8716,7 @@ app17.post("/:id/resume", async (c) => {
8641
8716
  jsonlMalformedLines = replay.malformedLines;
8642
8717
  jsonlReplayMessages = replay.messages;
8643
8718
  if (replay.jsonlMissing) {
8644
- console.error(`[jsonl-resume-miss] sessionKey=${sessionKey.slice(0, 8)}\u2026 conversationId=${conversationId.slice(0, 8)}\u2026 agentSessionId=${persistedAgentSessionId.slice(0, 8)}\u2026 expectedPath=${jsonlPath} configDir=${process.env.CLAUDE_CONFIG_DIR ?? "<unset>"}`);
8719
+ console.error(`[jsonl-resume-miss] cacheKey=${cacheKey.slice(0, 8)}\u2026 conversationId=${conversationId.slice(0, 8)}\u2026 agentSessionId=${persistedAgentSessionId.slice(0, 8)}\u2026 expectedPath=${jsonlPath} configDir=${process.env.CLAUDE_CONFIG_DIR ?? "<unset>"}`);
8645
8720
  }
8646
8721
  } else {
8647
8722
  jsonlMissing = true;
@@ -8804,7 +8879,7 @@ app17.post("/:id/resume", async (c) => {
8804
8879
  const reason = bridged ? "post-restart" : "page-refresh";
8805
8880
  try {
8806
8881
  const source = jsonlMissing ? persistedAgentSessionId ? "jsonl-missing" : "neo4j-only" : "jsonl";
8807
- appendFileSync4(streamLogPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [admin-resume] reason=${reason} source=${source} sessionKey=${sessionKey.slice(0, 8)} conversationId=${conversationId.slice(0, 8)} ${tag} loadedMessages=${messages.length} jsonlReplayMessages=${jsonlReplayMessages.length} neo4jMessages=${neo4jMessages.length} jsonlMalformed=${jsonlMalformedLines} componentCount=${totalComponents} userAttachmentCount=${totalAttachments} syntheticHidden=${syntheticHidden}
8882
+ appendFileSync4(streamLogPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [admin-resume] reason=${reason} source=${source} cacheKey=${cacheKey.slice(0, 8)} conversationId=${conversationId.slice(0, 8)} ${tag} loadedMessages=${messages.length} jsonlReplayMessages=${jsonlReplayMessages.length} neo4jMessages=${neo4jMessages.length} jsonlMalformed=${jsonlMalformedLines} componentCount=${totalComponents} userAttachmentCount=${totalAttachments} syntheticHidden=${syntheticHidden}
8808
8883
  `);
8809
8884
  if (totalComponents > 0) {
8810
8885
  appendFileSync4(streamLogPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [component-rehydrate] conversationId=${conversationId.slice(0, 8)} count=${totalComponents} valid=${totalValid} invalid=${totalInvalid} textRuns=${textRuns}
@@ -8821,7 +8896,7 @@ app17.post("/:id/resume", async (c) => {
8821
8896
  } catch {
8822
8897
  }
8823
8898
  const age = formatAge(updatedAt);
8824
- console.log(`[admin-resume] ${(/* @__PURE__ */ new Date()).toISOString()} reason=${reason} conversationId=${conversationId.slice(0, 8)}\u2026 age=${age} loaded=${messages.length} messages ${tag} components=${totalComponents} attachments=${totalAttachments} syntheticHidden=${syntheticHidden} jsonlMissing=${jsonlMissing} healMissing=${jsonlHealMissing} sessionKey=${sessionKey.slice(0, 8)}\u2026`);
8899
+ console.log(`[admin-resume] ${(/* @__PURE__ */ new Date()).toISOString()} reason=${reason} conversationId=${conversationId.slice(0, 8)}\u2026 age=${age} loaded=${messages.length} messages ${tag} components=${totalComponents} attachments=${totalAttachments} syntheticHidden=${syntheticHidden} jsonlMissing=${jsonlMissing} healMissing=${jsonlHealMissing} cacheKey=${cacheKey.slice(0, 8)}\u2026`);
8825
8900
  return c.json({
8826
8901
  conversationId,
8827
8902
  messages: rehydrated,
@@ -8834,10 +8909,10 @@ app17.post("/:id/resume", async (c) => {
8834
8909
  }
8835
8910
  });
8836
8911
  });
8837
- app17.post("/:id/label", requireAdminSession, async (c) => {
8912
+ app18.post("/:id/label", requireAdminSession, async (c) => {
8838
8913
  const conversationId = c.req.param("id");
8839
- const sessionKey = c.var.sessionKey;
8840
- const accountId = getAccountIdForSession(sessionKey);
8914
+ const cacheKey = c.var.cacheKey;
8915
+ const accountId = getAccountIdForSession(cacheKey);
8841
8916
  if (!accountId) return c.json({ error: "Account not found for session" }, 401);
8842
8917
  const owned = await verifyConversationOwnership(conversationId, accountId);
8843
8918
  if (!owned) return c.json({ error: "Conversation not found" }, 404);
@@ -8861,16 +8936,16 @@ app17.post("/:id/label", requireAdminSession, async (c) => {
8861
8936
  return c.json({ label: null });
8862
8937
  }
8863
8938
  });
8864
- app17.put("/:id/label", requireAdminSession, async (c) => {
8939
+ app18.put("/:id/label", requireAdminSession, async (c) => {
8865
8940
  const conversationId = c.req.param("id");
8866
- const sessionKey = c.var.sessionKey;
8941
+ const cacheKey = c.var.cacheKey;
8867
8942
  let body;
8868
8943
  try {
8869
8944
  body = await c.req.json();
8870
8945
  } catch {
8871
8946
  return c.json({ error: "Invalid JSON body" }, 400);
8872
8947
  }
8873
- const accountId = getAccountIdForSession(sessionKey);
8948
+ const accountId = getAccountIdForSession(cacheKey);
8874
8949
  if (!accountId) return c.json({ error: "Account not found for session" }, 401);
8875
8950
  const owned = await verifyConversationOwnership(conversationId, accountId);
8876
8951
  if (!owned) return c.json({ error: "Conversation not found" }, 404);
@@ -8887,11 +8962,11 @@ app17.put("/:id/label", requireAdminSession, async (c) => {
8887
8962
  return c.json({ error: "Failed to rename session" }, 500);
8888
8963
  }
8889
8964
  });
8890
- var sessions_default = app17;
8965
+ var sessions_default = app18;
8891
8966
 
8892
8967
  // server/routes/admin/browser.ts
8893
- var app18 = new Hono();
8894
- app18.post("/launch", async (c) => {
8968
+ var app19 = new Hono();
8969
+ app19.post("/launch", async (c) => {
8895
8970
  try {
8896
8971
  const transport = resolveBrowserTransport(c.req.raw, c.env?.incoming?.socket?.remoteAddress);
8897
8972
  if (transport === "vnc") {
@@ -8913,7 +8988,7 @@ app18.post("/launch", async (c) => {
8913
8988
  );
8914
8989
  }
8915
8990
  });
8916
- var browser_default = app18;
8991
+ var browser_default = app19;
8917
8992
 
8918
8993
  // server/routes/admin/browser-iframe.ts
8919
8994
  var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
@@ -8930,8 +9005,8 @@ function asString(v, max = 500) {
8930
9005
  function asNumber(v) {
8931
9006
  return typeof v === "number" && Number.isFinite(v) ? v : void 0;
8932
9007
  }
8933
- var app19 = new Hono();
8934
- app19.post("/event", async (c) => {
9008
+ var app20 = new Hono();
9009
+ app20.post("/event", async (c) => {
8935
9010
  let body = {};
8936
9011
  try {
8937
9012
  body = await c.req.json();
@@ -8952,7 +9027,7 @@ app19.post("/event", async (c) => {
8952
9027
  });
8953
9028
  return c.body(null, 204);
8954
9029
  });
8955
- var browser_iframe_default = app19;
9030
+ var browser_iframe_default = app20;
8956
9031
 
8957
9032
  // app/lib/cdp-client.ts
8958
9033
  var CDP_HOST = "127.0.0.1";
@@ -8995,8 +9070,8 @@ async function cdpNavigateNewTab(url, opts = {}) {
8995
9070
  }
8996
9071
 
8997
9072
  // server/routes/admin/device-browser.ts
8998
- var app20 = new Hono();
8999
- app20.post("/navigate", async (c) => {
9073
+ var app21 = new Hono();
9074
+ app21.post("/navigate", async (c) => {
9000
9075
  const TAG19 = "[device-url:click]";
9001
9076
  let body;
9002
9077
  try {
@@ -9083,7 +9158,7 @@ app20.post("/navigate", async (c) => {
9083
9158
  targetId: outcome.targetId
9084
9159
  });
9085
9160
  });
9086
- var device_browser_default = app20;
9161
+ var device_browser_default = app21;
9087
9162
 
9088
9163
  // server/routes/admin/events.ts
9089
9164
  var ALLOWED_EVENTS2 = /* @__PURE__ */ new Set([
@@ -9092,8 +9167,8 @@ var ALLOWED_EVENTS2 = /* @__PURE__ */ new Set([
9092
9167
  "device-url:vnc-surface-shown",
9093
9168
  "device-url:malformed"
9094
9169
  ]);
9095
- var app21 = new Hono();
9096
- app21.post("/", async (c) => {
9170
+ var app22 = new Hono();
9171
+ app22.post("/", async (c) => {
9097
9172
  const TAG19 = "[admin:events]";
9098
9173
  let body;
9099
9174
  try {
@@ -9124,7 +9199,7 @@ app21.post("/", async (c) => {
9124
9199
  console.error(`[${event}] ${formatted}`);
9125
9200
  return c.json({ ok: true });
9126
9201
  });
9127
- var events_default = app21;
9202
+ var events_default = app22;
9128
9203
 
9129
9204
  // server/routes/admin/cloudflare.ts
9130
9205
  import { homedir as homedir2 } from "os";
@@ -9242,7 +9317,7 @@ function validateBody(body) {
9242
9317
  }
9243
9318
  return null;
9244
9319
  }
9245
- var app22 = new Hono();
9320
+ var app23 = new Hono();
9246
9321
  function fieldFromReason(reason) {
9247
9322
  switch (reason) {
9248
9323
  case "not-signed-in":
@@ -9263,15 +9338,15 @@ function fieldFromReason(reason) {
9263
9338
  return "script";
9264
9339
  }
9265
9340
  }
9266
- app22.get("/domains", requireAdminSession, async (c) => {
9341
+ app23.get("/domains", requireAdminSession, async (c) => {
9267
9342
  const started = Date.now();
9268
- const sessionKey = c.var.sessionKey;
9343
+ const cacheKey = c.var.cacheKey;
9269
9344
  let correlationId;
9270
- let sessionKeyTail;
9345
+ let cacheKeyTail;
9271
9346
  let streamLogPath;
9272
9347
  function tag() {
9273
9348
  if (!correlationId) return "";
9274
- return ` conversationId=${correlationId} session_key=${sessionKeyTail ?? "unknown"}`;
9349
+ return ` conversationId=${correlationId} session_key=${cacheKeyTail ?? "unknown"}`;
9275
9350
  }
9276
9351
  function log(line) {
9277
9352
  console.log(`[cloudflare-domains] ${line}${tag()}`);
@@ -9299,9 +9374,9 @@ app22.get("/domains", requireAdminSession, async (c) => {
9299
9374
  };
9300
9375
  return c.json(body, 200);
9301
9376
  }
9302
- const accountId = getAccountIdForSession(sessionKey);
9303
- correlationId = getConversationIdForSession(sessionKey);
9304
- sessionKeyTail = sessionKey.slice(-8);
9377
+ const accountId = getAccountIdForSession(cacheKey);
9378
+ correlationId = getConversationIdForSession(cacheKey);
9379
+ cacheKeyTail = cacheKey.slice(-8);
9305
9380
  if (!accountId) return err("session", "No account bound to session \u2014 refresh chat.");
9306
9381
  if (!correlationId) return err("session", "No active conversation for session \u2014 refresh chat.");
9307
9382
  streamLogPath = streamLogPathFor(accountId, correlationId).streamLogPath;
@@ -9358,15 +9433,15 @@ ${result.stderr}` : ""}`;
9358
9433
  return c.json(success, 200);
9359
9434
  });
9360
9435
  var TUNNELS_TIMEOUT_MS = 30 * 1e3;
9361
- app22.get("/tunnels", requireAdminSession, async (c) => {
9436
+ app23.get("/tunnels", requireAdminSession, async (c) => {
9362
9437
  const started = Date.now();
9363
- const sessionKey = c.var.sessionKey;
9438
+ const cacheKey = c.var.cacheKey;
9364
9439
  let correlationId;
9365
- let sessionKeyTail;
9440
+ let cacheKeyTail;
9366
9441
  let streamLogPath;
9367
9442
  function tag() {
9368
9443
  if (!correlationId) return "";
9369
- return ` conversationId=${correlationId} session_key=${sessionKeyTail ?? "unknown"}`;
9444
+ return ` conversationId=${correlationId} session_key=${cacheKeyTail ?? "unknown"}`;
9370
9445
  }
9371
9446
  function log(line) {
9372
9447
  console.log(`[cloudflare-tunnels] ${line}${tag()}`);
@@ -9391,9 +9466,9 @@ app22.get("/tunnels", requireAdminSession, async (c) => {
9391
9466
  const body = { ok: false, field, message, output, defaultName, correlationId, streamLogPath };
9392
9467
  return c.json(body, 200);
9393
9468
  }
9394
- const accountId = getAccountIdForSession(sessionKey);
9395
- correlationId = getConversationIdForSession(sessionKey);
9396
- sessionKeyTail = sessionKey.slice(-8);
9469
+ const accountId = getAccountIdForSession(cacheKey);
9470
+ correlationId = getConversationIdForSession(cacheKey);
9471
+ cacheKeyTail = cacheKey.slice(-8);
9397
9472
  if (!accountId) return err("session", "No account bound to session \u2014 refresh chat.");
9398
9473
  if (!correlationId) return err("session", "No active conversation for session \u2014 refresh chat.");
9399
9474
  streamLogPath = streamLogPathFor(accountId, correlationId).streamLogPath;
@@ -9454,15 +9529,15 @@ ${result.stderr}` : ""}`;
9454
9529
  const success = { ok: true, tunnels, defaultName, correlationId, streamLogPath };
9455
9530
  return c.json(success, 200);
9456
9531
  });
9457
- app22.post("/setup", requireAdminSession, async (c) => {
9532
+ app23.post("/setup", requireAdminSession, async (c) => {
9458
9533
  const started = Date.now();
9459
- const sessionKey = c.var.sessionKey;
9534
+ const cacheKey = c.var.cacheKey;
9460
9535
  let correlationId;
9461
- let sessionKeyTail;
9536
+ let cacheKeyTail;
9462
9537
  let streamLogPath;
9463
9538
  function tag() {
9464
9539
  if (!correlationId) return "";
9465
- return ` conversationId=${correlationId} session_key=${sessionKeyTail ?? "unknown"}`;
9540
+ return ` conversationId=${correlationId} session_key=${cacheKeyTail ?? "unknown"}`;
9466
9541
  }
9467
9542
  function log(line) {
9468
9543
  console.log(`[cloudflare-setup] ${line}${tag()}`);
@@ -9498,9 +9573,9 @@ app22.post("/setup", requireAdminSession, async (c) => {
9498
9573
  } catch {
9499
9574
  return err("request", "Invalid JSON body");
9500
9575
  }
9501
- const accountId = getAccountIdForSession(sessionKey);
9502
- correlationId = getConversationIdForSession(sessionKey);
9503
- sessionKeyTail = sessionKey.slice(-8);
9576
+ const accountId = getAccountIdForSession(cacheKey);
9577
+ correlationId = getConversationIdForSession(cacheKey);
9578
+ cacheKeyTail = cacheKey.slice(-8);
9504
9579
  if (!accountId) {
9505
9580
  return err("request", "No account bound to session \u2014 refresh chat.");
9506
9581
  }
@@ -9553,7 +9628,7 @@ app22.post("/setup", requireAdminSession, async (c) => {
9553
9628
  try {
9554
9629
  cloudflareTask = await openCloudflareTask({
9555
9630
  accountId,
9556
- conversationKey: sessionKey,
9631
+ conversationId: correlationId,
9557
9632
  inputsProvided: Object.keys(taskInputs),
9558
9633
  inputs: taskInputs,
9559
9634
  inputSchema: CLOUDFLARE_SETUP_FORM_SCHEMA,
@@ -9601,7 +9676,7 @@ app22.post("/setup", requireAdminSession, async (c) => {
9601
9676
  taskId: cloudflareTask.taskId,
9602
9677
  taskElementId: cloudflareTask.taskElementId,
9603
9678
  accountId,
9604
- conversationKey: sessionKey,
9679
+ conversationId: correlationId,
9605
9680
  status: "failed",
9606
9681
  errorMessage: CLOUDFLARE_TASK_DIAGNOSTICS.endpointDiedPreReconcile
9607
9682
  });
@@ -9618,7 +9693,7 @@ app22.post("/setup", requireAdminSession, async (c) => {
9618
9693
  taskId: cloudflareTask.taskId,
9619
9694
  taskElementId: cloudflareTask.taskElementId,
9620
9695
  accountId,
9621
- conversationKey: sessionKey,
9696
+ conversationId: correlationId,
9622
9697
  status: "failed",
9623
9698
  errorMessage: `${CLOUDFLARE_TASK_DIAGNOSTICS.scriptExitedNonzero} exit=${status.execMainStatus}`
9624
9699
  });
@@ -9660,7 +9735,7 @@ app22.post("/setup", requireAdminSession, async (c) => {
9660
9735
  taskId: cloudflareTask.taskId,
9661
9736
  taskElementId: cloudflareTask.taskElementId,
9662
9737
  accountId,
9663
- conversationKey: sessionKey,
9738
+ conversationId: correlationId,
9664
9739
  tunnelId: tunnelState?.tunnelId,
9665
9740
  tunnelName: tunnelState?.tunnelName,
9666
9741
  hostnames: tunnelState ? hostnameRecords : void 0,
@@ -9691,7 +9766,7 @@ actionId: ${actionId}`,
9691
9766
  };
9692
9767
  return ok(success);
9693
9768
  });
9694
- var cloudflare_default = app22;
9769
+ var cloudflare_default = app23;
9695
9770
 
9696
9771
  // server/routes/admin/files.ts
9697
9772
  import { createReadStream as createReadStream3 } from "fs";
@@ -9751,7 +9826,7 @@ var UNIQUE_KEYS_BY_LABEL = {
9751
9826
  Event: ["eventId"],
9752
9827
  KnowledgeDocument: ["attachmentId"],
9753
9828
  DigitalDocument: ["attachmentId"],
9754
- Conversation: ["conversationId", "sessionKey"],
9829
+ Conversation: ["conversationId"],
9755
9830
  Message: ["messageId"],
9756
9831
  OnboardingState: ["accountId"],
9757
9832
  Workflow: ["workflowId"],
@@ -9972,7 +10047,7 @@ async function restoreNode(params) {
9972
10047
  }
9973
10048
 
9974
10049
  // app/lib/file-delete-cascade.ts
9975
- var UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
10050
+ var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
9976
10051
  function parseAttachmentPath(relPath2) {
9977
10052
  const segments = relPath2.split("/").filter(Boolean);
9978
10053
  if (segments.length !== 4) return null;
@@ -9980,7 +10055,7 @@ function parseAttachmentPath(relPath2) {
9980
10055
  const accountId = segments[1];
9981
10056
  const attachmentId = segments[2];
9982
10057
  const filename = segments[3];
9983
- if (!UUID_RE3.test(accountId) || !UUID_RE3.test(attachmentId)) return null;
10058
+ if (!UUID_RE4.test(accountId) || !UUID_RE4.test(attachmentId)) return null;
9984
10059
  const dot = filename.lastIndexOf(".");
9985
10060
  if (dot === -1) return null;
9986
10061
  const stem = filename.slice(0, dot);
@@ -10052,7 +10127,7 @@ async function cascadeDeleteDocument(params) {
10052
10127
  }
10053
10128
 
10054
10129
  // server/routes/admin/files.ts
10055
- var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
10130
+ var UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
10056
10131
  async function readMeta(absDir, baseName) {
10057
10132
  try {
10058
10133
  const raw = await readFile3(join9(absDir, `${baseName}.meta.json`), "utf8");
@@ -10074,7 +10149,7 @@ async function readAccountNames() {
10074
10149
  return map;
10075
10150
  }
10076
10151
  for (const name of names) {
10077
- if (!UUID_RE4.test(name)) continue;
10152
+ if (!UUID_RE5.test(name)) continue;
10078
10153
  const configPath2 = resolve15(accountsDir, name, "account.json");
10079
10154
  try {
10080
10155
  const raw = await readFile3(configPath2, "utf8");
@@ -10092,7 +10167,7 @@ async function readAccountNames() {
10092
10167
  return map;
10093
10168
  }
10094
10169
  async function enrich(absolute, entry, accountNames) {
10095
- if (entry.kind === "directory" && UUID_RE4.test(entry.name)) {
10170
+ if (entry.kind === "directory" && UUID_RE5.test(entry.name)) {
10096
10171
  const meta = await readMeta(join9(absolute, entry.name), entry.name);
10097
10172
  if (meta?.filename) {
10098
10173
  entry.displayName = meta.filename;
@@ -10108,7 +10183,7 @@ async function enrich(absolute, entry, accountNames) {
10108
10183
  if (entry.kind === "file") {
10109
10184
  const dot = entry.name.lastIndexOf(".");
10110
10185
  const base = dot === -1 ? entry.name : entry.name.slice(0, dot);
10111
- if (UUID_RE4.test(base)) {
10186
+ if (UUID_RE5.test(base)) {
10112
10187
  const meta = await readMeta(absolute, base);
10113
10188
  if (meta?.filename) {
10114
10189
  entry.displayName = meta.filename;
@@ -10120,14 +10195,14 @@ async function enrich(absolute, entry, accountNames) {
10120
10195
  function buildDisplayPath(relPath2, accountNames) {
10121
10196
  if (relPath2 === "." || relPath2 === "") return [];
10122
10197
  return relPath2.split("/").filter(Boolean).map((seg) => {
10123
- const dn = UUID_RE4.test(seg) ? accountNames.get(seg) : void 0;
10198
+ const dn = UUID_RE5.test(seg) ? accountNames.get(seg) : void 0;
10124
10199
  return dn ? { name: seg, displayName: dn } : { name: seg };
10125
10200
  });
10126
10201
  }
10127
- var app23 = new Hono();
10128
- app23.get("/", requireAdminSession, async (c) => {
10129
- const sessionKey = c.var.sessionKey;
10130
- if (!getAccountIdForSession(sessionKey)) {
10202
+ var app24 = new Hono();
10203
+ app24.get("/", requireAdminSession, async (c) => {
10204
+ const cacheKey = c.var.cacheKey;
10205
+ if (!getAccountIdForSession(cacheKey)) {
10131
10206
  console.error(`[data] auth-rejected endpoint="/api/admin/files" reason="no account for session"`);
10132
10207
  return c.json({ error: "Account not found for session" }, 401);
10133
10208
  }
@@ -10148,7 +10223,7 @@ app23.get("/", requireAdminSession, async (c) => {
10148
10223
  const names = await readdir2(absolute);
10149
10224
  const entries = [];
10150
10225
  for (const name of names) {
10151
- if (UUID_RE4.test(name.replace(/\.meta\.json$/, "")) && name.endsWith(".meta.json")) {
10226
+ if (UUID_RE5.test(name.replace(/\.meta\.json$/, "")) && name.endsWith(".meta.json")) {
10152
10227
  continue;
10153
10228
  }
10154
10229
  try {
@@ -10186,9 +10261,9 @@ app23.get("/", requireAdminSession, async (c) => {
10186
10261
  return c.json({ error: message }, 500);
10187
10262
  }
10188
10263
  });
10189
- app23.get("/download", requireAdminSession, async (c) => {
10190
- const sessionKey = c.var.sessionKey;
10191
- if (!getAccountIdForSession(sessionKey)) {
10264
+ app24.get("/download", requireAdminSession, async (c) => {
10265
+ const cacheKey = c.var.cacheKey;
10266
+ if (!getAccountIdForSession(cacheKey)) {
10192
10267
  console.error(`[data] auth-rejected endpoint="/api/admin/files/download" reason="no account for session"`);
10193
10268
  return c.json({ error: "Account not found for session" }, 401);
10194
10269
  }
@@ -10234,9 +10309,9 @@ app23.get("/download", requireAdminSession, async (c) => {
10234
10309
  return c.json({ error: message }, 500);
10235
10310
  }
10236
10311
  });
10237
- app23.post("/upload", requireAdminSession, async (c) => {
10238
- const sessionKey = c.var.sessionKey;
10239
- const accountId = getAccountIdForSession(sessionKey);
10312
+ app24.post("/upload", requireAdminSession, async (c) => {
10313
+ const cacheKey = c.var.cacheKey;
10314
+ const accountId = getAccountIdForSession(cacheKey);
10240
10315
  if (!accountId) {
10241
10316
  console.error(`[data] auth-rejected endpoint="/api/admin/files/upload" reason="no account for session"`);
10242
10317
  return c.json({ error: "Account not found for session" }, 401);
@@ -10292,9 +10367,9 @@ app23.post("/upload", requireAdminSession, async (c) => {
10292
10367
  mimeType: file.type
10293
10368
  });
10294
10369
  });
10295
- app23.delete("/", requireAdminSession, async (c) => {
10296
- const sessionKey = c.var.sessionKey;
10297
- const accountId = getAccountIdForSession(sessionKey);
10370
+ app24.delete("/", requireAdminSession, async (c) => {
10371
+ const cacheKey = c.var.cacheKey;
10372
+ const accountId = getAccountIdForSession(cacheKey);
10298
10373
  if (!accountId) {
10299
10374
  console.error(`[data] auth-rejected endpoint="DELETE /api/admin/files" reason="no account for session"`);
10300
10375
  return c.json({ error: "Account not found for session" }, 401);
@@ -10325,7 +10400,7 @@ app23.delete("/", requireAdminSession, async (c) => {
10325
10400
  }
10326
10401
  const dot = base.lastIndexOf(".");
10327
10402
  const stem = dot === -1 ? base : base.slice(0, dot);
10328
- const sidecarPath = UUID_RE4.test(stem) && base !== `${stem}.meta.json` ? join9(dirname6(absolute), `${stem}.meta.json`) : null;
10403
+ const sidecarPath = UUID_RE5.test(stem) && base !== `${stem}.meta.json` ? join9(dirname6(absolute), `${stem}.meta.json`) : null;
10329
10404
  await unlink2(absolute);
10330
10405
  if (sidecarPath) {
10331
10406
  try {
@@ -10359,7 +10434,7 @@ app23.delete("/", requireAdminSession, async (c) => {
10359
10434
  return c.json({ error: message }, 500);
10360
10435
  }
10361
10436
  });
10362
- var files_default = app23;
10437
+ var files_default = app24;
10363
10438
 
10364
10439
  // ../lib/graph-search/src/index.ts
10365
10440
  var import_dist = __toESM(require_dist());
@@ -10739,14 +10814,14 @@ var MAX_LIMIT = 2e3;
10739
10814
  var DEFAULT_VECTOR_THRESHOLD = 0.82;
10740
10815
  var MESSAGE_FAMILY_LABELS = ["Message", "UserMessage", "AssistantMessage", "WhatsAppMessage"];
10741
10816
  var CONVERSATION_PARENT_LABELS = /* @__PURE__ */ new Set(["AdminConversation", "PublicConversation"]);
10742
- var app24 = new Hono();
10743
- app24.get("/", requireAdminSession, async (c) => {
10744
- const sessionKey = c.var.sessionKey;
10817
+ var app25 = new Hono();
10818
+ app25.get("/", requireAdminSession, async (c) => {
10819
+ const cacheKey = c.var.cacheKey;
10745
10820
  const q = (c.req.query("q") ?? "").trim();
10746
10821
  const rawLimit = c.req.query("limit");
10747
10822
  const rawLabels = c.req.query("labels");
10748
10823
  const rawThreshold = c.req.query("threshold");
10749
- const accountId = getAccountIdForSession(sessionKey);
10824
+ const accountId = getAccountIdForSession(cacheKey);
10750
10825
  if (!accountId) {
10751
10826
  console.error(`[graph-search] auth-rejected endpoint="/api/admin/graph-search" reason="no account for session"`);
10752
10827
  return c.json({ error: "Account not found for session" }, 401);
@@ -10871,7 +10946,7 @@ app24.get("/", requireAdminSession, async (c) => {
10871
10946
  await session.close();
10872
10947
  }
10873
10948
  });
10874
- var graph_search_default = app24;
10949
+ var graph_search_default = app25;
10875
10950
 
10876
10951
  // server/routes/admin/graph-subgraph.ts
10877
10952
  import neo4j2 from "neo4j-driver";
@@ -11027,12 +11102,12 @@ var STRIPPED_PROPERTIES = /* @__PURE__ */ new Set([
11027
11102
  "passwordHash",
11028
11103
  "magicToken",
11029
11104
  "otpCode",
11030
- "sessionKey"
11105
+ "cacheKey"
11031
11106
  ]);
11032
- var app25 = new Hono();
11033
- app25.get("/", requireAdminSession, async (c) => {
11034
- const sessionKey = c.var.sessionKey;
11035
- const accountId = getAccountIdForSession(sessionKey);
11107
+ var app26 = new Hono();
11108
+ app26.get("/", requireAdminSession, async (c) => {
11109
+ const cacheKey = c.var.cacheKey;
11110
+ const accountId = getAccountIdForSession(cacheKey);
11036
11111
  if (!accountId) {
11037
11112
  console.error('[graph-page] auth-rejected reason="no account for session"');
11038
11113
  return c.json({ error: "Account not found for session" }, 401);
@@ -11613,14 +11688,14 @@ function pruneNode(node, warnedClasses, conversationWarnings) {
11613
11688
  }
11614
11689
  return trashed ? { id: node.id, labels, properties, trashed: true } : { id: node.id, labels, properties };
11615
11690
  }
11616
- var graph_subgraph_default = app25;
11691
+ var graph_subgraph_default = app26;
11617
11692
 
11618
11693
  // server/routes/admin/graph-delete.ts
11619
11694
  var ALLOWED_BY = ["graph-page", "graph-drag-trash"];
11620
- var app26 = new Hono();
11621
- app26.post("/", requireAdminSession, async (c) => {
11622
- const sessionKey = c.var.sessionKey;
11623
- const accountId = getAccountIdForSession(sessionKey);
11695
+ var app27 = new Hono();
11696
+ app27.post("/", requireAdminSession, async (c) => {
11697
+ const cacheKey = c.var.cacheKey;
11698
+ const accountId = getAccountIdForSession(cacheKey);
11624
11699
  if (!accountId) {
11625
11700
  console.error('[graph-page] delete auth-rejected reason="no account for session"');
11626
11701
  return c.json({ error: "Account not found for session" }, 401);
@@ -11689,13 +11764,13 @@ app26.post("/", requireAdminSession, async (c) => {
11689
11764
  }
11690
11765
  }
11691
11766
  });
11692
- var graph_delete_default = app26;
11767
+ var graph_delete_default = app27;
11693
11768
 
11694
11769
  // server/routes/admin/graph-restore.ts
11695
- var app27 = new Hono();
11696
- app27.post("/", requireAdminSession, async (c) => {
11697
- const sessionKey = c.var.sessionKey;
11698
- const accountId = getAccountIdForSession(sessionKey);
11770
+ var app28 = new Hono();
11771
+ app28.post("/", requireAdminSession, async (c) => {
11772
+ const cacheKey = c.var.cacheKey;
11773
+ const accountId = getAccountIdForSession(cacheKey);
11699
11774
  if (!accountId) {
11700
11775
  console.error('[graph-page] restore auth-rejected reason="no account for session"');
11701
11776
  return c.json({ error: "Account not found for session" }, 401);
@@ -11757,13 +11832,13 @@ app27.post("/", requireAdminSession, async (c) => {
11757
11832
  }
11758
11833
  }
11759
11834
  });
11760
- var graph_restore_default = app27;
11835
+ var graph_restore_default = app28;
11761
11836
 
11762
11837
  // server/routes/admin/graph-labels-in-graph.ts
11763
- var app28 = new Hono();
11764
- app28.get("/", requireAdminSession, async (c) => {
11765
- const sessionKey = c.var.sessionKey;
11766
- const accountId = getAccountIdForSession(sessionKey);
11838
+ var app29 = new Hono();
11839
+ app29.get("/", requireAdminSession, async (c) => {
11840
+ const cacheKey = c.var.cacheKey;
11841
+ const accountId = getAccountIdForSession(cacheKey);
11767
11842
  if (!accountId) {
11768
11843
  console.error('[graph-page] labels-in-graph-rejected reason="no account for session"');
11769
11844
  return c.json({ error: "Account not found for session" }, 401);
@@ -11827,14 +11902,14 @@ var LABELS_IN_GRAPH_CYPHER = `
11827
11902
  sum(halfEdges) AS relDegree
11828
11903
  RETURN label, nodeCount, relDegree
11829
11904
  `;
11830
- var graph_labels_in_graph_default = app28;
11905
+ var graph_labels_in_graph_default = app29;
11831
11906
 
11832
11907
  // server/routes/admin/graph-default-view.ts
11833
- var app29 = new Hono();
11834
- app29.get("/", requireAdminSession, async (c) => {
11835
- const sessionKey = c.var.sessionKey;
11836
- const accountId = getAccountIdForSession(sessionKey);
11837
- const userId = getUserIdForSession(sessionKey);
11908
+ var app30 = new Hono();
11909
+ app30.get("/", requireAdminSession, async (c) => {
11910
+ const cacheKey = c.var.cacheKey;
11911
+ const accountId = getAccountIdForSession(cacheKey);
11912
+ const userId = getUserIdForSession(cacheKey);
11838
11913
  if (!accountId || !userId) {
11839
11914
  console.error('[graph-page] default-view-rejected reason="missing account or user context"');
11840
11915
  return c.json({ error: "Account and user context required for default view" }, 401);
@@ -11869,10 +11944,10 @@ app29.get("/", requireAdminSession, async (c) => {
11869
11944
  }
11870
11945
  }
11871
11946
  });
11872
- app29.put("/", requireAdminSession, async (c) => {
11873
- const sessionKey = c.var.sessionKey;
11874
- const accountId = getAccountIdForSession(sessionKey);
11875
- const userId = getUserIdForSession(sessionKey);
11947
+ app30.put("/", requireAdminSession, async (c) => {
11948
+ const cacheKey = c.var.cacheKey;
11949
+ const accountId = getAccountIdForSession(cacheKey);
11950
+ const userId = getUserIdForSession(cacheKey);
11876
11951
  if (!accountId || !userId) {
11877
11952
  console.error('[graph-page] default-view-rejected reason="missing account or user context"');
11878
11953
  return c.json({ error: "Account and user context required for default view" }, 401);
@@ -11958,11 +12033,11 @@ var WRITE_CYPHER = `
11958
12033
  p.updatedAt = $updatedAt
11959
12034
  RETURN p.labels AS labels
11960
12035
  `;
11961
- var graph_default_view_default = app29;
12036
+ var graph_default_view_default = app30;
11962
12037
 
11963
12038
  // server/routes/admin/file-attach.ts
11964
- var app30 = new Hono();
11965
- app30.post("/", async (c) => {
12039
+ var app31 = new Hono();
12040
+ app31.post("/", async (c) => {
11966
12041
  try {
11967
12042
  const body = await c.req.json();
11968
12043
  const { filePath, accountId } = body;
@@ -11985,11 +12060,11 @@ app30.post("/", async (c) => {
11985
12060
  return c.json({ error: message }, 500);
11986
12061
  }
11987
12062
  });
11988
- var file_attach_default = app30;
12063
+ var file_attach_default = app31;
11989
12064
 
11990
12065
  // server/routes/admin/adherence.ts
11991
- var app31 = new Hono();
11992
- app31.get("/", requireAdminSession, async (c) => {
12066
+ var app32 = new Hono();
12067
+ app32.get("/", requireAdminSession, async (c) => {
11993
12068
  const agent = c.req.query("agent") ?? "admin";
11994
12069
  const includeBlock = c.req.query("block") === "1";
11995
12070
  const account = resolveAccount();
@@ -12010,7 +12085,7 @@ app31.get("/", requireAdminSession, async (c) => {
12010
12085
  return c.json({ error: "Failed to read adherence ledger", agent }, 500);
12011
12086
  }
12012
12087
  });
12013
- var adherence_default = app31;
12088
+ var adherence_default = app32;
12014
12089
 
12015
12090
  // server/routes/admin/sidebar-artefacts.ts
12016
12091
  import neo4j3 from "neo4j-driver";
@@ -12020,10 +12095,10 @@ import { existsSync as existsSync18 } from "fs";
12020
12095
  var LIMIT = 50;
12021
12096
  var TEXT_MIME_PREFIXES = ["text/", "application/json", "application/markdown"];
12022
12097
  var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
12023
- var app32 = new Hono();
12024
- app32.get("/", requireAdminSession, async (c) => {
12025
- const sessionKey = c.var.sessionKey;
12026
- const accountId = getAccountIdForSession(sessionKey);
12098
+ var app33 = new Hono();
12099
+ app33.get("/", requireAdminSession, async (c) => {
12100
+ const cacheKey = c.var.cacheKey;
12101
+ const accountId = getAccountIdForSession(cacheKey);
12027
12102
  if (!accountId) {
12028
12103
  return c.json({ error: "Account not found for session" }, 401);
12029
12104
  }
@@ -12227,18 +12302,18 @@ function isWithin(target, root) {
12227
12302
  const rel = relative2(root, target);
12228
12303
  return !rel.startsWith("..") && !isAbsolute(rel);
12229
12304
  }
12230
- var sidebar_artefacts_default = app32;
12305
+ var sidebar_artefacts_default = app33;
12231
12306
 
12232
12307
  // server/routes/admin/sidebar-artefact-save.ts
12233
12308
  import { mkdir as mkdir3, readdir as readdir4, stat as stat5, writeFile as writeFile4 } from "fs/promises";
12234
12309
  import { resolve as resolve17 } from "path";
12235
12310
  import { existsSync as existsSync19 } from "fs";
12236
12311
  var ADMIN_AGENT_FILES2 = /* @__PURE__ */ new Set(["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"]);
12237
- var UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
12238
- var app33 = new Hono();
12239
- app33.post("/", requireAdminSession, async (c) => {
12240
- const sessionKey = c.var.sessionKey;
12241
- const accountId = getAccountIdForSession(sessionKey);
12312
+ var UUID_RE6 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
12313
+ var app34 = new Hono();
12314
+ app34.post("/", requireAdminSession, async (c) => {
12315
+ const cacheKey = c.var.cacheKey;
12316
+ const accountId = getAccountIdForSession(cacheKey);
12242
12317
  if (!accountId) return c.json({ error: "Account not found for session" }, 401);
12243
12318
  const body = await safeJson(c);
12244
12319
  if (!body || typeof body.id !== "string" || typeof body.content !== "string") {
@@ -12294,7 +12369,7 @@ async function resolveSavePath(id, accountId, accountDir) {
12294
12369
  }
12295
12370
  return { kind: "admin-template", path: resolve17(parent, filename) };
12296
12371
  }
12297
- if (UUID_RE5.test(id)) {
12372
+ if (UUID_RE6.test(id)) {
12298
12373
  const dir = resolve17(ATTACHMENTS_ROOT, accountId, id);
12299
12374
  if (!existsSync19(dir)) {
12300
12375
  return { kind: "reject", status: 400, reason: "not-found" };
@@ -12316,20 +12391,20 @@ async function resolveSavePath(id, accountId, accountDir) {
12316
12391
  function relPath(absPath, root) {
12317
12392
  return absPath.startsWith(root) ? absPath.slice(root.length + 1) : absPath;
12318
12393
  }
12319
- var sidebar_artefact_save_default = app33;
12394
+ var sidebar_artefact_save_default = app34;
12320
12395
 
12321
12396
  // server/routes/admin/sidebar-artefact-content.ts
12322
12397
  import { readFile as readFile5, readdir as readdir5 } from "fs/promises";
12323
12398
  import { existsSync as existsSync20 } from "fs";
12324
12399
  import { resolve as resolve18 } from "path";
12325
- var UUID_RE6 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
12326
- var app34 = new Hono();
12327
- app34.get("/", requireAdminSession, async (c) => {
12328
- const sessionKey = c.var.sessionKey;
12329
- const accountId = getAccountIdForSession(sessionKey);
12400
+ var UUID_RE7 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
12401
+ var app35 = new Hono();
12402
+ app35.get("/", requireAdminSession, async (c) => {
12403
+ const cacheKey = c.var.cacheKey;
12404
+ const accountId = getAccountIdForSession(cacheKey);
12330
12405
  if (!accountId) return new Response("Unauthorized", { status: 401 });
12331
12406
  const id = c.req.query("id") ?? "";
12332
- if (!UUID_RE6.test(id)) {
12407
+ if (!UUID_RE7.test(id)) {
12333
12408
  console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
12334
12409
  return new Response("Not found", { status: 404 });
12335
12410
  }
@@ -12365,7 +12440,7 @@ app34.get("/", requireAdminSession, async (c) => {
12365
12440
  }
12366
12441
  });
12367
12442
  });
12368
- var sidebar_artefact_content_default = app34;
12443
+ var sidebar_artefact_content_default = app35;
12369
12444
 
12370
12445
  // server/routes/admin/health.ts
12371
12446
  import { existsSync as existsSync21, readFileSync as readFileSync16 } from "fs";
@@ -12410,8 +12485,8 @@ async function probeConversationDb() {
12410
12485
  });
12411
12486
  }
12412
12487
  }
12413
- var app35 = new Hono();
12414
- app35.get("/", async (c) => {
12488
+ var app36 = new Hono();
12489
+ app36.get("/", async (c) => {
12415
12490
  const version = readVersion();
12416
12491
  const probe = await probeConversationDb();
12417
12492
  const uptimeMs = Date.now() - new Date(PROCESS_STARTED_AT).getTime();
@@ -12433,37 +12508,38 @@ app35.get("/", async (c) => {
12433
12508
  uptimeMs
12434
12509
  });
12435
12510
  });
12436
- var health_default2 = app35;
12511
+ var health_default2 = app36;
12437
12512
 
12438
12513
  // server/routes/admin/index.ts
12439
- var app36 = new Hono();
12440
- app36.route("/session", session_default2);
12441
- app36.route("/chat", chat_default2);
12442
- app36.route("/compact", compact_default);
12443
- app36.route("/logs", logs_default);
12444
- app36.route("/claude-info", claude_info_default);
12445
- app36.route("/attachment", attachment_default);
12446
- app36.route("/agents", agents_default);
12447
- app36.route("/sessions", sessions_default);
12448
- app36.route("/browser", browser_default);
12449
- app36.route("/browser-iframe", browser_iframe_default);
12450
- app36.route("/device-browser", device_browser_default);
12451
- app36.route("/events", events_default);
12452
- app36.route("/cloudflare", cloudflare_default);
12453
- app36.route("/files", files_default);
12454
- app36.route("/graph-search", graph_search_default);
12455
- app36.route("/graph-subgraph", graph_subgraph_default);
12456
- app36.route("/graph-delete", graph_delete_default);
12457
- app36.route("/graph-restore", graph_restore_default);
12458
- app36.route("/graph-labels-in-graph", graph_labels_in_graph_default);
12459
- app36.route("/graph-default-view", graph_default_view_default);
12460
- app36.route("/file-attach", file_attach_default);
12461
- app36.route("/adherence", adherence_default);
12462
- app36.route("/sidebar-artefacts", sidebar_artefacts_default);
12463
- app36.route("/sidebar-artefact-save", sidebar_artefact_save_default);
12464
- app36.route("/sidebar-artefact-content", sidebar_artefact_content_default);
12465
- app36.route("/health-brand", health_default2);
12466
- var admin_default = app36;
12514
+ var app37 = new Hono();
12515
+ app37.route("/session", session_default2);
12516
+ app37.route("/chat", chat_default2);
12517
+ app37.route("/chat-failure", chat_failure_default);
12518
+ app37.route("/compact", compact_default);
12519
+ app37.route("/logs", logs_default);
12520
+ app37.route("/claude-info", claude_info_default);
12521
+ app37.route("/attachment", attachment_default);
12522
+ app37.route("/agents", agents_default);
12523
+ app37.route("/sessions", sessions_default);
12524
+ app37.route("/browser", browser_default);
12525
+ app37.route("/browser-iframe", browser_iframe_default);
12526
+ app37.route("/device-browser", device_browser_default);
12527
+ app37.route("/events", events_default);
12528
+ app37.route("/cloudflare", cloudflare_default);
12529
+ app37.route("/files", files_default);
12530
+ app37.route("/graph-search", graph_search_default);
12531
+ app37.route("/graph-subgraph", graph_subgraph_default);
12532
+ app37.route("/graph-delete", graph_delete_default);
12533
+ app37.route("/graph-restore", graph_restore_default);
12534
+ app37.route("/graph-labels-in-graph", graph_labels_in_graph_default);
12535
+ app37.route("/graph-default-view", graph_default_view_default);
12536
+ app37.route("/file-attach", file_attach_default);
12537
+ app37.route("/adherence", adherence_default);
12538
+ app37.route("/sidebar-artefacts", sidebar_artefacts_default);
12539
+ app37.route("/sidebar-artefact-save", sidebar_artefact_save_default);
12540
+ app37.route("/sidebar-artefact-content", sidebar_artefact_content_default);
12541
+ app37.route("/health-brand", health_default2);
12542
+ var admin_default = app37;
12467
12543
 
12468
12544
  // server/routes/sites.ts
12469
12545
  import { existsSync as existsSync22, readFileSync as readFileSync17, realpathSync as realpathSync4, statSync as statSync7 } from "fs";
@@ -12498,8 +12574,8 @@ function getExt(p) {
12498
12574
  if (idx < p.lastIndexOf("/")) return "";
12499
12575
  return p.slice(idx).toLowerCase();
12500
12576
  }
12501
- var app37 = new Hono();
12502
- app37.get("/:rel{.*}", (c) => {
12577
+ var app38 = new Hono();
12578
+ app38.get("/:rel{.*}", (c) => {
12503
12579
  const reqPath = c.req.path;
12504
12580
  const rawRel = c.req.param("rel") ?? "";
12505
12581
  const trimmed = rawRel.replace(/^\/+/, "").replace(/\/+$/, "");
@@ -12602,7 +12678,7 @@ app37.get("/:rel{.*}", (c) => {
12602
12678
  "X-Content-Type-Options": "nosniff"
12603
12679
  });
12604
12680
  });
12605
- var sites_default = app37;
12681
+ var sites_default = app38;
12606
12682
 
12607
12683
  // app/lib/graph-health.ts
12608
12684
  var HOUR_MS = 60 * 60 * 1e3;
@@ -12756,9 +12832,9 @@ watchFile(ALIAS_DOMAINS_PATH2, { interval: 2e3 }, () => {
12756
12832
  function isPublicHost(host) {
12757
12833
  return host.startsWith("public.") || aliasDomains.has(host);
12758
12834
  }
12759
- var app38 = new Hono();
12760
- app38.use("*", clientIpMiddleware);
12761
- app38.use("*", async (c, next) => {
12835
+ var app39 = new Hono();
12836
+ app39.use("*", clientIpMiddleware);
12837
+ app39.use("*", async (c, next) => {
12762
12838
  await next();
12763
12839
  c.header("X-Content-Type-Options", "nosniff");
12764
12840
  c.header("Referrer-Policy", "strict-origin-when-cross-origin");
@@ -12768,7 +12844,7 @@ app38.use("*", async (c, next) => {
12768
12844
  );
12769
12845
  });
12770
12846
  var HTTP_LOG_PATHS = /* @__PURE__ */ new Set(["/vnc-viewer.html", "/vnc-popout.html"]);
12771
- app38.use("*", async (c, next) => {
12847
+ app39.use("*", async (c, next) => {
12772
12848
  if (!HTTP_LOG_PATHS.has(c.req.path)) {
12773
12849
  await next();
12774
12850
  return;
@@ -12801,7 +12877,7 @@ var PUBLIC_ALLOWED_PREFIXES = [
12801
12877
  "/sites/"
12802
12878
  ];
12803
12879
  var PUBLIC_ALLOWED_EXACT = ["/favicon.ico"];
12804
- app38.use("*", async (c, next) => {
12880
+ app39.use("*", async (c, next) => {
12805
12881
  const host = (c.req.header("host") ?? "").split(":")[0];
12806
12882
  if (!isPublicHost(host)) {
12807
12883
  await next();
@@ -12841,7 +12917,7 @@ function resolveRemoteAuthOpts() {
12841
12917
  return brandLoginOpts;
12842
12918
  }
12843
12919
  var MAX_LOGIN_BODY = 8 * 1024;
12844
- app38.post("/__remote-auth/login", async (c) => {
12920
+ app39.post("/__remote-auth/login", async (c) => {
12845
12921
  const client = clientFrom(c);
12846
12922
  const clientIp = client.ip || "unknown";
12847
12923
  if (!requestIsTlsTerminated(c)) {
@@ -12886,7 +12962,7 @@ app38.post("/__remote-auth/login", async (c) => {
12886
12962
  }
12887
12963
  });
12888
12964
  });
12889
- app38.get("/__remote-auth/logout", (c) => {
12965
+ app39.get("/__remote-auth/logout", (c) => {
12890
12966
  const client = clientFrom(c);
12891
12967
  const clientIp = client.ip || "unknown";
12892
12968
  console.error(`[remote-auth] logout ip=${clientIp}`);
@@ -12899,7 +12975,7 @@ app38.get("/__remote-auth/logout", (c) => {
12899
12975
  }
12900
12976
  });
12901
12977
  });
12902
- app38.post("/__remote-auth/change-password", async (c) => {
12978
+ app39.post("/__remote-auth/change-password", async (c) => {
12903
12979
  const client = clientFrom(c);
12904
12980
  const clientIp = client.ip || "unknown";
12905
12981
  const rateLimited = checkRateLimit(client);
@@ -12950,13 +13026,13 @@ app38.post("/__remote-auth/change-password", async (c) => {
12950
13026
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "change", changeError: "Failed to save password", redirect }), 200);
12951
13027
  }
12952
13028
  });
12953
- app38.get("/__remote-auth/setup", (c) => {
13029
+ app39.get("/__remote-auth/setup", (c) => {
12954
13030
  if (isRemoteAuthConfigured()) {
12955
13031
  return c.redirect("/");
12956
13032
  }
12957
13033
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup" }), 200);
12958
13034
  });
12959
- app38.post("/__remote-auth/set-initial-password", async (c) => {
13035
+ app39.post("/__remote-auth/set-initial-password", async (c) => {
12960
13036
  if (isRemoteAuthConfigured()) {
12961
13037
  return c.redirect("/");
12962
13038
  }
@@ -12994,10 +13070,10 @@ app38.post("/__remote-auth/set-initial-password", async (c) => {
12994
13070
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
12995
13071
  }
12996
13072
  });
12997
- app38.get("/api/remote-auth/status", (c) => {
13073
+ app39.get("/api/remote-auth/status", (c) => {
12998
13074
  return c.json({ configured: isRemoteAuthConfigured() });
12999
13075
  });
13000
- app38.post("/api/remote-auth/set-password", async (c) => {
13076
+ app39.post("/api/remote-auth/set-password", async (c) => {
13001
13077
  let body;
13002
13078
  try {
13003
13079
  body = await c.req.json();
@@ -13027,9 +13103,9 @@ app38.post("/api/remote-auth/set-password", async (c) => {
13027
13103
  return c.json({ error: "Failed to save password" }, 500);
13028
13104
  }
13029
13105
  });
13030
- app38.route("/api/_client-error", client_error_default);
13106
+ app39.route("/api/_client-error", client_error_default);
13031
13107
  console.log("[client-error-route] mounted");
13032
- app38.use("*", async (c, next) => {
13108
+ app39.use("*", async (c, next) => {
13033
13109
  const host = (c.req.header("host") ?? "").split(":")[0];
13034
13110
  const path2 = c.req.path;
13035
13111
  if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/")) {
@@ -13069,15 +13145,15 @@ app38.use("*", async (c, next) => {
13069
13145
  console.error(`[remote-auth] login required ip=${clientIp} path=${path2} ${disambig}`);
13070
13146
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), redirect: path2 }), 200);
13071
13147
  });
13072
- app38.route("/api/health", health_default);
13073
- app38.route("/api/session", session_default);
13074
- app38.route("/api/chat", chat_default);
13075
- app38.route("/api/group", group_default);
13076
- app38.route("/api/access", access_default);
13077
- app38.route("/api/telegram", telegram_default);
13078
- app38.route("/api/whatsapp", whatsapp_default);
13079
- app38.route("/api/onboarding", onboarding_default);
13080
- app38.route("/api/admin", admin_default);
13148
+ app39.route("/api/health", health_default);
13149
+ app39.route("/api/session", session_default);
13150
+ app39.route("/api/chat", chat_default);
13151
+ app39.route("/api/group", group_default);
13152
+ app39.route("/api/access", access_default);
13153
+ app39.route("/api/telegram", telegram_default);
13154
+ app39.route("/api/whatsapp", whatsapp_default);
13155
+ app39.route("/api/onboarding", onboarding_default);
13156
+ app39.route("/api/admin", admin_default);
13081
13157
  var SAFE_SLUG_RE = /^[a-z][a-z0-9-]{2,49}$/;
13082
13158
  var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
13083
13159
  var IMAGE_MIME = {
@@ -13089,7 +13165,7 @@ var IMAGE_MIME = {
13089
13165
  ".svg": "image/svg+xml",
13090
13166
  ".ico": "image/x-icon"
13091
13167
  };
13092
- app38.get("/agent-assets/:slug/:filename", (c) => {
13168
+ app39.get("/agent-assets/:slug/:filename", (c) => {
13093
13169
  const slug = c.req.param("slug");
13094
13170
  const filename = c.req.param("filename");
13095
13171
  if (!SAFE_SLUG_RE.test(slug)) {
@@ -13124,7 +13200,7 @@ app38.get("/agent-assets/:slug/:filename", (c) => {
13124
13200
  "Cache-Control": "public, max-age=3600"
13125
13201
  });
13126
13202
  });
13127
- app38.get("/generated/:filename", (c) => {
13203
+ app39.get("/generated/:filename", (c) => {
13128
13204
  const filename = c.req.param("filename");
13129
13205
  if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
13130
13206
  console.error(`[generated] serve file=${filename} status=403`);
@@ -13154,7 +13230,7 @@ app38.get("/generated/:filename", (c) => {
13154
13230
  "Cache-Control": "public, max-age=86400"
13155
13231
  });
13156
13232
  });
13157
- app38.route("/sites", sites_default);
13233
+ app39.route("/sites", sites_default);
13158
13234
  var htmlCache = /* @__PURE__ */ new Map();
13159
13235
  var brandLogoPath = "/brand/maxy-monochrome.png";
13160
13236
  var brandIconPath = "/brand/maxy-monochrome.png";
@@ -13291,7 +13367,7 @@ function brandedPublicHtml(agentSlug) {
13291
13367
  function escapeHtml(s) {
13292
13368
  return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
13293
13369
  }
13294
- app38.get("/", (c) => {
13370
+ app39.get("/", (c) => {
13295
13371
  const host = (c.req.header("host") ?? "").split(":")[0];
13296
13372
  if (isPublicHost(host)) {
13297
13373
  const defaultSlug = resolveDefaultSlug();
@@ -13299,12 +13375,12 @@ app38.get("/", (c) => {
13299
13375
  }
13300
13376
  return c.html(cachedHtml("index.html"));
13301
13377
  });
13302
- app38.get("/public", (c) => {
13378
+ app39.get("/public", (c) => {
13303
13379
  const host = (c.req.header("host") ?? "").split(":")[0];
13304
13380
  if (isPublicHost(host)) return c.text("Not found", 404);
13305
13381
  return c.html(cachedHtml("public.html"));
13306
13382
  });
13307
- app38.get("/chat", (c) => {
13383
+ app39.get("/chat", (c) => {
13308
13384
  const host = (c.req.header("host") ?? "").split(":")[0];
13309
13385
  if (isPublicHost(host)) return c.text("Not found", 404);
13310
13386
  return c.html(cachedHtml("public.html"));
@@ -13323,9 +13399,9 @@ async function logViewerFetch(c, next) {
13323
13399
  duration_ms: Date.now() - start
13324
13400
  });
13325
13401
  }
13326
- app38.use("/vnc-viewer.html", logViewerFetch);
13327
- app38.use("/vnc-popout.html", logViewerFetch);
13328
- app38.get("/vnc-popout.html", (c) => {
13402
+ app39.use("/vnc-viewer.html", logViewerFetch);
13403
+ app39.use("/vnc-popout.html", logViewerFetch);
13404
+ app39.get("/vnc-popout.html", (c) => {
13329
13405
  let html = htmlCache.get("vnc-popout.html");
13330
13406
  if (!html) {
13331
13407
  html = readFileSync18(resolve21(process.cwd(), "public", "vnc-popout.html"), "utf-8");
@@ -13338,7 +13414,7 @@ app38.get("/vnc-popout.html", (c) => {
13338
13414
  }
13339
13415
  return c.html(html);
13340
13416
  });
13341
- app38.post("/api/vnc/client-event", async (c) => {
13417
+ app39.post("/api/vnc/client-event", async (c) => {
13342
13418
  let body;
13343
13419
  try {
13344
13420
  body = await c.req.json();
@@ -13359,20 +13435,20 @@ app38.post("/api/vnc/client-event", async (c) => {
13359
13435
  });
13360
13436
  return c.json({ ok: true });
13361
13437
  });
13362
- app38.get("/g/:slug", (c) => {
13438
+ app39.get("/g/:slug", (c) => {
13363
13439
  return c.html(brandedPublicHtml());
13364
13440
  });
13365
- app38.get("/graph", (c) => {
13441
+ app39.get("/graph", (c) => {
13366
13442
  const host = (c.req.header("host") ?? "").split(":")[0];
13367
13443
  if (isPublicHost(host)) return c.text("Not found", 404);
13368
13444
  return c.html(cachedHtml("graph.html"));
13369
13445
  });
13370
- app38.get("/data", (c) => {
13446
+ app39.get("/data", (c) => {
13371
13447
  const host = (c.req.header("host") ?? "").split(":")[0];
13372
13448
  if (isPublicHost(host)) return c.text("Not found", 404);
13373
13449
  return c.html(cachedHtml("data.html"));
13374
13450
  });
13375
- app38.get("/:slug", async (c, next) => {
13451
+ app39.get("/:slug", async (c, next) => {
13376
13452
  const slug = c.req.param("slug");
13377
13453
  if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
13378
13454
  const branding = loadBrandingCache(slug);
@@ -13382,15 +13458,15 @@ app38.get("/:slug", async (c, next) => {
13382
13458
  await next();
13383
13459
  });
13384
13460
  if (brandFaviconPath !== "/favicon.ico") {
13385
- app38.get("/favicon.ico", (c) => {
13461
+ app39.get("/favicon.ico", (c) => {
13386
13462
  c.header("Cache-Control", "public, max-age=300");
13387
13463
  return c.redirect(brandFaviconPath, 302);
13388
13464
  });
13389
13465
  }
13390
- app38.use("/*", serveStatic({ root: "./public" }));
13466
+ app39.use("/*", serveStatic({ root: "./public" }));
13391
13467
  var port = parseInt(process.env.MAXY_UI_INTERNAL_PORT ?? process.env.PORT ?? "19199", 10);
13392
13468
  var hostname = process.env.HOSTNAME ?? "127.0.0.1";
13393
- var httpServer = serve({ fetch: app38.fetch, port, hostname });
13469
+ var httpServer = serve({ fetch: app39.fetch, port, hostname });
13394
13470
  console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
13395
13471
  console.log("[boot] auth-mode summary: oauth=8 api-key=1 (api-key consumer: invokePublicAgent only)");
13396
13472
  var SUBAPP_MANIFEST = [
@@ -13411,7 +13487,7 @@ for (const m of SUBAPP_MANIFEST) {
13411
13487
  }
13412
13488
  try {
13413
13489
  const registered = [];
13414
- for (const r of app38.routes ?? []) {
13490
+ for (const r of app39.routes ?? []) {
13415
13491
  if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
13416
13492
  if (AGENT_SLUG_PATTERN.test(r.path)) {
13417
13493
  registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });
@@ -13443,6 +13519,13 @@ try {
13443
13519
  console.error(`[session] backfill startup failed: ${err instanceof Error ? err.message : String(err)}`);
13444
13520
  }
13445
13521
  })();
13522
+ (async () => {
13523
+ try {
13524
+ await backfillConversationChannelAddress();
13525
+ } catch (err) {
13526
+ console.error(`[session-985] channelAddress backfill startup failed: ${err instanceof Error ? err.message : String(err)}`);
13527
+ }
13528
+ })();
13446
13529
  (async () => {
13447
13530
  try {
13448
13531
  if (!existsSync23(USERS_FILE)) return;
@@ -13497,15 +13580,14 @@ autoDeliverPremiumPlugins(bootEntitlement?.purchasedPlugins ?? void 0);
13497
13580
  (async () => {
13498
13581
  if (!bootAccount) return;
13499
13582
  try {
13500
- const { recoverRunningCloudflareTasks } = await import("./cloudflare-task-tracker-M7APAYEF.js");
13583
+ const { recoverRunningCloudflareTasks } = await import("./cloudflare-task-tracker-B6FXP3HI.js");
13501
13584
  const result = await recoverRunningCloudflareTasks(
13502
13585
  bootAccount.accountId,
13503
13586
  configDirForWhatsApp,
13504
- // No conversationKey at boot the tracker's writeNodeWithEdges path
13505
- // requires it for the Conversation join, but the recovery path is
13506
- // operating on Tasks whose Conversation is already linked. Pass null
13507
- // and let the tracker pick up the linked Conversation via the Task's
13508
- // existing edge.
13587
+ // Task 985 — no conversationId at boot. The reconciler operates on
13588
+ // Tasks whose Conversation is already linked via RAISED_DURING; the
13589
+ // tracker resolves the conversationId off that edge when present.
13590
+ // Pass null and let the recovery path pick it up.
13509
13591
  null
13510
13592
  );
13511
13593
  if (result.scanned > 0) {
@@ -13587,10 +13669,10 @@ init({
13587
13669
  agentName = resolved.slug;
13588
13670
  }
13589
13671
  let enrichedText = msg.text || msg.mediaPath || msg.mediaType || msg.replyContext ? buildEnrichedText2(msg.text ?? "") : "";
13590
- if (msg.sessionKey) {
13672
+ if (msg.cacheKey) {
13591
13673
  const platformAccountId = bootAccount?.accountId ?? msg.accountId;
13592
- registerSession(msg.sessionKey, msg.agentType, platformAccountId, agentName);
13593
- console.error(`[session] channel session registered: sessionKey=${msg.sessionKey.slice(0, 12)}\u2026 channel=whatsapp accountId=${platformAccountId.slice(0, 8)}\u2026`);
13674
+ registerSession(msg.cacheKey, msg.agentType, platformAccountId, agentName);
13675
+ console.error(`[session] channel session registered: cacheKey=${msg.cacheKey.slice(0, 12)}\u2026 channel=whatsapp accountId=${platformAccountId.slice(0, 8)}\u2026`);
13594
13676
  }
13595
13677
  let gatewayResult;
13596
13678
  if (msg.text) {
@@ -13614,7 +13696,7 @@ init({
13614
13696
  for await (const event of invokeAgent(
13615
13697
  { type: msg.agentType, agentName },
13616
13698
  enrichedText,
13617
- msg.sessionKey,
13699
+ msg.cacheKey,
13618
13700
  [],
13619
13701
  void 0,
13620
13702
  gatewayResult
@@ -13632,7 +13714,7 @@ init({
13632
13714
  for await (const event of invokeAgent(
13633
13715
  { type: msg.agentType, agentName },
13634
13716
  enrichedText,
13635
- msg.sessionKey,
13717
+ msg.cacheKey,
13636
13718
  [],
13637
13719
  void 0,
13638
13720
  gatewayResult