@rubytech/create-maxy 1.0.877 → 1.0.879

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 (44) 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/cloudflare/PLUGIN.md +9 -0
  8. package/payload/platform/plugins/docs/references/platform.md +8 -2
  9. package/payload/platform/templates/agents/admin/IDENTITY.md +6 -0
  10. package/payload/server/chunk-2INJCOYG.js +1373 -0
  11. package/payload/server/chunk-DOIAYD3J.js +2282 -0
  12. package/payload/server/chunk-ICY65BIH.js +11364 -0
  13. package/payload/server/chunk-INI2ED6U.js +2277 -0
  14. package/payload/server/chunk-JTZYXIUW.js +1373 -0
  15. package/payload/server/chunk-LQDUG4II.js +11336 -0
  16. package/payload/server/chunk-Q5J4NI6Q.js +660 -0
  17. package/payload/server/chunk-RP25NRQY.js +660 -0
  18. package/payload/server/client-pool-AIZ5QKFD.js +34 -0
  19. package/payload/server/client-pool-JAM3QHGW.js +34 -0
  20. package/payload/server/cloudflare-task-tracker-B6FXP3HI.js +20 -0
  21. package/payload/server/cloudflare-task-tracker-HUTXJQXO.js +20 -0
  22. package/payload/server/maxy-edge.js +3 -3
  23. package/payload/server/public/assets/{Checkbox-m3yLBLrp.js → Checkbox-CqsIsmEi.js} +1 -1
  24. package/payload/server/public/assets/admin-uVxIhs_u.js +352 -0
  25. package/payload/server/public/assets/data-CH-nQ7oX.js +1 -0
  26. package/payload/server/public/assets/graph-labels-D0qUVHtZ.js +1 -0
  27. package/payload/server/public/assets/graph-mpWDe4rf.js +1 -0
  28. package/payload/server/public/assets/{jsx-runtime-DJwgVAMg.css → jsx-runtime-Cy_HdZWV.css} +1 -1
  29. package/payload/server/public/assets/page-CnyySOZF.js +1 -0
  30. package/payload/server/public/assets/{page-BLRjaAoU.js → page-DcK36vDf.js} +2 -2
  31. package/payload/server/public/assets/public-SXA00FTv.js +5 -0
  32. package/payload/server/public/assets/{useVoiceRecorder-JwwBC5pd.js → useVoiceRecorder-DcByEBLy.js} +1 -1
  33. package/payload/server/public/data.html +5 -5
  34. package/payload/server/public/graph.html +6 -6
  35. package/payload/server/public/index.html +8 -8
  36. package/payload/server/public/public.html +5 -5
  37. package/payload/server/server.js +569 -443
  38. package/payload/server/public/assets/admin-DEm0CCga.js +0 -352
  39. package/payload/server/public/assets/data-BkbjVYwP.js +0 -1
  40. package/payload/server/public/assets/graph-Cic-rDfg.js +0 -1
  41. package/payload/server/public/assets/graph-labels-C13OVh5P.js +0 -1
  42. package/payload/server/public/assets/page-p-Fj8Guk.js +0 -1
  43. package/payload/server/public/assets/public-4udeVi_T.js +0 -5
  44. /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-ICY65BIH.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-2INJCOYG.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-Q5J4NI6Q.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-DOIAYD3J.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(),
@@ -6884,7 +6894,7 @@ app9.post("/", async (c) => {
6884
6894
  const safe = typeof v === "string" ? truncate(v, 200) : typeof v === "number" || typeof v === "boolean" ? String(v) : JSON.stringify(v).slice(0, 200);
6885
6895
  return `${k}=${safe}`;
6886
6896
  }).join(" ");
