@rubytech/create-realagent 1.0.815 → 1.0.817

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 (41) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/admin/PLUGIN.md +4 -2
  3. package/payload/platform/plugins/admin/skills/onboarding/SKILL.md +2 -2
  4. package/payload/platform/plugins/cloudflare/PLUGIN.md +2 -2
  5. package/payload/platform/plugins/docs/references/cloudflare.md +3 -5
  6. package/payload/platform/plugins/docs/references/deployment.md +12 -12
  7. package/payload/platform/plugins/docs/references/internals.md +24 -24
  8. package/payload/platform/plugins/docs/references/memory-guide.md +1 -1
  9. package/payload/platform/plugins/docs/references/outlook-guide.md +1 -1
  10. package/payload/platform/plugins/docs/references/plugins-guide.md +8 -8
  11. package/payload/platform/plugins/docs/references/troubleshooting.md +41 -45
  12. package/payload/platform/plugins/memory/PLUGIN.md +4 -4
  13. package/payload/platform/plugins/whatsapp/PLUGIN.md +10 -4
  14. package/payload/platform/plugins/whatsapp/mcp/dist/index.js +80 -0
  15. package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
  16. package/payload/platform/plugins/whatsapp-import/PLUGIN.md +4 -4
  17. package/payload/platform/templates/agents/admin/IDENTITY.md +2 -0
  18. package/payload/platform/templates/specialists/agents/personal-assistant.md +2 -2
  19. package/payload/server/chunk-P3HTEK33.js +10074 -0
  20. package/payload/server/chunk-UYLZDEMC.js +1114 -0
  21. package/payload/server/chunk-Y3UQFQM7.js +10067 -0
  22. package/payload/server/client-pool-BMPFHXHB.js +31 -0
  23. package/payload/server/maxy-edge.js +2 -2
  24. package/payload/server/public/assets/{Checkbox-DZxF6s72.js → Checkbox-CTGhpDKq.js} +1 -1
  25. package/payload/server/public/assets/{admin-CTb65MiO.js → admin-2w0XSMC6.js} +20 -20
  26. package/payload/server/public/assets/data-Y77FLKjs.js +1 -0
  27. package/payload/server/public/assets/graph-C4-jEPDE.js +1 -0
  28. package/payload/server/public/assets/{jsx-runtime-Cb4WEnIV.css → jsx-runtime-D4WovFYk.css} +1 -1
  29. package/payload/server/public/assets/{page-BLanFGXC.js → page-DkBfWy4C.js} +1 -1
  30. package/payload/server/public/assets/{page-DuwlF8N5.js → page-zuI00fuC.js} +1 -1
  31. package/payload/server/public/assets/{public-BqeUfasT.js → public-BdVIVpv8.js} +1 -1
  32. package/payload/server/public/assets/{useAdminFetch-DLGqK3Fs.js → useAdminFetch-DmHu0oCx.js} +1 -1
  33. package/payload/server/public/assets/{useVoiceRecorder-CHPkBGVV.js → useVoiceRecorder-CSc_hxjV.js} +1 -1
  34. package/payload/server/public/data.html +5 -5
  35. package/payload/server/public/graph.html +6 -6
  36. package/payload/server/public/index.html +8 -8
  37. package/payload/server/public/public.html +5 -5
  38. package/payload/server/server.js +243 -409
  39. package/payload/server/public/assets/data-BO12TydY.js +0 -1
  40. package/payload/server/public/assets/graph-XIePTyWQ.js +0 -1
  41. /package/payload/server/public/assets/{jsx-runtime-BZMOvG0f.js → jsx-runtime-DkaAusaX.js} +0 -0
@@ -6,7 +6,6 @@ import {
6
6
  TELEGRAM_ADMIN_WEBHOOK_SECRET_FILE,
7
7
  TELEGRAM_WEBHOOK_SECRET_FILE,
8
8
  USERS_FILE,
9
- actionLogPath,
10
9
  autoDeliverPremiumPlugins,
11
10
  buildX11Env,
12
11
  callOauthLlm,
@@ -30,7 +29,6 @@ import {
30
29
  launchAction,
31
30
  load,
32
31
  logPath,
33
- reconcileCloudflareSetupFromLog,
34
32
  recordFailedAttempt,
35
33
  render,
36
34
  renderLoginPage,
@@ -53,7 +51,7 @@ import {
53
51
  vncLog,
54
52
  waitForExit,
55
53
  writeChromiumWrapper
56
- } from "./chunk-42BMMSRN.js";
54
+ } from "./chunk-P3HTEK33.js";
57
55
  import {
58
56
  agentLogStream,
59
57
  clearSessionHistory,
@@ -81,7 +79,7 @@ import {
81
79
  sigtermFlushStreamLogs,
82
80
  unregisterSession,
83
81
  validateSession
84
- } from "./chunk-52LIWKMM.js";
82
+ } from "./chunk-UYLZDEMC.js";
85
83
  import {
86
84
  ACCOUNTS_DIR,
87
85
  GREETING_DIRECTIVE,
@@ -620,8 +618,8 @@ var serveStatic = (options = { root: "" }) => {
620
618
  };
621
619
 
622
620
  // server/index.ts
623
- import { readFileSync as readFileSync19, existsSync as existsSync25, watchFile } from "fs";
624
- import { resolve as resolve25, join as join11, basename as basename7 } from "path";
621
+ import { readFileSync as readFileSync18, existsSync as existsSync24, watchFile } from "fs";
622
+ import { resolve as resolve25, join as join10, basename as basename7 } from "path";
625
623
  import { homedir as homedir2 } from "os";
626
624
 
627
625
  // app/lib/agent-slug-pattern.ts
@@ -807,52 +805,6 @@ function defaultRules() {
807
805
  scope: "session",
808
806
  suggestedAction: "The WebFetch SPA preflight has fired more than once in this conversation. Either the agent is ignoring the loud-failure directive (retrying WebFetch after seeing WEBFETCH_CANNOT_READ_JS_SPA), or multiple SPA URLs are being asked about. Read the conversation's stream log for the [tool-use] / [tool-result] sequence around each occurrence \u2014 if the agent dispatched WebFetch on the same URL or substituted Playwright silently, revisit the IDENTITY.md `Tool Failure Discipline` paragraph that names structured-error handling."
809
807
  },
810
- {
811
- // Task 867 — fires when setup-tunnel.sh emits step=done but no
812
- // `[persist] role=user … Cloudflare setup completed (actionId: <id>)`
813
- // line appears within 60s. Covers the three failure modes of the
814
- // action-relay-queue plumbing: queue-write failed, boot-drain consumer
815
- // skipped the record, or the agent's hoisted persist threw. The
816
- // followup pattern is intentionally narrow to the cloudflare-setup
817
- // relay shape (not a general "any user persist") so a benign user
818
- // typing in chat does not satisfy the followup. logSource=any so
819
- // the rule sees both the script tee (in stream logs / server.log)
820
- // and the [persist] line (server.log).
821
- id: "cloudflare-setup-relay-not-acknowledged",
822
- name: "Cloudflare-setup completed but the chat relay never acknowledged",
823
- type: "absent-followup",
824
- logSource: "any",
825
- pattern: "\\[script:setup-tunnel\\] step=done",
826
- followupPattern: "\\[persist\\] .*role=user.* Cloudflare setup completed \\(actionId:",
827
- followupWindowMs: 6e4,
828
- thresholdCount: 0,
829
- thresholdWindowMinutes: 0,
830
- suggestedAction: "[Task 867] cloudflare-setup completed but the post-action relay never reached the chat history. Check `[action-relay-queue] phase=enqueued` (queue write), `[action-completion-relay] phase=consumed` (boot-drain ran), and `[persist] role=user \u2026 Cloudflare setup completed` (graph write). One of those is missing; the matching grep recipe is in the Task 867 brief Observability section."
831
- },
832
- {
833
- // Task 879 §C — onboarding turn-completion contract: any assistant
834
- // turn that narrates a step transition with a non-question-terminated
835
- // phrase ("Moving to step 9 — operator persona...", "Step 8 done.")
836
- // must be followed within 30s by either a `render-component` call
837
- // (the next step's UI surfaces) or `onboarding-complete-step`
838
- // (deterministic step advance). The trigger pattern matches the
839
- // `[onboarding-step-narration]` line written by stream-parser at the
840
- // assistant text block emit site (only for non-question phrases).
841
- // Operator's symptom in Task 879's evidence: "Moving to step 9..."
842
- // followed by 4 minutes of silence until the operator nudged with
843
- // "yeah, so?" — exact failure shape this rule catches.
844
- id: "assistant-step-advance-deadend",
845
- name: "Onboarding step-advance narration without follow-up tool call",
846
- type: "absent-followup",
847
- logSource: "session",
848
- pattern: "\\[onboarding-step-narration\\]",
849
- followupPattern: "\\[render-component\\]|\\[tool-use\\][^\\n]*name=mcp__admin__onboarding-complete-step",
850
- followupWindowMs: 3e4,
851
- thresholdCount: 0,
852
- thresholdWindowMinutes: 0,
853
- scope: "session",
854
- suggestedAction: '[Task 879 \xA7C] An onboarding turn narrated a step transition (e.g. "Moving to step N", "Step N done.") but did not render a component or call `onboarding-complete-step` within 30s. The operator is left with a dead-end paragraph and no actionable surface. Check the `platform/plugins/admin/skills/onboarding/SKILL.md` Turn-completion contract section \u2014 the agent must end any step-advance turn with `render-component` or a `?`-terminated question, never bare prose.'
855
- },
856
808
  {
857
809
  // Task 538: fires when a [spawn] line appears in a conversation's stream
858
810
  // log but no subprocess-lifecycle marker follows within 10s. The three
@@ -2749,19 +2701,25 @@ function maybeRestoreCredsFromBackup(authDir) {
2749
2701
  function readSelfId(authDir) {
2750
2702
  try {
2751
2703
  const credsPath = resolveCredsPath(authDir);
2752
- if (!fsSync.existsSync(credsPath)) return { e164: null, jid: null };
2704
+ if (!fsSync.existsSync(credsPath)) return { e164: null, jid: null, lid: null };
2753
2705
  const raw = fsSync.readFileSync(credsPath, "utf-8");
2754
2706
  const parsed = JSON.parse(raw);
2755
2707
  const jid = parsed?.me?.id ?? null;
2708
+ const lidRaw = parsed?.me?.lid ?? null;
2756
2709
  let e164 = null;
2757
2710
  if (jid) {
2758
2711
  const match = jid.match(/^(\d+)(?::\d+)?@s\.whatsapp\.net$/i);
2759
2712
  if (match) e164 = match[1];
2760
2713
  }
2761
- return { e164, jid };
2714
+ let lid = null;
2715
+ if (lidRaw) {
2716
+ const lidMatch = lidRaw.match(/^(\d+)(?::\d+)?@lid$/i);
2717
+ if (lidMatch) lid = `${lidMatch[1]}@lid`;
2718
+ }
2719
+ return { e164, jid, lid };
2762
2720
  } catch (err) {
2763
2721
  console.warn(`${TAG2} readSelfId failed authDir=${authDir}: ${String(err)}`);
2764
- return { e164: null, jid: null };
2722
+ return { e164: null, jid: null, lid: null };
2765
2723
  }
2766
2724
  }
2767
2725
  async function clearAuth(authDir) {
@@ -3246,6 +3204,20 @@ function phonesMatch(a, b) {
3246
3204
  function isLidJid(jid) {
3247
3205
  return WHATSAPP_LID_RE.test(jid);
3248
3206
  }
3207
+ function withSelfLidShortcut(base, selfLid, selfJid) {
3208
+ if (!selfLid || !selfJid) return base;
3209
+ return {
3210
+ getPNForLID: async (lid) => {
3211
+ if (lid === selfLid) return selfJid;
3212
+ try {
3213
+ return await base?.getPNForLID(lid);
3214
+ } catch (err) {
3215
+ console.error(`[whatsapp:normalize] base lidMapping.getPNForLID threw lid=${lid}: ${String(err)}`);
3216
+ return void 0;
3217
+ }
3218
+ }
3219
+ };
3220
+ }
3249
3221
  async function resolveJidToE164(jid, lidMapping) {
3250
3222
  if (!jid) return null;
3251
3223
  const userMatch = jid.match(WHATSAPP_USER_JID_RE);
@@ -4367,6 +4339,7 @@ async function startConnection(accountId) {
4367
4339
  connected: false,
4368
4340
  selfPhone: null,
4369
4341
  selfJid: null,
4342
+ selfLid: null,
4370
4343
  reconnectAttempts: 0,
4371
4344
  abortController: new AbortController(),
4372
4345
  debouncer: null,
@@ -4430,7 +4403,13 @@ async function registerLoginSocket(accountId, sock, authDir) {
4430
4403
  }
4431
4404
  await stopConnection(accountId);
4432
4405
  const selfId = readSelfId(authDir);
4433
- const lidMapping = sock.signalRepository?.lidMapping ?? null;
4406
+ const rawLidMapping = sock.signalRepository?.lidMapping ?? null;
4407
+ const lidMapping = withSelfLidShortcut(rawLidMapping, selfId.lid, selfId.jid);
4408
+ if (selfId.lid) {
4409
+ console.error(`[whatsapp:lid] resolved self-lid accountId=${accountId} selfLid=${selfId.lid} source=creds`);
4410
+ } else {
4411
+ console.error(`[whatsapp:lid] FAIL self-lid-resolution accountId=${accountId} reason=creds-missing-lid`);
4412
+ }
4434
4413
  const conn = {
4435
4414
  accountId,
4436
4415
  platformAccountId,
@@ -4440,6 +4419,7 @@ async function registerLoginSocket(accountId, sock, authDir) {
4440
4419
  connected: true,
4441
4420
  selfPhone: selfId.e164,
4442
4421
  selfJid: selfId.jid,
4422
+ selfLid: selfId.lid,
4443
4423
  reconnectAttempts: 0,
4444
4424
  lastConnectedAt: Date.now(),
4445
4425
  abortController: new AbortController(),
@@ -4463,8 +4443,36 @@ async function registerLoginSocket(accountId, sock, authDir) {
4463
4443
  });
4464
4444
  monitorInbound(conn);
4465
4445
  watchForDisconnect(conn);
4446
+ attachSelfIdRefreshOnCredsUpdate(conn);
4466
4447
  console.error(`${TAG13} registered login socket for account=${accountId} phone=${selfId.e164 ?? "unknown"}`);
4467
4448
  }
4449
+ function attachSelfIdRefreshOnCredsUpdate(conn) {
4450
+ if (!conn.sock) return;
4451
+ const sock = conn.sock;
4452
+ const handler = () => {
4453
+ setTimeout(() => {
4454
+ try {
4455
+ const fresh = readSelfId(conn.authDir);
4456
+ const lidChanged = fresh.lid !== conn.selfLid;
4457
+ conn.selfPhone = fresh.e164;
4458
+ conn.selfJid = fresh.jid;
4459
+ conn.selfLid = fresh.lid;
4460
+ const rawLidMapping = sock?.signalRepository?.lidMapping ?? null;
4461
+ conn.lidMapping = withSelfLidShortcut(rawLidMapping, fresh.lid, fresh.jid);
4462
+ if (lidChanged) {
4463
+ if (fresh.lid) {
4464
+ console.error(`[whatsapp:lid] resolved self-lid accountId=${conn.accountId} selfLid=${fresh.lid} source=creds-update`);
4465
+ } else {
4466
+ console.error(`[whatsapp:lid] FAIL self-lid-resolution accountId=${conn.accountId} reason=creds-update-cleared-lid`);
4467
+ }
4468
+ }
4469
+ } catch (err) {
4470
+ console.error(`[whatsapp:lid] FAIL self-lid-refresh accountId=${conn.accountId} reason=${String(err).slice(0, 80)}`);
4471
+ }
4472
+ }, 1e3).unref?.();
4473
+ };
4474
+ sock.ev.on("creds.update", handler);
4475
+ }
4468
4476
  function reloadConfig(accountConfig) {
4469
4477
  loadConfig(accountConfig);
4470
4478
  }
@@ -4528,9 +4536,16 @@ async function connectWithReconnect(conn) {
4528
4536
  conn.connected = true;
4529
4537
  conn.selfPhone = selfId.e164;
4530
4538
  conn.selfJid = selfId.jid;
4539
+ conn.selfLid = selfId.lid;
4531
4540
  conn.lastConnectedAt = connectedAt;
4532
4541
  conn.lastError = void 0;
4533
- conn.lidMapping = sock.signalRepository?.lidMapping ?? null;
4542
+ const rawLidMapping = sock.signalRepository?.lidMapping ?? null;
4543
+ conn.lidMapping = withSelfLidShortcut(rawLidMapping, selfId.lid, selfId.jid);
4544
+ if (selfId.lid) {
4545
+ console.error(`[whatsapp:lid] resolved self-lid accountId=${conn.accountId} selfLid=${selfId.lid} source=creds`);
4546
+ } else {
4547
+ console.error(`[whatsapp:lid] FAIL self-lid-resolution accountId=${conn.accountId} reason=creds-missing-lid`);
4548
+ }
4534
4549
  console.error(`${TAG13} connected account=${conn.accountId} phone=${selfId.e164 ?? "unknown"}`);
4535
4550
  try {
4536
4551
  await sock.sendPresenceUpdate("available");
@@ -4550,6 +4565,7 @@ async function connectWithReconnect(conn) {
4550
4565
  conn.debouncer = null;
4551
4566
  }
4552
4567
  monitorInbound(conn);
4568
+ attachSelfIdRefreshOnCredsUpdate(conn);
4553
4569
  await waitForDisconnectEvent(conn);
4554
4570
  uptimeMs = Date.now() - connectedAt;
4555
4571
  conn.connected = false;
@@ -4900,8 +4916,16 @@ async function handleInboundMessage(conn, msg) {
4900
4916
  senderPhone,
4901
4917
  selfPhone,
4902
4918
  groupJid: remoteJid,
4919
+ // Three-way mention recognition (Sub-fix 4): legacy phone-JID equality,
4920
+ // legacy E.164-via-jidToE164 equality, plus the new self-LID equality so
4921
+ // mentions in LID-addressed groups (deliver as `<self-lid>@lid`) match.
4922
+ // Pre-LID groups continue matching the first two — additive, never blocks.
4923
+ // Each clause guards against null on its right operand; a creds-parse
4924
+ // failure leaves all three of selfJid/selfLid/selfPhone null and
4925
+ // jidToE164 of a malformed mention also returns null — without the
4926
+ // guards, `null === null` would spuriously match every nullified field.
4903
4927
  isMentioned: extracted.mentionedJids.some(
4904
- (jid) => jid === conn.selfJid || jidToE164(jid) === conn.selfPhone
4928
+ (jid) => conn.selfJid !== null && jid === conn.selfJid || conn.selfLid !== null && jid === conn.selfLid || conn.selfPhone !== null && jidToE164(jid) === conn.selfPhone
4905
4929
  ),
4906
4930
  config: whatsAppConfig,
4907
4931
  accountConfig: whatsAppConfig.accounts?.[conn.accountId]
@@ -7801,6 +7825,93 @@ app7.get("/messages", (c) => {
7801
7825
  return c.json({ error: String(err) }, 500);
7802
7826
  }
7803
7827
  });
7828
+ app7.get("/conversation-graph-state", async (c) => {
7829
+ try {
7830
+ const directSessionKey = c.req.query("sessionKey");
7831
+ const jid = c.req.query("jid");
7832
+ const accountIdQuery = c.req.query("accountId");
7833
+ let sessionKey;
7834
+ if (directSessionKey) {
7835
+ sessionKey = directSessionKey;
7836
+ } else {
7837
+ if (!jid) {
7838
+ return c.json({ error: "Provide either sessionKey or jid (with optional accountId)." }, 400);
7839
+ }
7840
+ const accountId = validateAccountId(accountIdQuery ?? null);
7841
+ if (isGroupJid(jid)) {
7842
+ sessionKey = deriveSessionKey({
7843
+ agentType: "public",
7844
+ accountId,
7845
+ senderPhone: "",
7846
+ isGroup: true,
7847
+ groupJid: jid
7848
+ });
7849
+ } else {
7850
+ const e164 = jid.replace(/^(\d+)(?::\d+)?@s\.whatsapp\.net$/i, "$1");
7851
+ if (!/^\d+$/.test(e164)) {
7852
+ return c.json({ error: `Cannot derive sessionKey from jid=${jid} \u2014 not a recognised user JID. Pass sessionKey explicitly.` }, 400);
7853
+ }
7854
+ sessionKey = deriveSessionKey({
7855
+ agentType: "public",
7856
+ accountId,
7857
+ senderPhone: e164,
7858
+ isGroup: false
7859
+ });
7860
+ }
7861
+ }
7862
+ const cypher = `
7863
+ MATCH (c:Conversation {sessionKey: $sessionKey})
7864
+ OPTIONAL MATCH (m:Message:WhatsAppMessage)-[:PART_OF]->(c)
7865
+ RETURN
7866
+ c.conversationId AS conversationId,
7867
+ m.messageId AS messageId,
7868
+ m.dateSent AS dateSent,
7869
+ m.fromMe AS fromMe,
7870
+ m.senderTelephone AS senderTelephone,
7871
+ m.body AS body
7872
+ ORDER BY dateSent ASC
7873
+ `;
7874
+ let session = null;
7875
+ const t0 = Date.now();
7876
+ let conversationId = null;
7877
+ const rows = [];
7878
+ try {
7879
+ session = getSession();
7880
+ const result = await session.run(cypher, { sessionKey });
7881
+ for (const rec of result.records) {
7882
+ if (conversationId === null) {
7883
+ conversationId = rec.get("conversationId") ?? null;
7884
+ }
7885
+ const messageId = rec.get("messageId");
7886
+ if (!messageId) continue;
7887
+ const dateSentRaw = rec.get("dateSent");
7888
+ rows.push({
7889
+ messageId,
7890
+ dateSent: dateSentRaw ? String(dateSentRaw) : null,
7891
+ fromMe: rec.get("fromMe") === true,
7892
+ senderTelephone: rec.get("senderTelephone") ?? null,
7893
+ body: rec.get("body") ?? null
7894
+ });
7895
+ }
7896
+ } catch (err) {
7897
+ const msg = err instanceof Error ? err.message : String(err);
7898
+ console.error(`${TAG18} conversation-graph-state ERR sessionKey=${sessionKey} reason=${msg}`);
7899
+ return c.json({ error: `Graph query failed: ${msg}`, sessionKey, cypher: cypher.trim() }, 500);
7900
+ }
7901
+ const ms = Date.now() - t0;
7902
+ console.error(`[mcp:whatsapp] tool=whatsapp-conversation-graph-state sessionKey=${sessionKey} graphRows=${rows.length} conversationId=${conversationId ?? "null"} ms=${ms}`);
7903
+ return c.json({
7904
+ sessionKey,
7905
+ conversationId,
7906
+ graphRows: rows,
7907
+ cypher: cypher.trim(),
7908
+ ms
7909
+ });
7910
+ } catch (err) {
7911
+ console.error(`${TAG18} conversation-graph-state error: ${String(err)}`);
7912
+ return c.json({ error: String(err) }, 500);
7913
+ }
7914
+ });
7804
7915
  app7.get("/group-info", async (c) => {
7805
7916
  const accountId = validateAccountId(c.req.query("accountId") ?? null);
7806
7917
  const jid = c.req.query("jid");
@@ -8216,7 +8327,7 @@ app9.post("/", async (c) => {
8216
8327
  const safe = typeof v === "string" ? truncate(v, 200) : typeof v === "number" || typeof v === "boolean" ? String(v) : JSON.stringify(v).slice(0, 200);
8217
8328
  return `${k}=${safe}`;
8218
8329
  }).join(" ");
8219
- const TAGGED_PREFIX_SOURCES = /* @__PURE__ */ new Set(["admin-chat-relay-poll"]);
8330
+ const TAGGED_PREFIX_SOURCES = /* @__PURE__ */ new Set([]);
8220
8331
  if (TAGGED_PREFIX_SOURCES.has(source)) {
8221
8332
  console.log(
8222
8333
  `[${source}] ts=${ts} ip=${ip} version=${version || "unknown"}${extra ? " " + extra : ""}`
@@ -8438,39 +8549,6 @@ app10.post("/", async (c) => {
8438
8549
  const payload = await createAdminSession(selected.accountId, selected.config.thinkingView, userId, userName, selected.role, avatar);
8439
8550
  return c.json(payload);
8440
8551
  });
8441
- app10.post("/rebind", async (c) => {
8442
- let body;
8443
- try {
8444
- body = await c.req.json();
8445
- } catch {
8446
- return c.json({ ok: false, error: "invalid_json" }, 400);
8447
- }
8448
- const sessionKey = typeof body.session_key === "string" ? body.session_key : "";
8449
- const conversationId = typeof body.lastKnownConversationId === "string" ? body.lastKnownConversationId : "";
8450
- if (!sessionKey || !conversationId) {
8451
- return c.json({ ok: false, error: "session_key and lastKnownConversationId required" }, 400);
8452
- }
8453
- const sk8 = sessionKey.slice(0, 8);
8454
- const cid8 = conversationId.slice(0, 8);
8455
- const bridge = await tryCookieBridgeForConversation(c, sessionKey, conversationId);
8456
- if (!bridge.ok) {
8457
- console.log(`[admin/session/rebind] sessionKey=${sk8}\u2026 result=bridge-rejected reason=${bridge.reason} conversationId=${cid8}\u2026`);
8458
- const status = bridge.reason === "conversation-not-found" ? 404 : bridge.reason === "account-mismatch" ? 403 : 401;
8459
- return c.json({ ok: false, error: bridge.reason }, status);
8460
- }
8461
- const existing = getConversationIdForSession(sessionKey);
8462
- if (existing && existing !== conversationId) {
8463
- console.log(`[admin/session/rebind] sessionKey=${sk8}\u2026 result=conflict conversationId=${existing.slice(0, 8)}\u2026 requested=${cid8}\u2026`);
8464
- return c.json({ ok: false, error: "conflict", conversationId: existing }, 409);
8465
- }
8466
- const bound = setConversationIdForSession(sessionKey, conversationId);
8467
- if (!bound) {
8468
- console.error(`[admin/session/rebind] sessionKey=${sk8}\u2026 result=bind-failed conversationId=${cid8}\u2026 (post-bridge session-store missing \u2014 programmer error)`);
8469
- return c.json({ ok: false, error: "bind_failed" }, 500);
8470
- }
8471
- console.log(`[admin/session/rebind] sessionKey=${sk8}\u2026 result=ok conversationId=${cid8}\u2026`);
8472
- return c.json({ ok: true, conversationId });
8473
- });
8474
8552
  var session_default2 = app10;
8475
8553
 
8476
8554
  // server/routes/admin/chat.ts
@@ -8765,7 +8843,7 @@ var app11 = new Hono();
8765
8843
  app11.post("/cancel", requireAdminSession, async (c) => {
8766
8844
  const session_key = c.var.sessionKey;
8767
8845
  try {
8768
- const { interruptClient: interruptClient2 } = await import("./client-pool-AIYWSJBR.js");
8846
+ const { interruptClient: interruptClient2 } = await import("./client-pool-BMPFHXHB.js");
8769
8847
  await interruptClient2(session_key);
8770
8848
  return c.json({ ok: true });
8771
8849
  } catch (err) {
@@ -9701,13 +9779,14 @@ app17.delete("/:id", requireAdminSession, async (c) => {
9701
9779
  return c.json({ error: "Failed to delete session" }, 500);
9702
9780
  }
9703
9781
  });
9704
- app17.get("/:id/messages", async (c) => {
9782
+ app17.post("/:id/resume", async (c) => {
9705
9783
  const conversationId = c.req.param("id");
9706
9784
  const sessionKey = c.req.query("session_key") ?? "";
9707
9785
  if (!sessionKey) {
9708
9786
  console.error(`[session] middleware-reject status=400 code=session-missing reason="session_key required" path=${c.req.path}`);
9709
9787
  return c.json({ error: "session_key required", code: "session-missing" }, 400);
9710
9788
  }
9789
+ let bridged = false;
9711
9790
  let result = validateSession(sessionKey, "admin");
9712
9791
  if (!result.ok) {
9713
9792
  if (result.reason === "session-not-registered") {
@@ -9720,6 +9799,7 @@ app17.get("/:id/messages", async (c) => {
9720
9799
  console.error(`[session] middleware-reject status=401 code=session-not-registered reason="cookie-bridge-rejected:${bridge.reason}" path=${c.req.path} sessionKey=${tail}\u2026`);
9721
9800
  return c.json({ error: "Invalid or expired admin session", code: "session-not-registered" }, 401);
9722
9801
  }
9802
+ bridged = true;
9723
9803
  result = validateSession(sessionKey, "admin");
9724
9804
  if (!result.ok) {
9725
9805
  const tail = sessionKey.slice(0, 8);
@@ -9734,34 +9814,12 @@ app17.get("/:id/messages", async (c) => {
9734
9814
  }
9735
9815
  }
9736
9816
  const accountId = getAccountIdForSession(sessionKey);
9737
- if (!accountId) return c.json({ error: "Account not found for session" }, 401);
9738
- const owned = await verifyConversationOwnership(conversationId, accountId);
9739
- if (!owned) return c.json({ error: "Conversation not found" }, 404);
9740
- try {
9741
- const messages = await getRecentMessages(conversationId, 50);
9742
- const sanitised = messages.map((m) => ({
9743
- ...m,
9744
- attachments: m.attachments.map((a) => ({
9745
- attachmentId: a.attachmentId,
9746
- filename: a.filename,
9747
- mimeType: a.mimeType,
9748
- sizeBytes: a.sizeBytes,
9749
- ordinal: a.ordinal
9750
- }))
9751
- }));
9752
- return c.json({ messages: sanitised });
9753
- } catch (err) {
9754
- console.error(`[sessions-messages] Failed: ${err instanceof Error ? err.message : String(err)}`);
9755
- return c.json({ error: "Failed to fetch messages" }, 500);
9756
- }
9757
- });
9758
- app17.post("/:id/resume", requireAdminSession, async (c) => {
9759
- const conversationId = c.req.param("id");
9760
- const sessionKey = c.var.sessionKey;
9761
- const accountId = getAccountIdForSession(sessionKey);
9762
9817
  const userId = getUserIdForSession(sessionKey);
9763
- if (!accountId || !userId) {
9764
- return c.json({ error: "Session missing account or user context" }, 400);
9818
+ if (!accountId) {
9819
+ return c.json({ error: "Session missing account context" }, 400);
9820
+ }
9821
+ if (!userId && !bridged) {
9822
+ return c.json({ error: "Session missing user context" }, 400);
9765
9823
  }
9766
9824
  const updatedAt = await verifyAndGetConversationUpdatedAt(conversationId, accountId);
9767
9825
  if (updatedAt === null) return c.json({ error: "Conversation not found" }, 404);
@@ -9816,8 +9874,9 @@ app17.post("/:id/resume", requireAdminSession, async (c) => {
9816
9874
  });
9817
9875
  const textRuns = rehydrated.reduce((n, m) => n + (m.events?.filter((e) => e.type === "text").length ?? 0), 0);
9818
9876
  const userMessageCount = rehydrated.filter((m) => m.role !== "assistant").length;
9877
+ const reason = bridged ? "post-restart" : "page-refresh";
9819
9878
  try {
9820
- appendFileSync5(streamLogPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [admin-resume] sessionKey=${sessionKey.slice(0, 8)} conversationId=${conversationId.slice(0, 8)} ${tag} loadedMessages=${messages.length} componentCount=${totalComponents} userAttachmentCount=${totalAttachments}
9879
+ appendFileSync5(streamLogPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [admin-resume] reason=${reason} sessionKey=${sessionKey.slice(0, 8)} conversationId=${conversationId.slice(0, 8)} ${tag} loadedMessages=${messages.length} componentCount=${totalComponents} userAttachmentCount=${totalAttachments}
9821
9880
  `);
9822
9881
  if (totalComponents > 0) {
9823
9882
  appendFileSync5(streamLogPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] [component-rehydrate] conversationId=${conversationId.slice(0, 8)} count=${totalComponents} valid=${totalValid} invalid=${totalInvalid} textRuns=${textRuns}
@@ -9830,7 +9889,7 @@ app17.post("/:id/resume", requireAdminSession, async (c) => {
9830
9889
  } catch {
9831
9890
  }
9832
9891
  const age = formatAge(updatedAt);
9833
- console.log(`[admin-resume] ${(/* @__PURE__ */ new Date()).toISOString()} conversationId=${conversationId.slice(0, 8)}\u2026 age=${age} loaded=${messages.length} messages ${tag} components=${totalComponents} attachments=${totalAttachments} sessionKey=${sessionKey.slice(0, 8)}\u2026`);
9892
+ 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} sessionKey=${sessionKey.slice(0, 8)}\u2026`);
9834
9893
  return c.json({ conversationId, messages: rehydrated });
9835
9894
  });
9836
9895
  app17.post("/:id/label", requireAdminSession, async (c) => {
@@ -9957,13 +10016,13 @@ async function cdpNavigateNewTab(url, opts = {}) {
9957
10016
  // server/routes/admin/device-browser.ts
9958
10017
  var app19 = new Hono();
9959
10018
  app19.post("/navigate", async (c) => {
9960
- const TAG20 = "[device-url:click]";
10019
+ const TAG19 = "[device-url:click]";
9961
10020
  let body;
9962
10021
  try {
9963
10022
  body = await c.req.json();
9964
10023
  } catch (err) {
9965
10024
  const detail = err instanceof Error ? err.message : String(err);
9966
- console.error(`${TAG20} reject reason=body-not-json detail=${detail} browser=fallback navigateResult=error`);
10025
+ console.error(`${TAG19} reject reason=body-not-json detail=${detail} browser=fallback navigateResult=error`);
9967
10026
  return c.json(
9968
10027
  { ok: false, navigateResult: "error", browser: "fallback", detail: "Request body was not valid JSON" },
9969
10028
  400
@@ -9973,7 +10032,7 @@ app19.post("/navigate", async (c) => {
9973
10032
  const intent = typeof body.intent === "string" ? body.intent : "";
9974
10033
  const hostname2 = typeof body.hostname === "string" ? body.hostname : "";
9975
10034
  if (!url) {
9976
- console.error(`${TAG20} reject reason=missing-url intent=${JSON.stringify(intent)} browser=fallback navigateResult=error`);
10035
+ console.error(`${TAG19} reject reason=missing-url intent=${JSON.stringify(intent)} browser=fallback navigateResult=error`);
9977
10036
  return c.json(
9978
10037
  { ok: false, navigateResult: "error", browser: "fallback", detail: "url field is required" },
9979
10038
  400
@@ -9983,7 +10042,7 @@ app19.post("/navigate", async (c) => {
9983
10042
  try {
9984
10043
  parsed = new URL(url);
9985
10044
  } catch {
9986
- console.error(`${TAG20} reject reason=url-malformed intent=${JSON.stringify(intent)} url=${url} browser=fallback navigateResult=error`);
10045
+ console.error(`${TAG19} reject reason=url-malformed intent=${JSON.stringify(intent)} url=${url} browser=fallback navigateResult=error`);
9987
10046
  return c.json(
9988
10047
  { ok: false, navigateResult: "error", browser: "fallback", detail: "url is not a valid URL" },
9989
10048
  400
@@ -9991,7 +10050,7 @@ app19.post("/navigate", async (c) => {
9991
10050
  }
9992
10051
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
9993
10052
  console.error(
9994
- `${TAG20} reject reason=scheme-not-allowed scheme=${parsed.protocol} intent=${JSON.stringify(intent)} browser=fallback navigateResult=error`
10053
+ `${TAG19} reject reason=scheme-not-allowed scheme=${parsed.protocol} intent=${JSON.stringify(intent)} browser=fallback navigateResult=error`
9995
10054
  );
9996
10055
  return c.json(
9997
10056
  {
@@ -10007,7 +10066,7 @@ app19.post("/navigate", async (c) => {
10007
10066
  const cdpOk = await ensureCdp(transport);
10008
10067
  if (!cdpOk) {
10009
10068
  console.error(
10010
- `${TAG20} intent=${JSON.stringify(intent)} browser=fallback navigateResult=cdp-unreachable hostname=${JSON.stringify(hostname2)}`
10069
+ `${TAG19} intent=${JSON.stringify(intent)} browser=fallback navigateResult=cdp-unreachable hostname=${JSON.stringify(hostname2)}`
10011
10070
  );
10012
10071
  return c.json(
10013
10072
  {
@@ -10023,7 +10082,7 @@ app19.post("/navigate", async (c) => {
10023
10082
  const browser = outcome.result === "ok" ? "vnc" : "fallback";
10024
10083
  const detailStr = outcome.detail ? ` detail=${JSON.stringify(outcome.detail.length > 230 ? outcome.detail.slice(0, 227) + "..." : outcome.detail)}` : "";
10025
10084
  console.error(
10026
- `${TAG20} intent=${JSON.stringify(intent)} browser=${browser} navigateResult=${outcome.result} hostname=${JSON.stringify(hostname2)} targetId=${outcome.targetId ?? "none"}${detailStr}`
10085
+ `${TAG19} intent=${JSON.stringify(intent)} browser=${browser} navigateResult=${outcome.result} hostname=${JSON.stringify(hostname2)} targetId=${outcome.targetId ?? "none"}${detailStr}`
10027
10086
  );
10028
10087
  if (outcome.result !== "ok") {
10029
10088
  return c.json(
@@ -10054,18 +10113,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
10054
10113
  ]);
10055
10114
  var app20 = new Hono();
10056
10115
  app20.post("/", async (c) => {
10057
- const TAG20 = "[admin:events]";
10116
+ const TAG19 = "[admin:events]";
10058
10117
  let body;
10059
10118
  try {
10060
10119
  body = await c.req.json();
10061
10120
  } catch (err) {
10062
10121
  const detail = err instanceof Error ? err.message : String(err);
10063
- console.error(`${TAG20} reject reason=body-not-json detail=${detail}`);
10122
+ console.error(`${TAG19} reject reason=body-not-json detail=${detail}`);
10064
10123
  return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
10065
10124
  }
10066
10125
  const event = typeof body.event === "string" ? body.event : "";
10067
10126
  if (!ALLOWED_EVENTS.has(event)) {
10068
- console.error(`${TAG20} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
10127
+ console.error(`${TAG19} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
10069
10128
  return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
10070
10129
  }
10071
10130
  const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
@@ -10089,7 +10148,7 @@ var events_default = app20;
10089
10148
  // server/routes/admin/cloudflare.ts
10090
10149
  import { homedir } from "os";
10091
10150
  import { resolve as resolve18 } from "path";
10092
- import { readFileSync as readFileSync17 } from "fs";
10151
+ import { readFileSync as readFileSync16 } from "fs";
10093
10152
 
10094
10153
  // app/lib/dns-label.ts
10095
10154
  var VALID_LABEL = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
@@ -10127,131 +10186,14 @@ function addAliasDomain(hostname2) {
10127
10186
  writeFileSync9(ALIAS_DOMAINS_PATH, JSON.stringify([...existing], null, 2) + "\n", "utf-8");
10128
10187
  }
10129
10188
 
10130
- // app/lib/action-relay-queue.ts
10131
- import {
10132
- existsSync as existsSync20,
10133
- mkdirSync as mkdirSync9,
10134
- readdirSync as readdirSync6,
10135
- readFileSync as readFileSync16,
10136
- unlinkSync as unlinkSync2,
10137
- writeFileSync as writeFileSync10
10138
- } from "fs";
10139
- import { join as join9 } from "path";
10140
- var TAG19 = "[action-relay-queue]";
10141
- var FILE_PREFIX = "action-completion-relay-";
10142
- var FILE_SUFFIX = ".json";
10143
- function queueDir(accountDir) {
10144
- return join9(accountDir, "queue");
10145
- }
10146
- function relayFilePath(accountDir, actionId) {
10147
- return join9(queueDir(accountDir), `${FILE_PREFIX}${actionId}${FILE_SUFFIX}`);
10148
- }
10149
- function enqueueActionCompletionRelay(opts) {
10150
- const dir = queueDir(opts.accountDir);
10151
- mkdirSync9(dir, { recursive: true });
10152
- const filePath = relayFilePath(opts.accountDir, opts.actionId);
10153
- const record = {
10154
- actionId: opts.actionId,
10155
- conversationId: opts.conversationId,
10156
- message: opts.message,
10157
- queuedAt: (/* @__PURE__ */ new Date()).toISOString()
10158
- };
10159
- const body = JSON.stringify(record);
10160
- try {
10161
- writeFileSync10(filePath, body, { flag: "wx", encoding: "utf-8" });
10162
- console.log(
10163
- `${TAG19} phase=enqueued actionId=${opts.actionId} conversationId=${opts.conversationId} path=${filePath} bytes=${Buffer.byteLength(body, "utf-8")}`
10164
- );
10165
- return { enqueued: true, path: filePath };
10166
- } catch (e) {
10167
- if (e.code === "EEXIST") {
10168
- console.log(
10169
- `${TAG19} phase=enqueue-skipped actionId=${opts.actionId} reason=already-queued`
10170
- );
10171
- return { enqueued: false, reason: "already-queued", path: filePath };
10172
- }
10173
- throw e;
10174
- }
10175
- }
10176
- function consumeActionCompletionRelays(accountDir) {
10177
- const dir = queueDir(accountDir);
10178
- if (!existsSync20(dir)) return [];
10179
- let entries;
10180
- try {
10181
- entries = readdirSync6(dir);
10182
- } catch (e) {
10183
- console.error(
10184
- `${TAG19} phase=readdir-failed dir=${dir} error=${e instanceof Error ? e.message : String(e)}`
10185
- );
10186
- return [];
10187
- }
10188
- const records = [];
10189
- for (const name of entries) {
10190
- if (!name.startsWith(FILE_PREFIX) || !name.endsWith(FILE_SUFFIX)) continue;
10191
- const filePath = join9(dir, name);
10192
- let raw;
10193
- try {
10194
- raw = readFileSync16(filePath, "utf-8");
10195
- } catch (e) {
10196
- console.error(
10197
- `${TAG19} phase=read-failed file=${name} error=${e instanceof Error ? e.message : String(e)}`
10198
- );
10199
- continue;
10200
- }
10201
- let parsed;
10202
- try {
10203
- parsed = JSON.parse(raw);
10204
- } catch (e) {
10205
- console.error(
10206
- `${TAG19} phase=parse-failed file=${name} error=${e instanceof Error ? e.message : String(e)}`
10207
- );
10208
- continue;
10209
- }
10210
- if (!isActionCompletionRelay(parsed)) {
10211
- console.error(
10212
- `${TAG19} phase=shape-invalid file=${name} keys=${parsed && typeof parsed === "object" ? Object.keys(parsed).join(",") : "non-object"}`
10213
- );
10214
- continue;
10215
- }
10216
- records.push({ rec: parsed, filePath });
10217
- }
10218
- records.sort((a, b) => a.rec.queuedAt.localeCompare(b.rec.queuedAt));
10219
- const out = [];
10220
- const now = Date.now();
10221
- for (const { rec, filePath } of records) {
10222
- const queuedAtMs = Date.parse(rec.queuedAt);
10223
- const ageMs = Number.isFinite(queuedAtMs) ? now - queuedAtMs : 0;
10224
- out.push({ ...rec, ageMs, filePath });
10225
- }
10226
- return out;
10227
- }
10228
- function deleteConsumedRelay(filePath) {
10229
- try {
10230
- unlinkSync2(filePath);
10231
- } catch (e) {
10232
- const code = e.code;
10233
- if (code !== "ENOENT") {
10234
- console.error(
10235
- `${TAG19} phase=unlink-failed file=${filePath} error=${e instanceof Error ? e.message : String(e)}`
10236
- );
10237
- }
10238
- }
10239
- }
10240
- function isActionCompletionRelay(value) {
10241
- if (!value || typeof value !== "object") return false;
10242
- const v = value;
10243
- return typeof v.actionId === "string" && typeof v.conversationId === "string" && typeof v.message === "string" && typeof v.queuedAt === "string";
10244
- }
10245
-
10246
10189
  // server/routes/admin/cloudflare.ts
10247
- import { existsSync as existsSyncFs, readFileSync as readFileSyncFs } from "fs";
10248
10190
  var SETUP_TIMEOUT_MS = 10 * 60 * 1e3;
10249
10191
  var DOMAINS_TIMEOUT_MS = 40 * 1e3;
10250
10192
  function loadBrandInfo() {
10251
10193
  const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve18(process.cwd(), "..");
10252
10194
  const brandPath = resolve18(platformRoot2, "config", "brand.json");
10253
10195
  try {
10254
- const parsed = JSON.parse(readFileSync17(brandPath, "utf-8"));
10196
+ const parsed = JSON.parse(readFileSync16(brandPath, "utf-8"));
10255
10197
  const hostname2 = typeof parsed.hostname === "string" && parsed.hostname ? parsed.hostname : "maxy";
10256
10198
  const configDir2 = typeof parsed.configDir === "string" && parsed.configDir ? parsed.configDir : ".maxy";
10257
10199
  return { hostname: hostname2, configDir: configDir2 };
@@ -10547,79 +10489,13 @@ actionId: ${actionId}`,
10547
10489
  };
10548
10490
  return ok(success);
10549
10491
  });
10550
- var RELAY_MAX_BODY = 8 * 1024;
10551
- var RELAY_MAX_MESSAGE = 2048;
10552
- var ACTION_ID_RE = /^[a-z0-9-]+$/;
10553
- app21.post("/relay-completion", requireAdminSession, async (c) => {
10554
- const sessionKey = c.var.sessionKey;
10555
- let raw;
10556
- try {
10557
- raw = await c.req.text();
10558
- } catch {
10559
- return c.json({ ok: false, reason: "invalid-body" }, 400);
10560
- }
10561
- if (Buffer.byteLength(raw, "utf-8") > RELAY_MAX_BODY) {
10562
- return c.json({ ok: false, reason: "body-too-large" }, 413);
10563
- }
10564
- let body;
10565
- try {
10566
- body = JSON.parse(raw);
10567
- } catch {
10568
- return c.json({ ok: false, reason: "invalid-json" }, 400);
10569
- }
10570
- if (!body || typeof body !== "object") {
10571
- return c.json({ ok: false, reason: "invalid-body" }, 400);
10572
- }
10573
- const { actionId, message } = body;
10574
- if (typeof actionId !== "string" || !ACTION_ID_RE.test(actionId) || actionId.length > 200) {
10575
- console.error(`[cloudflare-relay-completion] phase=enqueue-skipped reason=invalid-actionid sessionKey=${sessionKey.slice(-8)}`);
10576
- return c.json({ ok: false, reason: "invalid-actionid" }, 400);
10577
- }
10578
- if (typeof message !== "string" || message.length === 0 || message.length > RELAY_MAX_MESSAGE) {
10579
- return c.json({ ok: false, reason: "invalid-message" }, 400);
10580
- }
10581
- const conversationId = getConversationIdForSession(sessionKey);
10582
- if (!conversationId) {
10583
- console.error(`[cloudflare-relay-completion] phase=enqueue-skipped reason=missing-conversation-id sessionKey=${sessionKey.slice(-8)}`);
10584
- return c.json({ ok: false, reason: "missing-conversation-id" }, 400);
10585
- }
10586
- const account = resolveAccount();
10587
- if (!account) {
10588
- console.error(`[cloudflare-relay-completion] phase=enqueue-skipped reason=missing-account-dir actionId=${actionId}`);
10589
- return c.json({ ok: false, reason: "missing-account-dir" }, 500);
10590
- }
10591
- const logPath2 = actionLogPath(actionId);
10592
- let auditOutcome = "log-absent";
10593
- if (existsSyncFs(logPath2)) {
10594
- try {
10595
- const lines = readFileSyncFs(logPath2, "utf-8").split("\n");
10596
- const reconciled = reconcileCloudflareSetupFromLog(lines);
10597
- auditOutcome = reconciled ? reconciled.kind : "log-incomplete";
10598
- } catch (e) {
10599
- auditOutcome = `read-failed:${e instanceof Error ? e.message : String(e)}`;
10600
- }
10601
- }
10602
- console.log(`[cloudflare-relay-completion] phase=enqueue-attempt actionId=${actionId} auditOutcome=${auditOutcome} conversationId=${conversationId}`);
10603
- try {
10604
- enqueueActionCompletionRelay({
10605
- accountDir: account.accountDir,
10606
- actionId,
10607
- conversationId,
10608
- message
10609
- });
10610
- } catch (e) {
10611
- console.error(`[cloudflare-relay-completion] phase=enqueue-failed actionId=${actionId} error=${e instanceof Error ? e.message : String(e)}`);
10612
- return c.json({ ok: false, reason: "enqueue-failed" }, 500);
10613
- }
10614
- return c.body(null, 204);
10615
- });
10616
10492
  var cloudflare_default = app21;
10617
10493
 
10618
10494
  // server/routes/admin/files.ts
10619
10495
  import { createReadStream as createReadStream3 } from "fs";
10620
10496
  import { readdir as readdir2, readFile as readFile4, stat as stat4, mkdir as mkdir3, writeFile as writeFile4, unlink as unlink2 } from "fs/promises";
10621
10497
  import { realpathSync as realpathSync4 } from "fs";
10622
- import { basename as basename6, dirname as dirname8, join as join10, resolve as resolve20, sep as sep2 } from "path";
10498
+ import { basename as basename6, dirname as dirname8, join as join9, resolve as resolve20, sep as sep2 } from "path";
10623
10499
  import { Readable as Readable2 } from "stream";
10624
10500
 
10625
10501
  // app/lib/data-path.ts
@@ -10977,7 +10853,7 @@ async function cascadeDeleteDocument(params) {
10977
10853
  var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
10978
10854
  async function readMeta(absDir, baseName) {
10979
10855
  try {
10980
- const raw = await readFile4(join10(absDir, `${baseName}.meta.json`), "utf8");
10856
+ const raw = await readFile4(join9(absDir, `${baseName}.meta.json`), "utf8");
10981
10857
  const parsed = JSON.parse(raw);
10982
10858
  if (typeof parsed?.filename === "string") {
10983
10859
  return { filename: parsed.filename, mimeType: typeof parsed.mimeType === "string" ? parsed.mimeType : void 0 };
@@ -11015,7 +10891,7 @@ async function readAccountNames() {
11015
10891
  }
11016
10892
  async function enrich(absolute, entry, accountNames) {
11017
10893
  if (entry.kind === "directory" && UUID_RE4.test(entry.name)) {
11018
- const meta = await readMeta(join10(absolute, entry.name), entry.name);
10894
+ const meta = await readMeta(join9(absolute, entry.name), entry.name);
11019
10895
  if (meta?.filename) {
11020
10896
  entry.displayName = meta.filename;
11021
10897
  entry.mimeType = meta.mimeType;
@@ -11074,7 +10950,7 @@ app22.get("/", requireAdminSession, async (c) => {
11074
10950
  continue;
11075
10951
  }
11076
10952
  try {
11077
- const entryPath = join10(absolute, name);
10953
+ const entryPath = join9(absolute, name);
11078
10954
  const s = await stat4(entryPath);
11079
10955
  entries.push({
11080
10956
  name,
@@ -11247,7 +11123,7 @@ app22.delete("/", requireAdminSession, async (c) => {
11247
11123
  }
11248
11124
  const dot = base.lastIndexOf(".");
11249
11125
  const stem = dot === -1 ? base : base.slice(0, dot);
11250
- const sidecarPath = UUID_RE4.test(stem) && base !== `${stem}.meta.json` ? join10(dirname8(absolute), `${stem}.meta.json`) : null;
11126
+ const sidecarPath = UUID_RE4.test(stem) && base !== `${stem}.meta.json` ? join9(dirname8(absolute), `${stem}.meta.json`) : null;
11251
11127
  await unlink2(absolute);
11252
11128
  if (sidecarPath) {
11253
11129
  try {
@@ -12728,7 +12604,7 @@ var adherence_default = app30;
12728
12604
  import neo4j3 from "neo4j-driver";
12729
12605
  import { readFile as readFile5, readdir as readdir3, stat as stat5 } from "fs/promises";
12730
12606
  import { resolve as resolve21, relative as relative2, isAbsolute } from "path";
12731
- import { existsSync as existsSync21 } from "fs";
12607
+ import { existsSync as existsSync20 } from "fs";
12732
12608
  var LIMIT = 50;
12733
12609
  var TEXT_MIME_PREFIXES = ["text/", "application/json", "application/markdown"];
12734
12610
  var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
@@ -12876,7 +12752,7 @@ async function fetchAgentTemplateRows(accountDir) {
12876
12752
  async function unionSpecialistFilenames(overrideDir, bundledDir) {
12877
12753
  const names = /* @__PURE__ */ new Set();
12878
12754
  for (const dir of [overrideDir, bundledDir]) {
12879
- if (!existsSync21(dir)) continue;
12755
+ if (!existsSync20(dir)) continue;
12880
12756
  try {
12881
12757
  const entries = await readdir3(dir);
12882
12758
  for (const entry of entries) {
@@ -12891,7 +12767,7 @@ async function unionSpecialistFilenames(overrideDir, bundledDir) {
12891
12767
  }
12892
12768
  async function readAgentTemplateRow(inp) {
12893
12769
  let chosenPath = null;
12894
- if (existsSync21(inp.overridePath)) {
12770
+ if (existsSync20(inp.overridePath)) {
12895
12771
  try {
12896
12772
  validateFilePathInAccount(inp.overridePath, inp.overrideRoot);
12897
12773
  chosenPath = inp.overridePath;
@@ -12902,7 +12778,7 @@ async function readAgentTemplateRow(inp) {
12902
12778
  );
12903
12779
  return null;
12904
12780
  }
12905
- } else if (existsSync21(inp.bundledPath)) {
12781
+ } else if (existsSync20(inp.bundledPath)) {
12906
12782
  if (!isWithin(inp.bundledPath, inp.bundledRoot)) {
12907
12783
  console.error(
12908
12784
  `[admin/sidebar-artefacts] agent-template-read-failed agent=${inp.displayName} kind=${inp.logName} error="bundled path outside PLATFORM_ROOT"`
@@ -12944,7 +12820,7 @@ var sidebar_artefacts_default = app31;
12944
12820
  // server/routes/admin/sidebar-artefact-save.ts
12945
12821
  import { mkdir as mkdir4, readdir as readdir4, stat as stat6, writeFile as writeFile5 } from "fs/promises";
12946
12822
  import { resolve as resolve22 } from "path";
12947
- import { existsSync as existsSync22 } from "fs";
12823
+ import { existsSync as existsSync21 } from "fs";
12948
12824
  var ADMIN_AGENT_FILES2 = /* @__PURE__ */ new Set(["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"]);
12949
12825
  var UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
12950
12826
  var app32 = new Hono();
@@ -13008,7 +12884,7 @@ async function resolveSavePath(id, accountId, accountDir) {
13008
12884
  }
13009
12885
  if (UUID_RE5.test(id)) {
13010
12886
  const dir = resolve22(ATTACHMENTS_ROOT, accountId, id);
13011
- if (!existsSync22(dir)) {
12887
+ if (!existsSync21(dir)) {
13012
12888
  return { kind: "reject", status: 400, reason: "not-found" };
13013
12889
  }
13014
12890
  try {
@@ -13032,7 +12908,7 @@ var sidebar_artefact_save_default = app32;
13032
12908
 
13033
12909
  // server/routes/admin/sidebar-artefact-content.ts
13034
12910
  import { readFile as readFile6, readdir as readdir5 } from "fs/promises";
13035
- import { existsSync as existsSync23 } from "fs";
12911
+ import { existsSync as existsSync22 } from "fs";
13036
12912
  import { resolve as resolve23 } from "path";
13037
12913
  var UUID_RE6 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
13038
12914
  var app33 = new Hono();
@@ -13046,7 +12922,7 @@ app33.get("/", requireAdminSession, async (c) => {
13046
12922
  return new Response("Not found", { status: 404 });
13047
12923
  }
13048
12924
  const dir = resolve23(ATTACHMENTS_ROOT, accountId, id);
13049
- if (!existsSync23(dir)) {
12925
+ if (!existsSync22(dir)) {
13050
12926
  console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
13051
12927
  return new Response("Not found", { status: 404 });
13052
12928
  }
@@ -13108,7 +12984,7 @@ app34.route("/sidebar-artefact-content", sidebar_artefact_content_default);
13108
12984
  var admin_default = app34;
13109
12985
 
13110
12986
  // server/routes/sites.ts
13111
- import { existsSync as existsSync24, readFileSync as readFileSync18, realpathSync as realpathSync5, statSync as statSync8 } from "fs";
12987
+ import { existsSync as existsSync23, readFileSync as readFileSync17, realpathSync as realpathSync5, statSync as statSync8 } from "fs";
13112
12988
  import { resolve as resolve24 } from "path";
13113
12989
  var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
13114
12990
  var MIME = {
@@ -13175,7 +13051,7 @@ app35.get("/:rel{.*}", (c) => {
13175
13051
  }
13176
13052
  let stat7;
13177
13053
  try {
13178
- stat7 = existsSync24(filePath) ? statSync8(filePath) : null;
13054
+ stat7 = existsSync23(filePath) ? statSync8(filePath) : null;
13179
13055
  } catch {
13180
13056
  stat7 = null;
13181
13057
  }
@@ -13188,7 +13064,7 @@ app35.get("/:rel{.*}", (c) => {
13188
13064
  console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
13189
13065
  return c.text("Forbidden", 403);
13190
13066
  }
13191
- if (!existsSync24(filePath)) {
13067
+ if (!existsSync23(filePath)) {
13192
13068
  console.error(`[sites] not-found path=${reqPath} status=404`);
13193
13069
  return c.text("Not found", 404);
13194
13070
  }
@@ -13207,7 +13083,7 @@ app35.get("/:rel{.*}", (c) => {
13207
13083
  }
13208
13084
  let body;
13209
13085
  try {
13210
- body = readFileSync18(realPath);
13086
+ body = readFileSync17(realPath);
13211
13087
  } catch (err) {
13212
13088
  const code = err?.code;
13213
13089
  if (code === "EISDIR") {
@@ -13339,14 +13215,14 @@ function clientFrom(c) {
13339
13215
  );
13340
13216
  }
13341
13217
  var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT || "";
13342
- var BRAND_JSON_PATH = PLATFORM_ROOT7 ? join11(PLATFORM_ROOT7, "config", "brand.json") : "";
13218
+ var BRAND_JSON_PATH = PLATFORM_ROOT7 ? join10(PLATFORM_ROOT7, "config", "brand.json") : "";
13343
13219
  var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", domain: "getmaxy.com" };
13344
- if (BRAND_JSON_PATH && !existsSync25(BRAND_JSON_PATH)) {
13220
+ if (BRAND_JSON_PATH && !existsSync24(BRAND_JSON_PATH)) {
13345
13221
  console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
13346
13222
  }
13347
- if (BRAND_JSON_PATH && existsSync25(BRAND_JSON_PATH)) {
13223
+ if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
13348
13224
  try {
13349
- const parsed = JSON.parse(readFileSync19(BRAND_JSON_PATH, "utf-8"));
13225
+ const parsed = JSON.parse(readFileSync18(BRAND_JSON_PATH, "utf-8"));
13350
13226
  BRAND = { ...BRAND, ...parsed };
13351
13227
  } catch (err) {
13352
13228
  console.error(`[brand] Failed to parse brand.json: ${err.message}`);
@@ -13365,11 +13241,11 @@ var brandLoginOpts = {
13365
13241
  bodyFont: BRAND.defaultFonts?.body,
13366
13242
  logoContainsName: !!BRAND.logoContainsName
13367
13243
  };
13368
- var ALIAS_DOMAINS_PATH2 = join11(homedir2(), BRAND.configDir, "alias-domains.json");
13244
+ var ALIAS_DOMAINS_PATH2 = join10(homedir2(), BRAND.configDir, "alias-domains.json");
13369
13245
  function loadAliasDomains() {
13370
13246
  try {
13371
- if (!existsSync25(ALIAS_DOMAINS_PATH2)) return null;
13372
- const parsed = JSON.parse(readFileSync19(ALIAS_DOMAINS_PATH2, "utf-8"));
13247
+ if (!existsSync24(ALIAS_DOMAINS_PATH2)) return null;
13248
+ const parsed = JSON.parse(readFileSync18(ALIAS_DOMAINS_PATH2, "utf-8"));
13373
13249
  if (!Array.isArray(parsed)) {
13374
13250
  console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
13375
13251
  return null;
@@ -13715,14 +13591,14 @@ app36.get("/agent-assets/:slug/:filename", (c) => {
13715
13591
  console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
13716
13592
  return c.text("Forbidden", 403);
13717
13593
  }
13718
- if (!existsSync25(filePath)) {
13594
+ if (!existsSync24(filePath)) {
13719
13595
  console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
13720
13596
  return c.text("Not found", 404);
13721
13597
  }
13722
13598
  const ext = "." + filename.split(".").pop()?.toLowerCase();
13723
13599
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
13724
13600
  console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
13725
- const body = readFileSync19(filePath);
13601
+ const body = readFileSync18(filePath);
13726
13602
  return c.body(body, 200, {
13727
13603
  "Content-Type": contentType,
13728
13604
  "Cache-Control": "public, max-age=3600"
@@ -13745,14 +13621,14 @@ app36.get("/generated/:filename", (c) => {
13745
13621
  console.error(`[generated] serve file=${filename} status=403`);
13746
13622
  return c.text("Forbidden", 403);
13747
13623
  }
13748
- if (!existsSync25(filePath)) {
13624
+ if (!existsSync24(filePath)) {
13749
13625
  console.error(`[generated] serve file=${filename} status=404`);
13750
13626
  return c.text("Not found", 404);
13751
13627
  }
13752
13628
  const ext = "." + filename.split(".").pop()?.toLowerCase();
13753
13629
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
13754
13630
  console.log(`[generated] serve file=${filename} status=200`);
13755
- const body = readFileSync19(filePath);
13631
+ const body = readFileSync18(filePath);
13756
13632
  return c.body(body, 200, {
13757
13633
  "Content-Type": contentType,
13758
13634
  "Cache-Control": "public, max-age=86400"
@@ -13762,9 +13638,9 @@ app36.route("/sites", sites_default);
13762
13638
  var htmlCache = /* @__PURE__ */ new Map();
13763
13639
  var brandLogoPath = "/brand/maxy-monochrome.png";
13764
13640
  var brandIconPath = "/brand/maxy-monochrome.png";
13765
- if (BRAND_JSON_PATH && existsSync25(BRAND_JSON_PATH)) {
13641
+ if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
13766
13642
  try {
13767
- const fullBrand = JSON.parse(readFileSync19(BRAND_JSON_PATH, "utf-8"));
13643
+ const fullBrand = JSON.parse(readFileSync18(BRAND_JSON_PATH, "utf-8"));
13768
13644
  if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
13769
13645
  brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
13770
13646
  } catch {
@@ -13781,9 +13657,9 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
13781
13657
  function readInstalledVersion() {
13782
13658
  try {
13783
13659
  if (!PLATFORM_ROOT7) return "unknown";
13784
- const versionFile = join11(PLATFORM_ROOT7, "config", `.${BRAND.hostname}-version`);
13785
- if (!existsSync25(versionFile)) return "unknown";
13786
- const content = readFileSync19(versionFile, "utf-8").trim();
13660
+ const versionFile = join10(PLATFORM_ROOT7, "config", `.${BRAND.hostname}-version`);
13661
+ if (!existsSync24(versionFile)) return "unknown";
13662
+ const content = readFileSync18(versionFile, "utf-8").trim();
13787
13663
  return content || "unknown";
13788
13664
  } catch {
13789
13665
  return "unknown";
@@ -13824,7 +13700,7 @@ var clientErrorReporterScript = `<script>
13824
13700
  function cachedHtml(file) {
13825
13701
  let html = htmlCache.get(file);
13826
13702
  if (!html) {
13827
- html = readFileSync19(resolve25(process.cwd(), "public", file), "utf-8");
13703
+ html = readFileSync18(resolve25(process.cwd(), "public", file), "utf-8");
13828
13704
  const productNameEsc = escapeHtml(BRAND.productName);
13829
13705
  html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
13830
13706
  html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
@@ -13840,26 +13716,26 @@ ${clientErrorReporterScript}
13840
13716
  }
13841
13717
  var brandedHtmlCache = /* @__PURE__ */ new Map();
13842
13718
  function loadBrandingCache(agentSlug) {
13843
- const configDir2 = join11(homedir2(), BRAND.configDir);
13719
+ const configDir2 = join10(homedir2(), BRAND.configDir);
13844
13720
  try {
13845
- const accountJsonPath = join11(configDir2, "account.json");
13846
- if (!existsSync25(accountJsonPath)) return null;
13847
- const account = JSON.parse(readFileSync19(accountJsonPath, "utf-8"));
13721
+ const accountJsonPath = join10(configDir2, "account.json");
13722
+ if (!existsSync24(accountJsonPath)) return null;
13723
+ const account = JSON.parse(readFileSync18(accountJsonPath, "utf-8"));
13848
13724
  const accountId = account.accountId;
13849
13725
  if (!accountId) return null;
13850
- const cachePath = join11(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
13851
- if (!existsSync25(cachePath)) return null;
13852
- return JSON.parse(readFileSync19(cachePath, "utf-8"));
13726
+ const cachePath = join10(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
13727
+ if (!existsSync24(cachePath)) return null;
13728
+ return JSON.parse(readFileSync18(cachePath, "utf-8"));
13853
13729
  } catch {
13854
13730
  return null;
13855
13731
  }
13856
13732
  }
13857
13733
  function resolveDefaultSlug() {
13858
13734
  try {
13859
- const configDir2 = join11(homedir2(), BRAND.configDir);
13860
- const accountJsonPath = join11(configDir2, "account.json");
13861
- if (!existsSync25(accountJsonPath)) return null;
13862
- const account = JSON.parse(readFileSync19(accountJsonPath, "utf-8"));
13735
+ const configDir2 = join10(homedir2(), BRAND.configDir);
13736
+ const accountJsonPath = join10(configDir2, "account.json");
13737
+ if (!existsSync24(accountJsonPath)) return null;
13738
+ const account = JSON.parse(readFileSync18(accountJsonPath, "utf-8"));
13863
13739
  return account.defaultAgent || null;
13864
13740
  } catch {
13865
13741
  return null;
@@ -13932,7 +13808,7 @@ app36.use("/vnc-popout.html", logViewerFetch);
13932
13808
  app36.get("/vnc-popout.html", (c) => {
13933
13809
  let html = htmlCache.get("vnc-popout.html");
13934
13810
  if (!html) {
13935
- html = readFileSync19(resolve25(process.cwd(), "public", "vnc-popout.html"), "utf-8");
13811
+ html = readFileSync18(resolve25(process.cwd(), "public", "vnc-popout.html"), "utf-8");
13936
13812
  const name = escapeHtml(BRAND.productName);
13937
13813
  html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
13938
13814
  html = html.replace("</head>", ` ${brandScript}
@@ -14022,8 +13898,8 @@ try {
14022
13898
  (async () => {
14023
13899
  try {
14024
13900
  let userId = "";
14025
- if (existsSync25(USERS_FILE)) {
14026
- const users = JSON.parse(readFileSync19(USERS_FILE, "utf-8").trim() || "[]");
13901
+ if (existsSync24(USERS_FILE)) {
13902
+ const users = JSON.parse(readFileSync18(USERS_FILE, "utf-8").trim() || "[]");
14027
13903
  userId = users[0]?.userId ?? "";
14028
13904
  }
14029
13905
  await backfillNullUserIdConversations(userId);
@@ -14083,48 +13959,6 @@ for (const dir of bootEnabled) {
14083
13959
  bootDelivered.push(dir);
14084
13960
  }
14085
13961
  console.error(`[plugins] readiness enabled=${bootEnabled.length} delivered=${bootDelivered.length} dist-missing=[${bootDistMissing.join(",")}] missing=[${bootMissing.join(",")}]`);
14086
- (async () => {
14087
- if (!bootAccount) {
14088
- console.log("[action-completion-relay] phase=consumed-skip reason=no-account");
14089
- return;
14090
- }
14091
- let records;
14092
- try {
14093
- records = consumeActionCompletionRelays(bootAccount.accountDir);
14094
- } catch (err) {
14095
- console.error(`[action-completion-relay] phase=consume-failed error=${err instanceof Error ? err.message : String(err)}`);
14096
- return;
14097
- }
14098
- if (records.length === 0) {
14099
- console.log(`[action-completion-relay] phase=consumed-empty accountId=${bootAccount.accountId.slice(0, 8)}\u2026`);
14100
- return;
14101
- }
14102
- console.log(`[action-completion-relay] phase=consume-batch count=${records.length} accountId=${bootAccount.accountId.slice(0, 8)}\u2026`);
14103
- for (const rec of records) {
14104
- const sessionKey = `cloudflare-relay-boot:${rec.actionId}`;
14105
- let outcome = "injected";
14106
- let dispatchError;
14107
- try {
14108
- registerSession(sessionKey, "admin", bootAccount.accountId);
14109
- setConversationIdForSession(sessionKey, rec.conversationId);
14110
- for await (const _ev of invokeAgent({ type: "admin" }, rec.message, sessionKey)) {
14111
- }
14112
- } catch (err) {
14113
- outcome = "dispatch-failed";
14114
- dispatchError = err instanceof Error ? err.message : String(err);
14115
- } finally {
14116
- unregisterSession(sessionKey);
14117
- }
14118
- if (outcome === "injected") {
14119
- deleteConsumedRelay(rec.filePath);
14120
- console.log(`[action-completion-relay] phase=consumed actionId=${rec.actionId} conversationId=${rec.conversationId} ageMs=${rec.ageMs} outcome=injected`);
14121
- } else {
14122
- console.error(`[action-completion-relay] phase=consumed actionId=${rec.actionId} conversationId=${rec.conversationId} ageMs=${rec.ageMs} outcome=${outcome} reason=${JSON.stringify(dispatchError ?? "")} fileRetained=${JSON.stringify(rec.filePath)}`);
14123
- }
14124
- }
14125
- })().catch((err) => {
14126
- console.error(`[action-completion-relay] boot-drain rejected: ${err instanceof Error ? err.message : String(err)}`);
14127
- });
14128
13962
  if (bootAccountConfig?.whatsapp) {
14129
13963
  console.error(`[whatsapp:boot] loading whatsapp config from account.json publicAgent=${bootPublicAgent ?? "none"}`);
14130
13964
  } else {
@@ -14132,7 +13966,7 @@ if (bootAccountConfig?.whatsapp) {
14132
13966
  }
14133
13967
  init({
14134
13968
  configDir: configDirForWhatsApp,
14135
- platformRoot: resolve25(process.env.MAXY_PLATFORM_ROOT ?? join11(__dirname, "..")),
13969
+ platformRoot: resolve25(process.env.MAXY_PLATFORM_ROOT ?? join10(__dirname, "..")),
14136
13970
  accountConfig: bootAccountConfig,
14137
13971
  onMessage: async (msg) => {
14138
13972
  try {