6887
- const TAGGED_PREFIX_SOURCES = /* @__PURE__ */ new Set([]);
6897
+ const TAGGED_PREFIX_SOURCES = /* @__PURE__ */ new Set(["stream-log-click"]);
6888
6898
  if (TAGGED_PREFIX_SOURCES.has(source)) {
6889
6899
  console.log(
6890
6900
  `[${source}] ts=${ts} ip=${ip} version=${version || "unknown"}${extra ? " " + extra : ""}`
@@ -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-JAM3QHGW.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";
@@ -8443,7 +8518,12 @@ function reconstructAssistantEvents(content, components, conversationId, message
8443
8518
  }
8444
8519
  valid += 1;
8445
8520
  const eventIndex = events.length;
8446
- events.push({ type: "component", name: c.name, data: parsedData });
8521
+ events.push({
8522
+ type: "component",
8523
+ name: c.name,
8524
+ data: parsedData,
8525
+ ...c.attachmentId ? { attachmentId: c.attachmentId } : {}
8526
+ });
8447
8527
  if (c.submitted === true) {
8448
8528
  submittedEventIndices.push(eventIndex);
8449
8529
  }
@@ -8471,19 +8551,19 @@ function formatAge(updatedAtStr) {
8471
8551
  return "unknown";
8472
8552
  }
8473
8553
  }
8474
- var app17 = new Hono();
8475
- app17.get("/", requireAdminSession, async (c) => {
8476
- const sessionKey = c.var.sessionKey;
8477
- const accountId = getAccountIdForSession(sessionKey);
8554
+ var app18 = new Hono();
8555
+ app18.get("/", requireAdminSession, async (c) => {
8556
+ const cacheKey = c.var.cacheKey;
8557
+ const accountId = getAccountIdForSession(cacheKey);
8478
8558
  if (!accountId) return c.json({ error: "Account not found for session" }, 401);
8479
- const userId = getUserIdForSession(sessionKey);
8559
+ const userId = getUserIdForSession(cacheKey);
8480
8560
  if (!userId) return c.json({ error: "User identity required \u2014 authenticate with users.json PIN" }, 401);
8481
8561
  try {
8482
8562
  const flushed = await listAdminSessions(accountId, userId, 20);
8483
8563
  const inProgressRaw = listAdminSessionsInProgress(accountId, userId);
8484
8564
  const flushedRows = flushed.map((r) => ({
8485
8565
  conversationId: r.conversationId,
8486
- sessionKey: null,
8566
+ cacheKey: null,
8487
8567
  name: r.name,
8488
8568
  updatedAt: r.updatedAt,
8489
8569
  phase: "flushed",
@@ -8491,7 +8571,7 @@ app17.get("/", requireAdminSession, async (c) => {
8491
8571
  }));
8492
8572
  const inProgressRows = inProgressRaw.map((r) => ({
8493
8573
  conversationId: null,
8494
- sessionKey: r.sessionKey,
8574
+ cacheKey: r.cacheKey,
8495
8575
  name: null,
8496
8576
  updatedAt: new Date(r.createdAt).toISOString(),
8497
8577
  phase: "pre-flush",
@@ -8512,52 +8592,52 @@ app17.get("/", requireAdminSession, async (c) => {
8512
8592
  return c.json({ error: "Failed to fetch sessions" }, 500);
8513
8593
  }
8514
8594
  });
8515
- app17.post("/new", requireAdminSession, async (c) => {
8516
- const oldSessionKey = c.var.sessionKey;
8517
- const accountId = getAccountIdForSession(oldSessionKey);
8518
- const userId = getUserIdForSession(oldSessionKey);
8595
+ app18.post("/new", requireAdminSession, async (c) => {
8596
+ const oldCacheKey = c.var.cacheKey;
8597
+ const accountId = getAccountIdForSession(oldCacheKey);
8598
+ const userId = getUserIdForSession(oldCacheKey);
8519
8599
  if (!accountId || !userId) {
8520
8600
  return c.json({ error: "Session missing account or user context" }, 400);
8521
8601
  }
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 });
8602
+ const newCacheKey = crypto2.randomUUID();
8603
+ const userName = getUserNameForSession(oldCacheKey);
8604
+ registerSession(newCacheKey, "admin", accountId, void 0, userId, userName);
8605
+ const previousConversationId = clearSessionHistory(oldCacheKey);
8606
+ unregisterSession(oldCacheKey);
8607
+ 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`);
8608
+ return c.json({ session_key: newCacheKey, conversationId: null });
8529
8609
  });
8530
- app17.post("/switch", requireAdminSession, async (c) => {
8531
- const sessionKey = c.var.sessionKey;
8532
- const accountId = getAccountIdForSession(sessionKey);
8533
- const userId = getUserIdForSession(sessionKey);
8610
+ app18.post("/switch", requireAdminSession, async (c) => {
8611
+ const cacheKey = c.var.cacheKey;
8612
+ const accountId = getAccountIdForSession(cacheKey);
8613
+ const userId = getUserIdForSession(cacheKey);
8534
8614
  if (!accountId || !userId) {
8535
8615
  return c.json({ error: "Session missing account or user context" }, 400);
8536
8616
  }
8537
8617
  const body = await c.req.json().catch(() => ({}));
8538
- const targetSessionKey = typeof body.target_session_key === "string" ? body.target_session_key : "";
8539
- if (!targetSessionKey) {
8618
+ const targetCacheKey = typeof body.target_session_key === "string" ? body.target_session_key : "";
8619
+ if (!targetCacheKey) {
8540
8620
  return c.json({ error: "target_session_key required" }, 400);
8541
8621
  }
8542
- const targetCheck = validateSession(targetSessionKey, "admin");
8622
+ const targetCheck = validateSession(targetCacheKey, "admin");
8543
8623
  if (!targetCheck.ok) {
8544
- console.error(`[session-switch] reject reason=${targetCheck.reason} from=${sessionKey.slice(0, 8)} target=${targetSessionKey.slice(0, 8)}`);
8624
+ console.error(`[session-switch] reject reason=${targetCheck.reason} from=${cacheKey.slice(0, 8)} target=${targetCacheKey.slice(0, 8)}`);
8545
8625
  return c.json({ error: "Target session not registered or wrong agent type", code: targetCheck.reason }, 404);
8546
8626
  }
8547
- const targetAccountId = getAccountIdForSession(targetSessionKey);
8548
- const targetUserId = getUserIdForSession(targetSessionKey);
8627
+ const targetAccountId = getAccountIdForSession(targetCacheKey);
8628
+ const targetUserId = getUserIdForSession(targetCacheKey);
8549
8629
  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}`);
8630
+ 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
8631
  return c.json({ error: "Target session not owned by current operator" }, 403);
8552
8632
  }
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 });
8633
+ const targetConversationId = getConversationIdForSession(targetCacheKey) ?? null;
8634
+ 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)}`);
8635
+ return c.json({ session_key: targetCacheKey, conversationId: targetConversationId });
8556
8636
  });
8557
- app17.delete("/:id", requireAdminSession, async (c) => {
8637
+ app18.delete("/:id", requireAdminSession, async (c) => {
8558
8638
  const conversationId = c.req.param("id");
8559
- const sessionKey = c.var.sessionKey;
8560
- const accountId = getAccountIdForSession(sessionKey);
8639
+ const cacheKey = c.var.cacheKey;
8640
+ const accountId = getAccountIdForSession(cacheKey);
8561
8641
  if (!accountId) return c.json({ error: "Account not found for session" }, 401);
8562
8642
  const owned = await verifyConversationOwnership(conversationId, accountId);
8563
8643
  if (!owned) return c.json({ error: "Conversation not found" }, 404);
@@ -8569,42 +8649,42 @@ app17.delete("/:id", requireAdminSession, async (c) => {
8569
8649
  return c.json({ error: "Failed to delete session" }, 500);
8570
8650
  }
8571
8651
  });
8572
- app17.post("/:id/resume", async (c) => {
8652
+ app18.post("/:id/resume", async (c) => {
8573
8653
  const conversationId = c.req.param("id");
8574
- const sessionKey = c.req.query("session_key") ?? "";
8575
- if (!sessionKey) {
8654
+ const cacheKey = c.req.query("session_key") ?? "";
8655
+ if (!cacheKey) {
8576
8656
  console.error(`[session] middleware-reject status=400 code=session-missing reason="session_key required" path=${c.req.path}`);
8577
8657
  return c.json({ error: "session_key required", code: "session-missing" }, 400);
8578
8658
  }
8579
8659
  let bridged = false;
8580
- let result = validateSession(sessionKey, "admin");
8660
+ let result = validateSession(cacheKey, "admin");
8581
8661
  if (!result.ok) {
8582
8662
  if (result.reason === "session-not-registered") {
8583
- const bridge = await tryCookieBridgeForConversation(c, sessionKey, conversationId);
8663
+ const bridge = await tryCookieBridgeForConversation(c, cacheKey, conversationId);
8584
8664
  if (!bridge.ok) {
8585
8665
  if (bridge.reason === "conversation-not-found") {
8586
8666
  return c.json({ error: "Conversation not found" }, 404);
8587
8667
  }
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`);
8668
+ const tail = cacheKey.slice(0, 8);
8669
+ 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
8670
  return c.json({ error: "Invalid or expired admin session", code: "session-not-registered" }, 401);
8591
8671
  }
8592
8672
  bridged = true;
8593
- result = validateSession(sessionKey, "admin");
8673
+ result = validateSession(cacheKey, "admin");
8594
8674
  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`);
8675
+ const tail = cacheKey.slice(0, 8);
8676
+ 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
8677
  return c.json({ error: "Invalid or expired admin session", code: "session-not-registered" }, 401);
8598
8678
  }
8599
8679
  } else {
8600
- const tail = sessionKey.slice(0, 8);
8680
+ const tail = cacheKey.slice(0, 8);
8601
8681
  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`);
8682
+ console.error(`[session] middleware-reject status=401 code=${wireCode} reason="invalid or expired admin session" path=${c.req.path} cacheKey=${tail}\u2026`);
8603
8683
  return c.json({ error: "Invalid or expired admin session", code: wireCode }, 401);
8604
8684
  }
8605
8685
  }
8606
- const accountId = getAccountIdForSession(sessionKey);
8607
- const userId = getUserIdForSession(sessionKey);
8686
+ const accountId = getAccountIdForSession(cacheKey);
8687
+ const userId = getUserIdForSession(cacheKey);
8608
8688
  if (!accountId) {
8609
8689
  return c.json({ error: "Session missing account context" }, 400);
8610
8690
  }
@@ -8614,9 +8694,9 @@ app17.post("/:id/resume", async (c) => {
8614
8694
  const updatedAt = await verifyAndGetConversationUpdatedAt(conversationId, accountId);
8615
8695
  if (updatedAt === null) return c.json({ error: "Conversation not found" }, 404);
8616
8696
  const persistedAgentSessionId = await getAgentSessionIdForConversation(conversationId);
8617
- setConversationIdForSession(sessionKey, conversationId);
8697
+ setConversationIdForSession(cacheKey, conversationId);
8618
8698
  if (persistedAgentSessionId) {
8619
- setAgentSessionId(sessionKey, persistedAgentSessionId);
8699
+ setAgentSessionId(cacheKey, persistedAgentSessionId);
8620
8700
  }
8621
8701
  const streamLogPath = resolvePath(ACCOUNTS_DIR, accountId, "logs", `claude-agent-stream-${conversationId}.log`);
8622
8702
  const tag = persistedAgentSessionId ? `agentSessionId=${persistedAgentSessionId.slice(0, 8)}\u2026` : "agentSessionId=missing";
@@ -8641,7 +8721,7 @@ app17.post("/:id/resume", async (c) => {
8641
8721
  jsonlMalformedLines = replay.malformedLines;
8642
8722
  jsonlReplayMessages = replay.messages;
8643
8723
  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>"}`);
8724
+ 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
8725
  }
8646
8726
  } else {
8647
8727
  jsonlMissing = true;
@@ -8656,13 +8736,15 @@ app17.post("/:id/resume", async (c) => {
8656
8736
  if (match) {
8657
8737
  const mergedComponents = j.components.map((c2, i) => {
8658
8738
  const neoComp = match.components?.[i];
8739
+ const attachmentId = neoComp?.attachmentId ?? c2.artefactAttachmentId;
8659
8740
  return {
8660
8741
  componentId: neoComp?.componentId ?? c2.componentId,
8661
8742
  name: c2.name,
8662
8743
  data: c2.data,
8663
8744
  ordinal: c2.ordinal,
8664
8745
  textOffset: c2.textOffset,
8665
- submitted: neoComp?.submitted ?? false
8746
+ submitted: neoComp?.submitted ?? false,
8747
+ ...attachmentId ? { attachmentId } : {}
8666
8748
  };
8667
8749
  });
8668
8750
  return {
@@ -8686,7 +8768,9 @@ app17.post("/:id/resume", async (c) => {
8686
8768
  data: c2.data,
8687
8769
  ordinal: c2.ordinal,
8688
8770
  textOffset: c2.textOffset,
8689
- submitted: false
8771
+ submitted: false,
8772
+ // Task 989 — JSONL has artefactAttachmentId for persistent components.
8773
+ ...c2.artefactAttachmentId ? { attachmentId: c2.artefactAttachmentId } : {}
8690
8774
  })),
8691
8775
  attachments: []
8692
8776
  };
@@ -8804,7 +8888,7 @@ app17.post("/:id/resume", async (c) => {
8804
8888
  const reason = bridged ? "post-restart" : "page-refresh";
8805
8889
  try {
8806
8890
  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}
8891
+ 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
8892
  `);
8809
8893
  if (totalComponents > 0) {
8810
8894
  appendFileSync4(streamLogPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [component-rehydrate] conversationId=${conversationId.slice(0, 8)} count=${totalComponents} valid=${totalValid} invalid=${totalInvalid} textRuns=${textRuns}
@@ -8821,7 +8905,7 @@ app17.post("/:id/resume", async (c) => {
8821
8905
  } catch {
8822
8906
  }
8823
8907
  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`);
8908
+ 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
8909
  return c.json({
8826
8910
  conversationId,
8827
8911
  messages: rehydrated,
@@ -8834,10 +8918,10 @@ app17.post("/:id/resume", async (c) => {
8834
8918
  }
8835
8919
  });
8836
8920
  });
8837
- app17.post("/:id/label", requireAdminSession, async (c) => {
8921
+ app18.post("/:id/label", requireAdminSession, async (c) => {
8838
8922
  const conversationId = c.req.param("id");
8839
- const sessionKey = c.var.sessionKey;
8840
- const accountId = getAccountIdForSession(sessionKey);
8923
+ const cacheKey = c.var.cacheKey;
8924
+ const accountId = getAccountIdForSession(cacheKey);
8841
8925
  if (!accountId) return c.json({ error: "Account not found for session" }, 401);
8842
8926
  const owned = await verifyConversationOwnership(conversationId, accountId);
8843
8927
  if (!owned) return c.json({ error: "Conversation not found" }, 404);
@@ -8861,16 +8945,16 @@ app17.post("/:id/label", requireAdminSession, async (c) => {
8861
8945
  return c.json({ label: null });
8862
8946
  }
8863
8947
  });
8864
- app17.put("/:id/label", requireAdminSession, async (c) => {
8948
+ app18.put("/:id/label", requireAdminSession, async (c) => {
8865
8949
  const conversationId = c.req.param("id");
8866
- const sessionKey = c.var.sessionKey;
8950
+ const cacheKey = c.var.cacheKey;
8867
8951
  let body;
8868
8952
  try {
8869
8953
  body = await c.req.json();
8870
8954
  } catch {
8871
8955
  return c.json({ error: "Invalid JSON body" }, 400);
8872
8956
  }
8873
- const accountId = getAccountIdForSession(sessionKey);
8957
+ const accountId = getAccountIdForSession(cacheKey);
8874
8958
  if (!accountId) return c.json({ error: "Account not found for session" }, 401);
8875
8959
  const owned = await verifyConversationOwnership(conversationId, accountId);
8876
8960
  if (!owned) return c.json({ error: "Conversation not found" }, 404);
@@ -8887,11 +8971,11 @@ app17.put("/:id/label", requireAdminSession, async (c) => {
8887
8971
  return c.json({ error: "Failed to rename session" }, 500);
8888
8972
  }
8889
8973
  });
8890
- var sessions_default = app17;
8974
+ var sessions_default = app18;
8891
8975
 
8892
8976
  // server/routes/admin/browser.ts
8893
- var app18 = new Hono();
8894
- app18.post("/launch", async (c) => {
8977
+ var app19 = new Hono();
8978
+ app19.post("/launch", async (c) => {
8895
8979
  try {
8896
8980
  const transport = resolveBrowserTransport(c.req.raw, c.env?.incoming?.socket?.remoteAddress);
8897
8981
  if (transport === "vnc") {
@@ -8913,7 +8997,7 @@ app18.post("/launch", async (c) => {
8913
8997
  );
8914
8998
  }
8915
8999
  });
8916
- var browser_default = app18;
9000
+ var browser_default = app19;
8917
9001
 
8918
9002
  // server/routes/admin/browser-iframe.ts
8919
9003
  var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
@@ -8930,8 +9014,8 @@ function asString(v, max = 500) {
8930
9014
  function asNumber(v) {
8931
9015
  return typeof v === "number" && Number.isFinite(v) ? v : void 0;
8932
9016
  }
8933
- var app19 = new Hono();
8934
- app19.post("/event", async (c) => {
9017
+ var app20 = new Hono();
9018
+ app20.post("/event", async (c) => {
8935
9019
  let body = {};
8936
9020
  try {
8937
9021
  body = await c.req.json();
@@ -8952,7 +9036,7 @@ app19.post("/event", async (c) => {
8952
9036
  });
8953
9037
  return c.body(null, 204);
8954
9038
  });
8955
- var browser_iframe_default = app19;
9039
+ var browser_iframe_default = app20;
8956
9040
 
8957
9041
  // app/lib/cdp-client.ts
8958
9042
  var CDP_HOST = "127.0.0.1";
@@ -8995,8 +9079,8 @@ async function cdpNavigateNewTab(url, opts = {}) {
8995
9079
  }
8996
9080
 
8997
9081
  // server/routes/admin/device-browser.ts
8998
- var app20 = new Hono();
8999
- app20.post("/navigate", async (c) => {
9082
+ var app21 = new Hono();
9083
+ app21.post("/navigate", async (c) => {
9000
9084
  const TAG19 = "[device-url:click]";
9001
9085
  let body;
9002
9086
  try {
@@ -9083,7 +9167,7 @@ app20.post("/navigate", async (c) => {
9083
9167
  targetId: outcome.targetId
9084
9168
  });
9085
9169
  });
9086
- var device_browser_default = app20;
9170
+ var device_browser_default = app21;
9087
9171
 
9088
9172
  // server/routes/admin/events.ts
9089
9173
  var ALLOWED_EVENTS2 = /* @__PURE__ */ new Set([
@@ -9092,8 +9176,8 @@ var ALLOWED_EVENTS2 = /* @__PURE__ */ new Set([
9092
9176
  "device-url:vnc-surface-shown",
9093
9177
  "device-url:malformed"
9094
9178
  ]);
9095
- var app21 = new Hono();
9096
- app21.post("/", async (c) => {
9179
+ var app22 = new Hono();
9180
+ app22.post("/", async (c) => {
9097
9181
  const TAG19 = "[admin:events]";
9098
9182
  let body;
9099
9183
  try {
@@ -9124,7 +9208,7 @@ app21.post("/", async (c) => {
9124
9208
  console.error(`[${event}] ${formatted}`);
9125
9209
  return c.json({ ok: true });
9126
9210
  });
9127
- var events_default = app21;
9211
+ var events_default = app22;
9128
9212
 
9129
9213
  // server/routes/admin/cloudflare.ts
9130
9214
  import { homedir as homedir2 } from "os";
@@ -9175,6 +9259,31 @@ var CLOUDFLARE_SETUP_FORM_SCHEMA = {
9175
9259
  // server/routes/admin/cloudflare.ts
9176
9260
  var SETUP_TIMEOUT_MS = 10 * 60 * 1e3;
9177
9261
  var DOMAINS_TIMEOUT_MS = 40 * 1e3;
9262
+ var discoveryCache = /* @__PURE__ */ new Map();
9263
+ function getDiscoveryEntry(accountId) {
9264
+ let entry = discoveryCache.get(accountId);
9265
+ if (!entry) {
9266
+ entry = { tunnels: [], domains: [] };
9267
+ discoveryCache.set(accountId, entry);
9268
+ }
9269
+ return entry;
9270
+ }
9271
+ function rememberDomains(accountId, domains) {
9272
+ const entry = getDiscoveryEntry(accountId);
9273
+ entry.domains = domains;
9274
+ entry.domainsUpdatedAt = Date.now();
9275
+ }
9276
+ function rememberTunnels(accountId, tunnels) {
9277
+ const entry = getDiscoveryEntry(accountId);
9278
+ entry.tunnels = tunnels;
9279
+ entry.tunnelsUpdatedAt = Date.now();
9280
+ }
9281
+ function readDiscoveryResults(accountId) {
9282
+ if (!accountId) return { tunnels: [], domains: [] };
9283
+ const entry = discoveryCache.get(accountId);
9284
+ if (!entry) return { tunnels: [], domains: [] };
9285
+ return { tunnels: entry.tunnels, domains: entry.domains };
9286
+ }
9178
9287
  function loadBrandInfo() {
9179
9288
  const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve13(process.cwd(), "..");
9180
9289
  const brandPath = resolve13(platformRoot2, "config", "brand.json");
@@ -9242,7 +9351,7 @@ function validateBody(body) {
9242
9351
  }
9243
9352
  return null;
9244
9353
  }
9245
- var app22 = new Hono();
9354
+ var app23 = new Hono();
9246
9355
  function fieldFromReason(reason) {
9247
9356
  switch (reason) {
9248
9357
  case "not-signed-in":
@@ -9263,15 +9372,15 @@ function fieldFromReason(reason) {
9263
9372
  return "script";
9264
9373
  }
9265
9374
  }
9266
- app22.get("/domains", requireAdminSession, async (c) => {
9375
+ app23.get("/domains", requireAdminSession, async (c) => {
9267
9376
  const started = Date.now();
9268
- const sessionKey = c.var.sessionKey;
9377
+ const cacheKey = c.var.cacheKey;
9269
9378
  let correlationId;
9270
- let sessionKeyTail;
9379
+ let cacheKeyTail;
9271
9380
  let streamLogPath;
9272
9381
  function tag() {
9273
9382
  if (!correlationId) return "";
9274
- return ` conversationId=${correlationId} session_key=${sessionKeyTail ?? "unknown"}`;
9383
+ return ` conversationId=${correlationId} session_key=${cacheKeyTail ?? "unknown"}`;
9275
9384
  }
9276
9385
  function log(line) {
9277
9386
  console.log(`[cloudflare-domains] ${line}${tag()}`);
@@ -9299,9 +9408,9 @@ app22.get("/domains", requireAdminSession, async (c) => {
9299
9408
  };
9300
9409
  return c.json(body, 200);
9301
9410
  }
9302
- const accountId = getAccountIdForSession(sessionKey);
9303
- correlationId = getConversationIdForSession(sessionKey);
9304
- sessionKeyTail = sessionKey.slice(-8);
9411
+ const accountId = getAccountIdForSession(cacheKey);
9412
+ correlationId = getConversationIdForSession(cacheKey);
9413
+ cacheKeyTail = cacheKey.slice(-8);
9305
9414
  if (!accountId) return err("session", "No account bound to session \u2014 refresh chat.");
9306
9415
  if (!correlationId) return err("session", "No active conversation for session \u2014 refresh chat.");
9307
9416
  streamLogPath = streamLogPathFor(accountId, correlationId).streamLogPath;
@@ -9348,6 +9457,7 @@ ${result.stderr}` : ""}`;
9348
9457
  correlationId,
9349
9458
  streamLogPath
9350
9459
  };
9460
+ rememberDomains(accountId, domains);
9351
9461
  const total = Date.now() - started;
9352
9462
  log(`phase=response-sent total_ms=${total} count=${domains.length}`);
9353
9463
  writeRouteMilestone(
@@ -9358,15 +9468,15 @@ ${result.stderr}` : ""}`;
9358
9468
  return c.json(success, 200);
9359
9469
  });
9360
9470
  var TUNNELS_TIMEOUT_MS = 30 * 1e3;
9361
- app22.get("/tunnels", requireAdminSession, async (c) => {
9471
+ app23.get("/tunnels", requireAdminSession, async (c) => {
9362
9472
  const started = Date.now();
9363
- const sessionKey = c.var.sessionKey;
9473
+ const cacheKey = c.var.cacheKey;
9364
9474
  let correlationId;
9365
- let sessionKeyTail;
9475
+ let cacheKeyTail;
9366
9476
  let streamLogPath;
9367
9477
  function tag() {
9368
9478
  if (!correlationId) return "";
9369
- return ` conversationId=${correlationId} session_key=${sessionKeyTail ?? "unknown"}`;
9479
+ return ` conversationId=${correlationId} session_key=${cacheKeyTail ?? "unknown"}`;
9370
9480
  }
9371
9481
  function log(line) {
9372
9482
  console.log(`[cloudflare-tunnels] ${line}${tag()}`);
@@ -9391,9 +9501,9 @@ app22.get("/tunnels", requireAdminSession, async (c) => {
9391
9501
  const body = { ok: false, field, message, output, defaultName, correlationId, streamLogPath };
9392
9502
  return c.json(body, 200);
9393
9503
  }
9394
- const accountId = getAccountIdForSession(sessionKey);
9395
- correlationId = getConversationIdForSession(sessionKey);
9396
- sessionKeyTail = sessionKey.slice(-8);
9504
+ const accountId = getAccountIdForSession(cacheKey);
9505
+ correlationId = getConversationIdForSession(cacheKey);
9506
+ cacheKeyTail = cacheKey.slice(-8);
9397
9507
  if (!accountId) return err("session", "No account bound to session \u2014 refresh chat.");
9398
9508
  if (!correlationId) return err("session", "No active conversation for session \u2014 refresh chat.");
9399
9509
  streamLogPath = streamLogPathFor(accountId, correlationId).streamLogPath;
@@ -9451,18 +9561,21 @@ ${result.stderr}` : ""}`;
9451
9561
  "cloudflare-tunnels",
9452
9562
  `phase=response-sent total_ms=${total} count=${tunnels.length} source=cloudflared${nullTag}`
9453
9563
  );
9564
+ rememberTunnels(accountId, tunnels);
9454
9565
  const success = { ok: true, tunnels, defaultName, correlationId, streamLogPath };
9455
9566
  return c.json(success, 200);
9456
9567
  });
9457
- app22.post("/setup", requireAdminSession, async (c) => {
9568
+ app23.post("/setup", requireAdminSession, async (c) => {
9458
9569
  const started = Date.now();
9459
- const sessionKey = c.var.sessionKey;
9570
+ const cacheKey = c.var.cacheKey;
9460
9571
  let correlationId;
9461
- let sessionKeyTail;
9572
+ let cacheKeyTail;
9462
9573
  let streamLogPath;
9574
+ let resolvedAccountId;
9575
+ const heldInputs = {};
9463
9576
  function tag() {
9464
9577
  if (!correlationId) return "";
9465
- return ` conversationId=${correlationId} session_key=${sessionKeyTail ?? "unknown"}`;
9578
+ return ` conversationId=${correlationId} session_key=${cacheKeyTail ?? "unknown"}`;
9466
9579
  }
9467
9580
  function log(line) {
9468
9581
  console.log(`[cloudflare-setup] ${line}${tag()}`);
@@ -9485,7 +9598,9 @@ app22.post("/setup", requireAdminSession, async (c) => {
9485
9598
  message,
9486
9599
  output,
9487
9600
  correlationId,
9488
- streamLogPath
9601
+ streamLogPath,
9602
+ inputsAlreadyHeld: { ...heldInputs },
9603
+ discoveryResults: readDiscoveryResults(resolvedAccountId)
9489
9604
  };
9490
9605
  return c.json(body2, 200);
9491
9606
  }
@@ -9498,12 +9613,13 @@ app22.post("/setup", requireAdminSession, async (c) => {
9498
9613
  } catch {
9499
9614
  return err("request", "Invalid JSON body");
9500
9615
  }
9501
- const accountId = getAccountIdForSession(sessionKey);
9502
- correlationId = getConversationIdForSession(sessionKey);
9503
- sessionKeyTail = sessionKey.slice(-8);
9616
+ const accountId = getAccountIdForSession(cacheKey);
9617
+ correlationId = getConversationIdForSession(cacheKey);
9618
+ cacheKeyTail = cacheKey.slice(-8);
9504
9619
  if (!accountId) {
9505
9620
  return err("request", "No account bound to session \u2014 refresh chat.");
9506
9621
  }
9622
+ resolvedAccountId = accountId;
9507
9623
  if (!correlationId) {
9508
9624
  return err("request", "No active conversation for session \u2014 refresh chat.");
9509
9625
  }
@@ -9512,6 +9628,9 @@ app22.post("/setup", requireAdminSession, async (c) => {
9512
9628
  const adminFqdn = `${body.adminLabel}.${body.adminDomain}`;
9513
9629
  const publicFqdn = body.publicLabel ? `${body.publicLabel}.${body.publicDomain}` : void 0;
9514
9630
  const apex = body.apex && body.apex.length > 0 ? body.apex : void 0;
9631
+ heldInputs.admin = adminFqdn;
9632
+ if (publicFqdn) heldInputs.public = publicFqdn;
9633
+ if (apex) heldInputs.apex = apex;
9515
9634
  log(`phase=submit-received admin=${adminFqdn} public=${publicFqdn ?? "absent"} apex=${apex ?? "absent"}`);
9516
9635
  const account = resolveAccount();
9517
9636
  if (!account) {
@@ -9553,7 +9672,7 @@ app22.post("/setup", requireAdminSession, async (c) => {
9553
9672
  try {
9554
9673
  cloudflareTask = await openCloudflareTask({
9555
9674
  accountId,
9556
- conversationKey: sessionKey,
9675
+ conversationId: correlationId,
9557
9676
  inputsProvided: Object.keys(taskInputs),
9558
9677
  inputs: taskInputs,
9559
9678
  inputSchema: CLOUDFLARE_SETUP_FORM_SCHEMA,
@@ -9601,7 +9720,7 @@ app22.post("/setup", requireAdminSession, async (c) => {
9601
9720
  taskId: cloudflareTask.taskId,
9602
9721
  taskElementId: cloudflareTask.taskElementId,
9603
9722
  accountId,
9604
- conversationKey: sessionKey,
9723
+ conversationId: correlationId,
9605
9724
  status: "failed",
9606
9725
  errorMessage: CLOUDFLARE_TASK_DIAGNOSTICS.endpointDiedPreReconcile
9607
9726
  });
@@ -9618,7 +9737,7 @@ app22.post("/setup", requireAdminSession, async (c) => {
9618
9737
  taskId: cloudflareTask.taskId,
9619
9738
  taskElementId: cloudflareTask.taskElementId,
9620
9739
  accountId,
9621
- conversationKey: sessionKey,
9740
+ conversationId: correlationId,
9622
9741
  status: "failed",
9623
9742
  errorMessage: `${CLOUDFLARE_TASK_DIAGNOSTICS.scriptExitedNonzero} exit=${status.execMainStatus}`
9624
9743
  });
@@ -9660,7 +9779,7 @@ app22.post("/setup", requireAdminSession, async (c) => {
9660
9779
  taskId: cloudflareTask.taskId,
9661
9780
  taskElementId: cloudflareTask.taskElementId,
9662
9781
  accountId,
9663
- conversationKey: sessionKey,
9782
+ conversationId: correlationId,
9664
9783
  tunnelId: tunnelState?.tunnelId,
9665
9784
  tunnelName: tunnelState?.tunnelName,
9666
9785
  hostnames: tunnelState ? hostnameRecords : void 0,
@@ -9691,7 +9810,7 @@ actionId: ${actionId}`,
9691
9810
  };
9692
9811
  return ok(success);
9693
9812
  });
9694
- var cloudflare_default = app22;
9813
+ var cloudflare_default = app23;
9695
9814
 
9696
9815
  // server/routes/admin/files.ts
9697
9816
  import { createReadStream as createReadStream3 } from "fs";
@@ -9751,7 +9870,7 @@ var UNIQUE_KEYS_BY_LABEL = {
9751
9870
  Event: ["eventId"],
9752
9871
  KnowledgeDocument: ["attachmentId"],
9753
9872
  DigitalDocument: ["attachmentId"],
9754
- Conversation: ["conversationId", "sessionKey"],
9873
+ Conversation: ["conversationId"],
9755
9874
  Message: ["messageId"],
9756
9875
  OnboardingState: ["accountId"],
9757
9876
  Workflow: ["workflowId"],
@@ -9972,7 +10091,7 @@ async function restoreNode(params) {
9972
10091
  }
9973
10092
 
9974
10093
  // 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;
10094
+ 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
10095
  function parseAttachmentPath(relPath2) {
9977
10096
  const segments = relPath2.split("/").filter(Boolean);
9978
10097
  if (segments.length !== 4) return null;
@@ -9980,7 +10099,7 @@ function parseAttachmentPath(relPath2) {
9980
10099
  const accountId = segments[1];
9981
10100
  const attachmentId = segments[2];
9982
10101
  const filename = segments[3];
9983
- if (!UUID_RE3.test(accountId) || !UUID_RE3.test(attachmentId)) return null;
10102
+ if (!UUID_RE4.test(accountId) || !UUID_RE4.test(attachmentId)) return null;
9984
10103
  const dot = filename.lastIndexOf(".");
9985
10104
  if (dot === -1) return null;
9986
10105
  const stem = filename.slice(0, dot);
@@ -10052,7 +10171,7 @@ async function cascadeDeleteDocument(params) {
10052
10171
  }
10053
10172
 
10054
10173
  // 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;
10174
+ 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
10175
  async function readMeta(absDir, baseName) {
10057
10176
  try {
10058
10177
  const raw = await readFile3(join9(absDir, `${baseName}.meta.json`), "utf8");
@@ -10074,7 +10193,7 @@ async function readAccountNames() {
10074
10193
  return map;
10075
10194
  }
10076
10195
  for (const name of names) {
10077
- if (!UUID_RE4.test(name)) continue;
10196
+ if (!UUID_RE5.test(name)) continue;
10078
10197
  const configPath2 = resolve15(accountsDir, name, "account.json");
10079
10198
  try {
10080
10199
  const raw = await readFile3(configPath2, "utf8");
@@ -10092,7 +10211,7 @@ async function readAccountNames() {
10092
10211
  return map;
10093
10212
  }
10094
10213
  async function enrich(absolute, entry, accountNames) {
10095
- if (entry.kind === "directory" && UUID_RE4.test(entry.name)) {
10214
+ if (entry.kind === "directory" && UUID_RE5.test(entry.name)) {
10096
10215
  const meta = await readMeta(join9(absolute, entry.name), entry.name);
10097
10216
  if (meta?.filename) {
10098
10217
  entry.displayName = meta.filename;
@@ -10108,7 +10227,7 @@ async function enrich(absolute, entry, accountNames) {
10108
10227
  if (entry.kind === "file") {
10109
10228
  const dot = entry.name.lastIndexOf(".");
10110
10229
  const base = dot === -1 ? entry.name : entry.name.slice(0, dot);
10111
- if (UUID_RE4.test(base)) {
10230
+ if (UUID_RE5.test(base)) {
10112
10231
  const meta = await readMeta(absolute, base);
10113
10232
  if (meta?.filename) {
10114
10233
  entry.displayName = meta.filename;
@@ -10120,14 +10239,14 @@ async function enrich(absolute, entry, accountNames) {
10120
10239
  function buildDisplayPath(relPath2, accountNames) {
10121
10240
  if (relPath2 === "." || relPath2 === "") return [];
10122
10241
  return relPath2.split("/").filter(Boolean).map((seg) => {
10123
- const dn = UUID_RE4.test(seg) ? accountNames.get(seg) : void 0;
10242
+ const dn = UUID_RE5.test(seg) ? accountNames.get(seg) : void 0;
10124
10243
  return dn ? { name: seg, displayName: dn } : { name: seg };
10125
10244
  });
10126
10245
  }
10127
- var app23 = new Hono();
10128
- app23.get("/", requireAdminSession, async (c) => {
10129
- const sessionKey = c.var.sessionKey;
10130
- if (!getAccountIdForSession(sessionKey)) {
10246
+ var app24 = new Hono();
10247
+ app24.get("/", requireAdminSession, async (c) => {
10248
+ const cacheKey = c.var.cacheKey;
10249
+ if (!getAccountIdForSession(cacheKey)) {
10131
10250
  console.error(`[data] auth-rejected endpoint="/api/admin/files" reason="no account for session"`);
10132
10251
  return c.json({ error: "Account not found for session" }, 401);
10133
10252
  }
@@ -10148,7 +10267,7 @@ app23.get("/", requireAdminSession, async (c) => {
10148
10267
  const names = await readdir2(absolute);
10149
10268
  const entries = [];
10150
10269
  for (const name of names) {
10151
- if (UUID_RE4.test(name.replace(/\.meta\.json$/, "")) && name.endsWith(".meta.json")) {
10270
+ if (UUID_RE5.test(name.replace(/\.meta\.json$/, "")) && name.endsWith(".meta.json")) {
10152
10271
  continue;
10153
10272
  }
10154
10273
  try {
@@ -10186,9 +10305,9 @@ app23.get("/", requireAdminSession, async (c) => {
10186
10305
  return c.json({ error: message }, 500);
10187
10306
  }
10188
10307
  });
10189
- app23.get("/download", requireAdminSession, async (c) => {
10190
- const sessionKey = c.var.sessionKey;
10191
- if (!getAccountIdForSession(sessionKey)) {
10308
+ app24.get("/download", requireAdminSession, async (c) => {
10309
+ const cacheKey = c.var.cacheKey;
10310
+ if (!getAccountIdForSession(cacheKey)) {
10192
10311
  console.error(`[data] auth-rejected endpoint="/api/admin/files/download" reason="no account for session"`);
10193
10312
  return c.json({ error: "Account not found for session" }, 401);
10194
10313
  }
@@ -10234,9 +10353,9 @@ app23.get("/download", requireAdminSession, async (c) => {
10234
10353
  return c.json({ error: message }, 500);
10235
10354
  }
10236
10355
  });
10237
- app23.post("/upload", requireAdminSession, async (c) => {
10238
- const sessionKey = c.var.sessionKey;
10239
- const accountId = getAccountIdForSession(sessionKey);
10356
+ app24.post("/upload", requireAdminSession, async (c) => {
10357
+ const cacheKey = c.var.cacheKey;
10358
+ const accountId = getAccountIdForSession(cacheKey);
10240
10359
  if (!accountId) {
10241
10360
  console.error(`[data] auth-rejected endpoint="/api/admin/files/upload" reason="no account for session"`);
10242
10361
  return c.json({ error: "Account not found for session" }, 401);
@@ -10292,9 +10411,9 @@ app23.post("/upload", requireAdminSession, async (c) => {
10292
10411
  mimeType: file.type
10293
10412
  });
10294
10413
  });
10295
- app23.delete("/", requireAdminSession, async (c) => {
10296
- const sessionKey = c.var.sessionKey;
10297
- const accountId = getAccountIdForSession(sessionKey);
10414
+ app24.delete("/", requireAdminSession, async (c) => {
10415
+ const cacheKey = c.var.cacheKey;
10416
+ const accountId = getAccountIdForSession(cacheKey);
10298
10417
  if (!accountId) {
10299
10418
  console.error(`[data] auth-rejected endpoint="DELETE /api/admin/files" reason="no account for session"`);
10300
10419
  return c.json({ error: "Account not found for session" }, 401);
@@ -10325,7 +10444,7 @@ app23.delete("/", requireAdminSession, async (c) => {
10325
10444
  }
10326
10445
  const dot = base.lastIndexOf(".");
10327
10446
  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;
10447
+ const sidecarPath = UUID_RE5.test(stem) && base !== `${stem}.meta.json` ? join9(dirname6(absolute), `${stem}.meta.json`) : null;
10329
10448
  await unlink2(absolute);
10330
10449
  if (sidecarPath) {
10331
10450
  try {
@@ -10359,7 +10478,7 @@ app23.delete("/", requireAdminSession, async (c) => {
10359
10478
  return c.json({ error: message }, 500);
10360
10479
  }
10361
10480
  });
10362
- var files_default = app23;
10481
+ var files_default = app24;
10363
10482
 
10364
10483
  // ../lib/graph-search/src/index.ts
10365
10484
  var import_dist = __toESM(require_dist());
@@ -10739,14 +10858,14 @@ var MAX_LIMIT = 2e3;
10739
10858
  var DEFAULT_VECTOR_THRESHOLD = 0.82;
10740
10859
  var MESSAGE_FAMILY_LABELS = ["Message", "UserMessage", "AssistantMessage", "WhatsAppMessage"];
10741
10860
  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;
10861
+ var app25 = new Hono();
10862
+ app25.get("/", requireAdminSession, async (c) => {
10863
+ const cacheKey = c.var.cacheKey;
10745
10864
  const q = (c.req.query("q") ?? "").trim();
10746
10865
  const rawLimit = c.req.query("limit");
10747
10866
  const rawLabels = c.req.query("labels");
10748
10867
  const rawThreshold = c.req.query("threshold");
10749
- const accountId = getAccountIdForSession(sessionKey);
10868
+ const accountId = getAccountIdForSession(cacheKey);
10750
10869
  if (!accountId) {
10751
10870
  console.error(`[graph-search] auth-rejected endpoint="/api/admin/graph-search" reason="no account for session"`);
10752
10871
  return c.json({ error: "Account not found for session" }, 401);
@@ -10871,7 +10990,7 @@ app24.get("/", requireAdminSession, async (c) => {
10871
10990
  await session.close();
10872
10991
  }
10873
10992
  });
10874
- var graph_search_default = app24;
10993
+ var graph_search_default = app25;
10875
10994
 
10876
10995
  // server/routes/admin/graph-subgraph.ts
10877
10996
  import neo4j2 from "neo4j-driver";
@@ -11027,12 +11146,12 @@ var STRIPPED_PROPERTIES = /* @__PURE__ */ new Set([
11027
11146
  "passwordHash",
11028
11147
  "magicToken",
11029
11148
  "otpCode",
11030
- "sessionKey"
11149
+ "cacheKey"
11031
11150
  ]);
11032
- var app25 = new Hono();
11033
- app25.get("/", requireAdminSession, async (c) => {
11034
- const sessionKey = c.var.sessionKey;
11035
- const accountId = getAccountIdForSession(sessionKey);
11151
+ var app26 = new Hono();
11152
+ app26.get("/", requireAdminSession, async (c) => {
11153
+ const cacheKey = c.var.cacheKey;
11154
+ const accountId = getAccountIdForSession(cacheKey);
11036
11155
  if (!accountId) {
11037
11156
  console.error('[graph-page] auth-rejected reason="no account for session"');
11038
11157
  return c.json({ error: "Account not found for session" }, 401);
@@ -11613,14 +11732,14 @@ function pruneNode(node, warnedClasses, conversationWarnings) {
11613
11732
  }
11614
11733
  return trashed ? { id: node.id, labels, properties, trashed: true } : { id: node.id, labels, properties };
11615
11734
  }
11616
- var graph_subgraph_default = app25;
11735
+ var graph_subgraph_default = app26;
11617
11736
 
11618
11737
  // server/routes/admin/graph-delete.ts
11619
11738
  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);
11739
+ var app27 = new Hono();
11740
+ app27.post("/", requireAdminSession, async (c) => {
11741
+ const cacheKey = c.var.cacheKey;
11742
+ const accountId = getAccountIdForSession(cacheKey);
11624
11743
  if (!accountId) {
11625
11744
  console.error('[graph-page] delete auth-rejected reason="no account for session"');
11626
11745
  return c.json({ error: "Account not found for session" }, 401);
@@ -11689,13 +11808,13 @@ app26.post("/", requireAdminSession, async (c) => {
11689
11808
  }
11690
11809
  }
11691
11810
  });
11692
- var graph_delete_default = app26;
11811
+ var graph_delete_default = app27;
11693
11812
 
11694
11813
  // 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);
11814
+ var app28 = new Hono();
11815
+ app28.post("/", requireAdminSession, async (c) => {
11816
+ const cacheKey = c.var.cacheKey;
11817
+ const accountId = getAccountIdForSession(cacheKey);
11699
11818
  if (!accountId) {
11700
11819
  console.error('[graph-page] restore auth-rejected reason="no account for session"');
11701
11820
  return c.json({ error: "Account not found for session" }, 401);
@@ -11757,13 +11876,13 @@ app27.post("/", requireAdminSession, async (c) => {
11757
11876
  }
11758
11877
  }
11759
11878
  });
11760
- var graph_restore_default = app27;
11879
+ var graph_restore_default = app28;
11761
11880
 
11762
11881
  // 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);
11882
+ var app29 = new Hono();
11883
+ app29.get("/", requireAdminSession, async (c) => {
11884
+ const cacheKey = c.var.cacheKey;
11885
+ const accountId = getAccountIdForSession(cacheKey);
11767
11886
  if (!accountId) {
11768
11887
  console.error('[graph-page] labels-in-graph-rejected reason="no account for session"');
11769
11888
  return c.json({ error: "Account not found for session" }, 401);
@@ -11827,14 +11946,14 @@ var LABELS_IN_GRAPH_CYPHER = `
11827
11946
  sum(halfEdges) AS relDegree
11828
11947
  RETURN label, nodeCount, relDegree
11829
11948
  `;
11830
- var graph_labels_in_graph_default = app28;
11949
+ var graph_labels_in_graph_default = app29;
11831
11950
 
11832
11951
  // 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);
11952
+ var app30 = new Hono();
11953
+ app30.get("/", requireAdminSession, async (c) => {
11954
+ const cacheKey = c.var.cacheKey;
11955
+ const accountId = getAccountIdForSession(cacheKey);
11956
+ const userId = getUserIdForSession(cacheKey);
11838
11957
  if (!accountId || !userId) {
11839
11958
  console.error('[graph-page] default-view-rejected reason="missing account or user context"');
11840
11959
  return c.json({ error: "Account and user context required for default view" }, 401);
@@ -11869,10 +11988,10 @@ app29.get("/", requireAdminSession, async (c) => {
11869
11988
  }
11870
11989
  }
11871
11990
  });
11872
- app29.put("/", requireAdminSession, async (c) => {
11873
- const sessionKey = c.var.sessionKey;
11874
- const accountId = getAccountIdForSession(sessionKey);
11875
- const userId = getUserIdForSession(sessionKey);
11991
+ app30.put("/", requireAdminSession, async (c) => {
11992
+ const cacheKey = c.var.cacheKey;
11993
+ const accountId = getAccountIdForSession(cacheKey);
11994
+ const userId = getUserIdForSession(cacheKey);
11876
11995
  if (!accountId || !userId) {
11877
11996
  console.error('[graph-page] default-view-rejected reason="missing account or user context"');
11878
11997
  return c.json({ error: "Account and user context required for default view" }, 401);
@@ -11958,11 +12077,11 @@ var WRITE_CYPHER = `
11958
12077
  p.updatedAt = $updatedAt
11959
12078
  RETURN p.labels AS labels
11960
12079
  `;
11961
- var graph_default_view_default = app29;
12080
+ var graph_default_view_default = app30;
11962
12081
 
11963
12082
  // server/routes/admin/file-attach.ts
11964
- var app30 = new Hono();
11965
- app30.post("/", async (c) => {
12083
+ var app31 = new Hono();
12084
+ app31.post("/", async (c) => {
11966
12085
  try {
11967
12086
  const body = await c.req.json();
11968
12087
  const { filePath, accountId } = body;
@@ -11985,11 +12104,11 @@ app30.post("/", async (c) => {
11985
12104
  return c.json({ error: message }, 500);
11986
12105
  }
11987
12106
  });
11988
- var file_attach_default = app30;
12107
+ var file_attach_default = app31;
11989
12108
 
11990
12109
  // server/routes/admin/adherence.ts
11991
- var app31 = new Hono();
11992
- app31.get("/", requireAdminSession, async (c) => {
12110
+ var app32 = new Hono();
12111
+ app32.get("/", requireAdminSession, async (c) => {
11993
12112
  const agent = c.req.query("agent") ?? "admin";
11994
12113
  const includeBlock = c.req.query("block") === "1";
11995
12114
  const account = resolveAccount();
@@ -12010,7 +12129,7 @@ app31.get("/", requireAdminSession, async (c) => {
12010
12129
  return c.json({ error: "Failed to read adherence ledger", agent }, 500);
12011
12130
  }
12012
12131
  });
12013
- var adherence_default = app31;
12132
+ var adherence_default = app32;
12014
12133
 
12015
12134
  // server/routes/admin/sidebar-artefacts.ts
12016
12135
  import neo4j3 from "neo4j-driver";
@@ -12020,10 +12139,10 @@ import { existsSync as existsSync18 } from "fs";
12020
12139
  var LIMIT = 50;
12021
12140
  var TEXT_MIME_PREFIXES = ["text/", "application/json", "application/markdown"];
12022
12141
  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);
12142
+ var app33 = new Hono();
12143
+ app33.get("/", requireAdminSession, async (c) => {
12144
+ const cacheKey = c.var.cacheKey;
12145
+ const accountId = getAccountIdForSession(cacheKey);
12027
12146
  if (!accountId) {
12028
12147
  return c.json({ error: "Account not found for session" }, 401);
12029
12148
  }
@@ -12227,18 +12346,18 @@ function isWithin(target, root) {
12227
12346
  const rel = relative2(root, target);
12228
12347
  return !rel.startsWith("..") && !isAbsolute(rel);
12229
12348
  }
12230
- var sidebar_artefacts_default = app32;
12349
+ var sidebar_artefacts_default = app33;
12231
12350
 
12232
12351
  // server/routes/admin/sidebar-artefact-save.ts
12233
12352
  import { mkdir as mkdir3, readdir as readdir4, stat as stat5, writeFile as writeFile4 } from "fs/promises";
12234
12353
  import { resolve as resolve17 } from "path";
12235
12354
  import { existsSync as existsSync19 } from "fs";
12236
12355
  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);
12356
+ var UUID_RE6 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
12357
+ var app34 = new Hono();
12358
+ app34.post("/", requireAdminSession, async (c) => {
12359
+ const cacheKey = c.var.cacheKey;
12360
+ const accountId = getAccountIdForSession(cacheKey);
12242
12361
  if (!accountId) return c.json({ error: "Account not found for session" }, 401);
12243
12362
  const body = await safeJson(c);
12244
12363
  if (!body || typeof body.id !== "string" || typeof body.content !== "string") {
@@ -12294,7 +12413,7 @@ async function resolveSavePath(id, accountId, accountDir) {
12294
12413
  }
12295
12414
  return { kind: "admin-template", path: resolve17(parent, filename) };
12296
12415
  }
12297
- if (UUID_RE5.test(id)) {
12416
+ if (UUID_RE6.test(id)) {
12298
12417
  const dir = resolve17(ATTACHMENTS_ROOT, accountId, id);
12299
12418
  if (!existsSync19(dir)) {
12300
12419
  return { kind: "reject", status: 400, reason: "not-found" };
@@ -12316,20 +12435,20 @@ async function resolveSavePath(id, accountId, accountDir) {
12316
12435
  function relPath(absPath, root) {
12317
12436
  return absPath.startsWith(root) ? absPath.slice(root.length + 1) : absPath;
12318
12437
  }
12319
- var sidebar_artefact_save_default = app33;
12438
+ var sidebar_artefact_save_default = app34;
12320
12439
 
12321
12440
  // server/routes/admin/sidebar-artefact-content.ts
12322
12441
  import { readFile as readFile5, readdir as readdir5 } from "fs/promises";
12323
12442
  import { existsSync as existsSync20 } from "fs";
12324
12443
  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);
12444
+ var UUID_RE7 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
12445
+ var app35 = new Hono();
12446
+ app35.get("/", requireAdminSession, async (c) => {
12447
+ const cacheKey = c.var.cacheKey;
12448
+ const accountId = getAccountIdForSession(cacheKey);
12330
12449
  if (!accountId) return new Response("Unauthorized", { status: 401 });
12331
12450
  const id = c.req.query("id") ?? "";
12332
- if (!UUID_RE6.test(id)) {
12451
+ if (!UUID_RE7.test(id)) {
12333
12452
  console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
12334
12453
  return new Response("Not found", { status: 404 });
12335
12454
  }
@@ -12365,7 +12484,7 @@ app34.get("/", requireAdminSession, async (c) => {
12365
12484
  }
12366
12485
  });
12367
12486
  });
12368
- var sidebar_artefact_content_default = app34;
12487
+ var sidebar_artefact_content_default = app35;
12369
12488
 
12370
12489
  // server/routes/admin/health.ts
12371
12490
  import { existsSync as existsSync21, readFileSync as readFileSync16 } from "fs";
@@ -12410,8 +12529,8 @@ async function probeConversationDb() {
12410
12529
  });
12411
12530
  }
12412
12531
  }
12413
- var app35 = new Hono();
12414
- app35.get("/", async (c) => {
12532
+ var app36 = new Hono();
12533
+ app36.get("/", async (c) => {
12415
12534
  const version = readVersion();
12416
12535
  const probe = await probeConversationDb();
12417
12536
  const uptimeMs = Date.now() - new Date(PROCESS_STARTED_AT).getTime();
@@ -12433,37 +12552,38 @@ app35.get("/", async (c) => {
12433
12552
  uptimeMs
12434
12553
  });
12435
12554
  });
12436
- var health_default2 = app35;
12555
+ var health_default2 = app36;
12437
12556
 
12438
12557
  // 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;
12558
+ var app37 = new Hono();
12559
+ app37.route("/session", session_default2);
12560
+ app37.route("/chat", chat_default2);
12561
+ app37.route("/chat-failure", chat_failure_default);
12562
+ app37.route("/compact", compact_default);
12563
+ app37.route("/logs", logs_default);
12564
+ app37.route("/claude-info", claude_info_default);
12565
+ app37.route("/attachment", attachment_default);
12566
+ app37.route("/agents", agents_default);
12567
+ app37.route("/sessions", sessions_default);
12568
+ app37.route("/browser", browser_default);
12569
+ app37.route("/browser-iframe", browser_iframe_default);
12570
+ app37.route("/device-browser", device_browser_default);
12571
+ app37.route("/events", events_default);
12572
+ app37.route("/cloudflare", cloudflare_default);
12573
+ app37.route("/files", files_default);
12574
+ app37.route("/graph-search", graph_search_default);
12575
+ app37.route("/graph-subgraph", graph_subgraph_default);
12576
+ app37.route("/graph-delete", graph_delete_default);
12577
+ app37.route("/graph-restore", graph_restore_default);
12578
+ app37.route("/graph-labels-in-graph", graph_labels_in_graph_default);
12579
+ app37.route("/graph-default-view", graph_default_view_default);
12580
+ app37.route("/file-attach", file_attach_default);
12581
+ app37.route("/adherence", adherence_default);
12582
+ app37.route("/sidebar-artefacts", sidebar_artefacts_default);
12583
+ app37.route("/sidebar-artefact-save", sidebar_artefact_save_default);
12584
+ app37.route("/sidebar-artefact-content", sidebar_artefact_content_default);
12585
+ app37.route("/health-brand", health_default2);
12586
+ var admin_default = app37;
12467
12587
 
12468
12588
  // server/routes/sites.ts
12469
12589
  import { existsSync as existsSync22, readFileSync as readFileSync17, realpathSync as realpathSync4, statSync as statSync7 } from "fs";
@@ -12498,8 +12618,8 @@ function getExt(p) {
12498
12618
  if (idx < p.lastIndexOf("/")) return "";
12499
12619
  return p.slice(idx).toLowerCase();
12500
12620
  }
12501
- var app37 = new Hono();
12502
- app37.get("/:rel{.*}", (c) => {
12621
+ var app38 = new Hono();
12622
+ app38.get("/:rel{.*}", (c) => {
12503
12623
  const reqPath = c.req.path;
12504
12624
  const rawRel = c.req.param("rel") ?? "";
12505
12625
  const trimmed = rawRel.replace(/^\/+/, "").replace(/\/+$/, "");
@@ -12602,7 +12722,7 @@ app37.get("/:rel{.*}", (c) => {
12602
12722
  "X-Content-Type-Options": "nosniff"
12603
12723
  });
12604
12724
  });
12605
- var sites_default = app37;
12725
+ var sites_default = app38;
12606
12726
 
12607
12727
  // app/lib/graph-health.ts
12608
12728
  var HOUR_MS = 60 * 60 * 1e3;
@@ -12756,9 +12876,9 @@ watchFile(ALIAS_DOMAINS_PATH2, { interval: 2e3 }, () => {
12756
12876
  function isPublicHost(host) {
12757
12877
  return host.startsWith("public.") || aliasDomains.has(host);
12758
12878
  }
12759
- var app38 = new Hono();
12760
- app38.use("*", clientIpMiddleware);
12761
- app38.use("*", async (c, next) => {
12879
+ var app39 = new Hono();
12880
+ app39.use("*", clientIpMiddleware);
12881
+ app39.use("*", async (c, next) => {
12762
12882
  await next();
12763
12883
  c.header("X-Content-Type-Options", "nosniff");
12764
12884
  c.header("Referrer-Policy", "strict-origin-when-cross-origin");
@@ -12768,7 +12888,7 @@ app38.use("*", async (c, next) => {
12768
12888
  );
12769
12889
  });
12770
12890
  var HTTP_LOG_PATHS = /* @__PURE__ */ new Set(["/vnc-viewer.html", "/vnc-popout.html"]);
12771
- app38.use("*", async (c, next) => {
12891
+ app39.use("*", async (c, next) => {
12772
12892
  if (!HTTP_LOG_PATHS.has(c.req.path)) {
12773
12893
  await next();
12774
12894
  return;
@@ -12801,7 +12921,7 @@ var PUBLIC_ALLOWED_PREFIXES = [
12801
12921
  "/sites/"
12802
12922
  ];
12803
12923
  var PUBLIC_ALLOWED_EXACT = ["/favicon.ico"];
12804
- app38.use("*", async (c, next) => {
12924
+ app39.use("*", async (c, next) => {
12805
12925
  const host = (c.req.header("host") ?? "").split(":")[0];
12806
12926
  if (!isPublicHost(host)) {
12807
12927
  await next();
@@ -12841,7 +12961,7 @@ function resolveRemoteAuthOpts() {
12841
12961
  return brandLoginOpts;
12842
12962
  }
12843
12963
  var MAX_LOGIN_BODY = 8 * 1024;
12844
- app38.post("/__remote-auth/login", async (c) => {
12964
+ app39.post("/__remote-auth/login", async (c) => {
12845
12965
  const client = clientFrom(c);
12846
12966
  const clientIp = client.ip || "unknown";
12847
12967
  if (!requestIsTlsTerminated(c)) {
@@ -12886,7 +13006,7 @@ app38.post("/__remote-auth/login", async (c) => {
12886
13006
  }
12887
13007
  });
12888
13008
  });
12889
- app38.get("/__remote-auth/logout", (c) => {
13009
+ app39.get("/__remote-auth/logout", (c) => {
12890
13010
  const client = clientFrom(c);
12891
13011
  const clientIp = client.ip || "unknown";
12892
13012
  console.error(`[remote-auth] logout ip=${clientIp}`);
@@ -12899,7 +13019,7 @@ app38.get("/__remote-auth/logout", (c) => {
12899
13019
  }
12900
13020
  });
12901
13021
  });
12902
- app38.post("/__remote-auth/change-password", async (c) => {
13022
+ app39.post("/__remote-auth/change-password", async (c) => {
12903
13023
  const client = clientFrom(c);
12904
13024
  const clientIp = client.ip || "unknown";
12905
13025
  const rateLimited = checkRateLimit(client);
@@ -12950,13 +13070,13 @@ app38.post("/__remote-auth/change-password", async (c) => {
12950
13070
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "change", changeError: "Failed to save password", redirect }), 200);
12951
13071
  }
12952
13072
  });
12953
- app38.get("/__remote-auth/setup", (c) => {
13073
+ app39.get("/__remote-auth/setup", (c) => {
12954
13074
  if (isRemoteAuthConfigured()) {
12955
13075
  return c.redirect("/");
12956
13076
  }
12957
13077
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup" }), 200);
12958
13078
  });
12959
- app38.post("/__remote-auth/set-initial-password", async (c) => {
13079
+ app39.post("/__remote-auth/set-initial-password", async (c) => {
12960
13080
  if (isRemoteAuthConfigured()) {
12961
13081
  return c.redirect("/");
12962
13082
  }
@@ -12994,10 +13114,10 @@ app38.post("/__remote-auth/set-initial-password", async (c) => {
12994
13114
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), mode: "setup", setupError: "Failed to save password. Please try again." }), 200);
12995
13115
  }
12996
13116
  });
12997
- app38.get("/api/remote-auth/status", (c) => {
13117
+ app39.get("/api/remote-auth/status", (c) => {
12998
13118
  return c.json({ configured: isRemoteAuthConfigured() });
12999
13119
  });
13000
- app38.post("/api/remote-auth/set-password", async (c) => {
13120
+ app39.post("/api/remote-auth/set-password", async (c) => {
13001
13121
  let body;
13002
13122
  try {
13003
13123
  body = await c.req.json();
@@ -13027,9 +13147,9 @@ app38.post("/api/remote-auth/set-password", async (c) => {
13027
13147
  return c.json({ error: "Failed to save password" }, 500);
13028
13148
  }
13029
13149
  });
13030
- app38.route("/api/_client-error", client_error_default);
13150
+ app39.route("/api/_client-error", client_error_default);
13031
13151
  console.log("[client-error-route] mounted");
13032
- app38.use("*", async (c, next) => {
13152
+ app39.use("*", async (c, next) => {
13033
13153
  const host = (c.req.header("host") ?? "").split(":")[0];
13034
13154
  const path2 = c.req.path;
13035
13155
  if (path2 === "/favicon.ico" || path2.startsWith("/assets/") || path2.startsWith("/brand/")) {
@@ -13069,15 +13189,15 @@ app38.use("*", async (c, next) => {
13069
13189
  console.error(`[remote-auth] login required ip=${clientIp} path=${path2} ${disambig}`);
13070
13190
  return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), redirect: path2 }), 200);
13071
13191
  });
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);
13192
+ app39.route("/api/health", health_default);
13193
+ app39.route("/api/session", session_default);
13194
+ app39.route("/api/chat", chat_default);
13195
+ app39.route("/api/group", group_default);
13196
+ app39.route("/api/access", access_default);
13197
+ app39.route("/api/telegram", telegram_default);
13198
+ app39.route("/api/whatsapp", whatsapp_default);
13199
+ app39.route("/api/onboarding", onboarding_default);
13200
+ app39.route("/api/admin", admin_default);
13081
13201
  var SAFE_SLUG_RE = /^[a-z][a-z0-9-]{2,49}$/;
13082
13202
  var SAFE_FILENAME_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
13083
13203
  var IMAGE_MIME = {
@@ -13089,7 +13209,7 @@ var IMAGE_MIME = {
13089
13209
  ".svg": "image/svg+xml",
13090
13210
  ".ico": "image/x-icon"
13091
13211
  };
13092
- app38.get("/agent-assets/:slug/:filename", (c) => {
13212
+ app39.get("/agent-assets/:slug/:filename", (c) => {
13093
13213
  const slug = c.req.param("slug");
13094
13214
  const filename = c.req.param("filename");
13095
13215
  if (!SAFE_SLUG_RE.test(slug)) {
@@ -13124,7 +13244,7 @@ app38.get("/agent-assets/:slug/:filename", (c) => {
13124
13244
  "Cache-Control": "public, max-age=3600"
13125
13245
  });
13126
13246
  });
13127
- app38.get("/generated/:filename", (c) => {
13247
+ app39.get("/generated/:filename", (c) => {
13128
13248
  const filename = c.req.param("filename");
13129
13249
  if (!SAFE_FILENAME_RE.test(filename) || filename.includes("..")) {
13130
13250
  console.error(`[generated] serve file=${filename} status=403`);
@@ -13154,7 +13274,7 @@ app38.get("/generated/:filename", (c) => {
13154
13274
  "Cache-Control": "public, max-age=86400"
13155
13275
  });
13156
13276
  });
13157
- app38.route("/sites", sites_default);
13277
+ app39.route("/sites", sites_default);
13158
13278
  var htmlCache = /* @__PURE__ */ new Map();
13159
13279
  var brandLogoPath = "/brand/maxy-monochrome.png";
13160
13280
  var brandIconPath = "/brand/maxy-monochrome.png";
@@ -13291,7 +13411,7 @@ function brandedPublicHtml(agentSlug) {
13291
13411
  function escapeHtml(s) {
13292
13412
  return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
13293
13413
  }
13294
- app38.get("/", (c) => {
13414
+ app39.get("/", (c) => {
13295
13415
  const host = (c.req.header("host") ?? "").split(":")[0];
13296
13416
  if (isPublicHost(host)) {
13297
13417
  const defaultSlug = resolveDefaultSlug();
@@ -13299,12 +13419,12 @@ app38.get("/", (c) => {
13299
13419
  }
13300
13420
  return c.html(cachedHtml("index.html"));
13301
13421
  });
13302
- app38.get("/public", (c) => {
13422
+ app39.get("/public", (c) => {
13303
13423
  const host = (c.req.header("host") ?? "").split(":")[0];
13304
13424
  if (isPublicHost(host)) return c.text("Not found", 404);
13305
13425
  return c.html(cachedHtml("public.html"));
13306
13426
  });
13307
- app38.get("/chat", (c) => {
13427
+ app39.get("/chat", (c) => {
13308
13428
  const host = (c.req.header("host") ?? "").split(":")[0];
13309
13429
  if (isPublicHost(host)) return c.text("Not found", 404);
13310
13430
  return c.html(cachedHtml("public.html"));
@@ -13323,9 +13443,9 @@ async function logViewerFetch(c, next) {
13323
13443
  duration_ms: Date.now() - start
13324
13444
  });
13325
13445
  }
13326
- app38.use("/vnc-viewer.html", logViewerFetch);
13327
- app38.use("/vnc-popout.html", logViewerFetch);
13328
- app38.get("/vnc-popout.html", (c) => {
13446
+ app39.use("/vnc-viewer.html", logViewerFetch);
13447
+ app39.use("/vnc-popout.html", logViewerFetch);
13448
+ app39.get("/vnc-popout.html", (c) => {
13329
13449
  let html = htmlCache.get("vnc-popout.html");
13330
13450
  if (!html) {
13331
13451
  html = readFileSync18(resolve21(process.cwd(), "public", "vnc-popout.html"), "utf-8");
@@ -13338,7 +13458,7 @@ app38.get("/vnc-popout.html", (c) => {
13338
13458
  }
13339
13459
  return c.html(html);
13340
13460
  });
13341
- app38.post("/api/vnc/client-event", async (c) => {
13461
+ app39.post("/api/vnc/client-event", async (c) => {
13342
13462
  let body;
13343
13463
  try {
13344
13464
  body = await c.req.json();
@@ -13359,20 +13479,20 @@ app38.post("/api/vnc/client-event", async (c) => {
13359
13479
  });
13360
13480
  return c.json({ ok: true });
13361
13481
  });
13362
- app38.get("/g/:slug", (c) => {
13482
+ app39.get("/g/:slug", (c) => {
13363
13483
  return c.html(brandedPublicHtml());
13364
13484
  });
13365
- app38.get("/graph", (c) => {
13485
+ app39.get("/graph", (c) => {
13366
13486
  const host = (c.req.header("host") ?? "").split(":")[0];
13367
13487
  if (isPublicHost(host)) return c.text("Not found", 404);
13368
13488
  return c.html(cachedHtml("graph.html"));
13369
13489
  });
13370
- app38.get("/data", (c) => {
13490
+ app39.get("/data", (c) => {
13371
13491
  const host = (c.req.header("host") ?? "").split(":")[0];
13372
13492
  if (isPublicHost(host)) return c.text("Not found", 404);
13373
13493
  return c.html(cachedHtml("data.html"));
13374
13494
  });
13375
- app38.get("/:slug", async (c, next) => {
13495
+ app39.get("/:slug", async (c, next) => {
13376
13496
  const slug = c.req.param("slug");
13377
13497
  if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
13378
13498
  const branding = loadBrandingCache(slug);
@@ -13382,15 +13502,15 @@ app38.get("/:slug", async (c, next) => {
13382
13502
  await next();
13383
13503
  });
13384
13504
  if (brandFaviconPath !== "/favicon.ico") {
13385
- app38.get("/favicon.ico", (c) => {
13505
+ app39.get("/favicon.ico", (c) => {
13386
13506
  c.header("Cache-Control", "public, max-age=300");
13387
13507
  return c.redirect(brandFaviconPath, 302);
13388
13508
  });
13389
13509
  }
13390
- app38.use("/*", serveStatic({ root: "./public" }));
13510
+ app39.use("/*", serveStatic({ root: "./public" }));
13391
13511
  var port = parseInt(process.env.MAXY_UI_INTERNAL_PORT ?? process.env.PORT ?? "19199", 10);
13392
13512
  var hostname = process.env.HOSTNAME ?? "127.0.0.1";
13393
- var httpServer = serve({ fetch: app38.fetch, port, hostname });
13513
+ var httpServer = serve({ fetch: app39.fetch, port, hostname });
13394
13514
  console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
13395
13515
  console.log("[boot] auth-mode summary: oauth=8 api-key=1 (api-key consumer: invokePublicAgent only)");
13396
13516
  var SUBAPP_MANIFEST = [
@@ -13411,7 +13531,7 @@ for (const m of SUBAPP_MANIFEST) {
13411
13531
  }
13412
13532
  try {
13413
13533
  const registered = [];
13414
- for (const r of app38.routes ?? []) {
13534
+ for (const r of app39.routes ?? []) {
13415
13535
  if (typeof r.path !== "string" || r.path.includes(":") || r.path.includes("*")) continue;
13416
13536
  if (AGENT_SLUG_PATTERN.test(r.path)) {
13417
13537
  registered.push({ method: (r.method ?? "ALL").toUpperCase(), path: r.path });
@@ -13443,6 +13563,13 @@ try {
13443
13563
  console.error(`[session] backfill startup failed: ${err instanceof Error ? err.message : String(err)}`);
13444
13564
  }
13445
13565
  })();
13566
+ (async () => {
13567
+ try {
13568
+ await backfillConversationChannelAddress();
13569
+ } catch (err) {
13570
+ console.error(`[session-985] channelAddress backfill startup failed: ${err instanceof Error ? err.message : String(err)}`);
13571
+ }
13572
+ })();
13446
13573
  (async () => {
13447
13574
  try {
13448
13575
  if (!existsSync23(USERS_FILE)) return;
@@ -13497,15 +13624,14 @@ autoDeliverPremiumPlugins(bootEntitlement?.purchasedPlugins ?? void 0);
13497
13624
  (async () => {
13498
13625
  if (!bootAccount) return;
13499
13626
  try {
13500
- const { recoverRunningCloudflareTasks } = await import("./cloudflare-task-tracker-M7APAYEF.js");
13627
+ const { recoverRunningCloudflareTasks } = await import("./cloudflare-task-tracker-HUTXJQXO.js");
13501
13628
  const result = await recoverRunningCloudflareTasks(
13502
13629
  bootAccount.accountId,
13503
13630
  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.
13631
+ // Task 985 — no conversationId at boot. The reconciler operates on
13632
+ // Tasks whose Conversation is already linked via RAISED_DURING; the
13633
+ // tracker resolves the conversationId off that edge when present.
13634
+ // Pass null and let the recovery path pick it up.
13509
13635
  null
13510
13636
  );
13511
13637
  if (result.scanned > 0) {
@@ -13587,10 +13713,10 @@ init({
13587
13713
  agentName = resolved.slug;
13588
13714
  }
13589
13715
  let enrichedText = msg.text || msg.mediaPath || msg.mediaType || msg.replyContext ? buildEnrichedText2(msg.text ?? "") : "";
13590
- if (msg.sessionKey) {
13716
+ if (msg.cacheKey) {
13591
13717
  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`);
13718
+ registerSession(msg.cacheKey, msg.agentType, platformAccountId, agentName);
13719
+ console.error(`[session] channel session registered: cacheKey=${msg.cacheKey.slice(0, 12)}\u2026 channel=whatsapp accountId=${platformAccountId.slice(0, 8)}\u2026`);
13594
13720
  }
13595
13721
  let gatewayResult;
13596
13722
  if (msg.text) {
@@ -13614,7 +13740,7 @@ init({
13614
13740
  for await (const event of invokeAgent(
13615
13741
  { type: msg.agentType, agentName },
13616
13742
  enrichedText,
13617
- msg.sessionKey,
13743
+ msg.cacheKey,
13618
13744
  [],
13619
13745
  void 0,
13620
13746
  gatewayResult
@@ -13632,7 +13758,7 @@ init({
13632
13758
  for await (const event of invokeAgent(
13633
13759
  { type: msg.agentType, agentName },
13634
13760
  enrichedText,
13635
- msg.sessionKey,
13761
+ msg.cacheKey,
13636
13762
  [],
13637
13763
  void 0,
13638
13764
  gatewayResult