@rubytech/create-maxy 1.0.807 → 1.0.809

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 (52) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/admin/skills/onboarding/SKILL.md +2 -0
  3. package/payload/platform/plugins/docs/references/cloudflare.md +1 -0
  4. package/payload/platform/plugins/docs/references/memory-guide.md +4 -0
  5. package/payload/platform/plugins/docs/references/troubleshooting.md +19 -1
  6. package/payload/platform/plugins/memory/mcp/dist/index.js +86 -0
  7. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  8. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.d.ts.map +1 -1
  9. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js +19 -0
  10. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js.map +1 -1
  11. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-pass.d.ts +23 -0
  12. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-pass.d.ts.map +1 -0
  13. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-pass.js +401 -0
  14. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-insight-pass.js.map +1 -0
  15. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-preview.d.ts +28 -0
  16. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-preview.d.ts.map +1 -0
  17. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-preview.js +34 -0
  18. package/payload/platform/plugins/memory/mcp/dist/tools/whatsapp-export-preview.js.map +1 -0
  19. package/payload/platform/plugins/memory/references/schema-base.md +12 -0
  20. package/payload/platform/plugins/whatsapp/PLUGIN.md +3 -1
  21. package/payload/platform/plugins/whatsapp-import/bin/ingest.mjs +225 -346
  22. package/payload/platform/plugins/whatsapp-import/bin/whatsapp-ingest.sh +28 -10
  23. package/payload/platform/plugins/whatsapp-import/lib/dist/derive-keys.d.ts +21 -0
  24. package/payload/platform/plugins/whatsapp-import/lib/dist/derive-keys.d.ts.map +1 -0
  25. package/payload/platform/plugins/whatsapp-import/lib/dist/derive-keys.js +41 -0
  26. package/payload/platform/plugins/whatsapp-import/lib/dist/derive-keys.js.map +1 -0
  27. package/payload/platform/plugins/whatsapp-import/lib/dist/filter.d.ts +29 -0
  28. package/payload/platform/plugins/whatsapp-import/lib/dist/filter.d.ts.map +1 -0
  29. package/payload/platform/plugins/whatsapp-import/lib/dist/filter.js +123 -0
  30. package/payload/platform/plugins/whatsapp-import/lib/dist/filter.js.map +1 -0
  31. package/payload/platform/plugins/whatsapp-import/lib/dist/index.d.ts +4 -0
  32. package/payload/platform/plugins/whatsapp-import/lib/dist/index.d.ts.map +1 -1
  33. package/payload/platform/plugins/whatsapp-import/lib/dist/index.js +9 -1
  34. package/payload/platform/plugins/whatsapp-import/lib/dist/index.js.map +1 -1
  35. package/payload/platform/plugins/whatsapp-import/lib/src/__tests__/filter-gate.test.ts +170 -0
  36. package/payload/platform/plugins/whatsapp-import/lib/src/__tests__/ingest-idempotence.test.ts +141 -0
  37. package/payload/platform/plugins/whatsapp-import/lib/src/derive-keys.ts +59 -0
  38. package/payload/platform/plugins/whatsapp-import/lib/src/filter.ts +136 -0
  39. package/payload/platform/plugins/whatsapp-import/lib/src/index.ts +12 -0
  40. package/payload/platform/plugins/whatsapp-import/skills/whatsapp-import/SKILL.md +80 -25
  41. package/payload/platform/plugins/whatsapp-import/skills/whatsapp-import-enrich/SKILL.md +22 -3
  42. package/payload/platform/templates/agents/admin/IDENTITY.md +1 -0
  43. package/payload/platform/templates/agents/admin/SOUL.md +2 -0
  44. package/payload/platform/templates/specialists/agents/database-operator.md +9 -4
  45. package/payload/server/chunk-CRWLE6BZ.js +3511 -0
  46. package/payload/server/chunk-V3VLAL7N.js +10009 -0
  47. package/payload/server/client-pool-N2Y57223.js +31 -0
  48. package/payload/server/maxy-edge.js +5 -4
  49. package/payload/server/public/assets/admin-Bwrd2DBq.js +352 -0
  50. package/payload/server/public/index.html +1 -1
  51. package/payload/server/server.js +596 -250
  52. package/payload/server/public/assets/admin-CTM9Vb-j.js +0 -352
@@ -6,6 +6,7 @@ import {
6
6
  TELEGRAM_ADMIN_WEBHOOK_SECRET_FILE,
7
7
  TELEGRAM_WEBHOOK_SECRET_FILE,
8
8
  USERS_FILE,
9
+ actionLogPath,
9
10
  autoDeliverPremiumPlugins,
10
11
  buildX11Env,
11
12
  callOauthLlm,
@@ -29,6 +30,7 @@ import {
29
30
  launchAction,
30
31
  load,
31
32
  logPath,
33
+ reconcileCloudflareSetupFromLog,
32
34
  recordFailedAttempt,
33
35
  render,
34
36
  renderLoginPage,
@@ -50,7 +52,7 @@ import {
50
52
  vncLog,
51
53
  waitForExit,
52
54
  writeChromiumWrapper
53
- } from "./chunk-LSUMH6OF.js";
55
+ } from "./chunk-V3VLAL7N.js";
54
56
  import {
55
57
  ACCOUNTS_DIR,
56
58
  GREETING_DIRECTIVE,
@@ -114,7 +116,7 @@ import {
114
116
  verifyAndGetConversationUpdatedAt,
115
117
  verifyConversationOwnership,
116
118
  writeAdminUserAndPerson
117
- } from "./chunk-YULDSPAC.js";
119
+ } from "./chunk-CRWLE6BZ.js";
118
120
  import {
119
121
  __commonJS,
120
122
  __toESM
@@ -617,15 +619,15 @@ var serveStatic = (options = { root: "" }) => {
617
619
  };
618
620
 
619
621
  // server/index.ts
620
- import { readFileSync as readFileSync17, existsSync as existsSync24, watchFile } from "fs";
621
- import { resolve as resolve24, join as join10, basename as basename7 } from "path";
622
+ import { readFileSync as readFileSync19, existsSync as existsSync25, watchFile } from "fs";
623
+ import { resolve as resolve25, join as join11, basename as basename7 } from "path";
622
624
  import { homedir as homedir2 } from "os";
623
625
 
624
626
  // app/lib/agent-slug-pattern.ts
625
627
  var AGENT_SLUG_PATTERN = /^\/([a-z][a-z0-9-]{2,49})$/;
626
628
 
627
629
  // server/routes/health.ts
628
- import { existsSync as existsSync6, readFileSync as readFileSync5 } from "fs";
630
+ import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
629
631
  import { createConnection } from "net";
630
632
 
631
633
  // app/lib/network.ts
@@ -804,6 +806,28 @@ function defaultRules() {
804
806
  scope: "session",
805
807
  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."
806
808
  },
809
+ {
810
+ // Task 867 — fires when setup-tunnel.sh emits step=done but no
811
+ // `[persist] role=user … Cloudflare setup completed (actionId: <id>)`
812
+ // line appears within 60s. Covers the three failure modes of the
813
+ // action-relay-queue plumbing: queue-write failed, boot-drain consumer
814
+ // skipped the record, or the agent's hoisted persist threw. The
815
+ // followup pattern is intentionally narrow to the cloudflare-setup
816
+ // relay shape (not a general "any user persist") so a benign user
817
+ // typing in chat does not satisfy the followup. logSource=any so
818
+ // the rule sees both the script tee (in stream logs / server.log)
819
+ // and the [persist] line (server.log).
820
+ id: "cloudflare-setup-relay-not-acknowledged",
821
+ name: "Cloudflare-setup completed but the chat relay never acknowledged",
822
+ type: "absent-followup",
823
+ logSource: "any",
824
+ pattern: "\\[script:setup-tunnel\\] step=done",
825
+ followupPattern: "\\[persist\\] .*role=user.* Cloudflare setup completed \\(actionId:",
826
+ followupWindowMs: 6e4,
827
+ thresholdCount: 0,
828
+ thresholdWindowMinutes: 0,
829
+ 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."
830
+ },
807
831
  {
808
832
  // Task 538: fires when a [spawn] line appears in a conversation's stream
809
833
  // log but no subprocess-lifecycle marker follows within 10s. The three
@@ -2812,7 +2836,7 @@ var credsSaveQueue = Promise.resolve();
2812
2836
  async function drainCredsSaveQueue(timeoutMs = 5e3) {
2813
2837
  console.error(`${TAG3} draining credential save queue\u2026`);
2814
2838
  const timer2 = new Promise(
2815
- (resolve25) => setTimeout(() => resolve25("timeout"), timeoutMs)
2839
+ (resolve26) => setTimeout(() => resolve26("timeout"), timeoutMs)
2816
2840
  );
2817
2841
  const result = await Promise.race([
2818
2842
  credsSaveQueue.then(() => "drained"),
@@ -2940,11 +2964,11 @@ async function createWaSocket(opts) {
2940
2964
  return sock;
2941
2965
  }
2942
2966
  async function waitForConnection(sock) {
2943
- return new Promise((resolve25, reject) => {
2967
+ return new Promise((resolve26, reject) => {
2944
2968
  const handler = (update) => {
2945
2969
  if (update.connection === "open") {
2946
2970
  sock.ev.off("connection.update", handler);
2947
- resolve25();
2971
+ resolve26();
2948
2972
  }
2949
2973
  if (update.connection === "close") {
2950
2974
  sock.ev.off("connection.update", handler);
@@ -3058,14 +3082,14 @@ ${inspected}`;
3058
3082
  return inspect2(err, INSPECT_OPTS2);
3059
3083
  }
3060
3084
  function withTimeout(label, promise, timeoutMs) {
3061
- return new Promise((resolve25, reject) => {
3085
+ return new Promise((resolve26, reject) => {
3062
3086
  const timer2 = setTimeout(() => {
3063
3087
  reject(new Error(`${label} timed out after ${timeoutMs}ms`));
3064
3088
  }, timeoutMs);
3065
3089
  promise.then(
3066
3090
  (value) => {
3067
3091
  clearTimeout(timer2);
3068
- resolve25(value);
3092
+ resolve26(value);
3069
3093
  },
3070
3094
  (err) => {
3071
3095
  clearTimeout(timer2);
@@ -3586,8 +3610,8 @@ async function persistWhatsAppMessage(input) {
3586
3610
  const { givenName, familyName } = splitName(input.pushName);
3587
3611
  const prev = sessionWriteLocks.get(input.sessionKey);
3588
3612
  let release;
3589
- const mine = new Promise((resolve25) => {
3590
- release = resolve25;
3613
+ const mine = new Promise((resolve26) => {
3614
+ release = resolve26;
3591
3615
  });
3592
3616
  const chained = (prev ?? Promise.resolve()).then(() => mine);
3593
3617
  sessionWriteLocks.set(input.sessionKey, chained);
@@ -3605,7 +3629,7 @@ async function persistWhatsAppMessage(input) {
3605
3629
  existingM IS NOT NULL AS messageExisted
3606
3630
  MERGE (s:Person {telephone: $senderTelephone})
3607
3631
  ON CREATE SET
3608
- s.accountId = $accountId,
3632
+ s.accountId = $platformAccountId,
3609
3633
  s.givenName = $givenName,
3610
3634
  s.familyName = $familyName,
3611
3635
  s.source = 'whatsapp-live',
@@ -3617,7 +3641,7 @@ async function persistWhatsAppMessage(input) {
3617
3641
  MERGE (m:Message {messageId: $messageId})
3618
3642
  ON CREATE SET
3619
3643
  m:WhatsAppMessage,
3620
- m.accountId = $accountId,
3644
+ m.accountId = $platformAccountId,
3621
3645
  m.conversationId = c.conversationId,
3622
3646
  m.source = 'whatsapp',
3623
3647
  m.createdByAgent = 'whatsapp-live',
@@ -3663,7 +3687,7 @@ async function persistWhatsAppMessage(input) {
3663
3687
  const params = {
3664
3688
  sessionKey: input.sessionKey,
3665
3689
  messageId,
3666
- accountId: input.accountId,
3690
+ platformAccountId: input.platformAccountId,
3667
3691
  senderTelephone,
3668
3692
  senderName: input.pushName ?? null,
3669
3693
  givenName,
@@ -3680,7 +3704,7 @@ async function persistWhatsAppMessage(input) {
3680
3704
  const result = await session.run(cypher, params);
3681
3705
  const ms = Date.now() - t0;
3682
3706
  if (result.records.length === 0) {
3683
- console.error(`${TAG7} skip reason=conversation-not-found accountId=${input.accountId} sessionKey=${input.sessionKey} messageId=${messageId}`);
3707
+ console.error(`${TAG7} skip reason=conversation-not-found accountId=${input.platformAccountId} sessionKey=${input.sessionKey} messageId=${messageId}`);
3684
3708
  return null;
3685
3709
  }
3686
3710
  const rec = result.records[0];
@@ -3701,14 +3725,14 @@ async function persistWhatsAppMessage(input) {
3701
3725
  console.error(`${TAG7} next-skip reason=no-prior msgId=${messageId}`);
3702
3726
  }
3703
3727
  console.error(
3704
- `${TAG7} write messageId=${messageId} sessionKey=${input.sessionKey} fromMe=${input.fromMe} dateSent=${dateSentIso} bodyBytes=${Buffer.byteLength(input.body, "utf8")} ms=${ms}`
3728
+ `${TAG7} write messageId=${messageId} accountId=${input.platformAccountId} sessionKey=${input.sessionKey} fromMe=${input.fromMe} dateSent=${dateSentIso} bodyBytes=${Buffer.byteLength(input.body, "utf8")} ms=${ms}`
3705
3729
  );
3706
3730
  return { messageId, created: true, senderElementId };
3707
3731
  } catch (err) {
3708
3732
  const ms = Date.now() - t0;
3709
3733
  const reason = sanitizeReason(err);
3710
3734
  console.error(
3711
- `${TAG7} FAIL accountId=${input.accountId} remoteJid=${input.remoteJid} msgKey=${input.msgKeyId} reason=${reason} ms=${ms}`
3735
+ `${TAG7} FAIL accountId=${input.platformAccountId} waname=${input.accountId} remoteJid=${input.remoteJid} msgKey=${input.msgKeyId} reason=${reason} ms=${ms}`
3712
3736
  );
3713
3737
  return null;
3714
3738
  } finally {
@@ -3746,7 +3770,7 @@ async function ensureWhatsAppConversation(input) {
3746
3770
  const t0 = Date.now();
3747
3771
  try {
3748
3772
  const result = await ensureConversation(
3749
- input.accountId,
3773
+ input.platformAccountId,
3750
3774
  input.agentType,
3751
3775
  input.sessionKey
3752
3776
  );
@@ -3771,6 +3795,52 @@ async function ensureWhatsAppConversation(input) {
3771
3795
  }
3772
3796
  }
3773
3797
 
3798
+ // app/lib/whatsapp/platform-account-id.ts
3799
+ import { readdirSync as readdirSync2, readFileSync as readFileSync5 } from "fs";
3800
+ import { resolve as resolve6 } from "path";
3801
+ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
3802
+ var cached = null;
3803
+ var cachedAccountsDir = null;
3804
+ function resolvePlatformAccountId(accountsDir = ACCOUNTS_DIR) {
3805
+ if (cached !== null && cachedAccountsDir === accountsDir) return cached;
3806
+ const valid = enumerateValidAccountIds(accountsDir);
3807
+ if (valid.length === 0) {
3808
+ throw new Error(
3809
+ `[whatsapp-persist] resolvePlatformAccountId: no platform account found under ${accountsDir} \u2014 corrupt install? Cannot stamp n.accountId without a valid UUID.`
3810
+ );
3811
+ }
3812
+ if (valid.length > 1) {
3813
+ throw new Error(
3814
+ `[whatsapp-persist] resolvePlatformAccountId: multiple platform accounts found under ${accountsDir} (${valid.join(", ")}) \u2014 Phase 0 invariant requires exactly one. Loud-fail rather than picking one silently.`
3815
+ );
3816
+ }
3817
+ cached = valid[0];
3818
+ cachedAccountsDir = accountsDir;
3819
+ return cached;
3820
+ }
3821
+ function enumerateValidAccountIds(accountsDir) {
3822
+ let names;
3823
+ try {
3824
+ names = readdirSync2(accountsDir);
3825
+ } catch (err) {
3826
+ if (err.code === "ENOENT") return [];
3827
+ throw err;
3828
+ }
3829
+ const valid = [];
3830
+ for (const name of names) {
3831
+ if (!UUID_RE.test(name)) continue;
3832
+ const configPath2 = resolve6(accountsDir, name, "account.json");
3833
+ try {
3834
+ JSON.parse(readFileSync5(configPath2, "utf-8"));
3835
+ valid.push(name);
3836
+ } catch (err) {
3837
+ const code = err.code;
3838
+ if (code === "ENOENT") continue;
3839
+ }
3840
+ }
3841
+ return valid;
3842
+ }
3843
+
3774
3844
  // app/lib/whatsapp/inbound/media.ts
3775
3845
  import { randomUUID as randomUUID3 } from "crypto";
3776
3846
  import { writeFile, mkdir } from "fs/promises";
@@ -4249,9 +4319,23 @@ async function startConnection(accountId) {
4249
4319
  console.error(`${TAG13} no credentials for account=${accountId}`);
4250
4320
  return;
4251
4321
  }
4322
+ let platformAccountId;
4323
+ try {
4324
+ platformAccountId = resolvePlatformAccountId();
4325
+ console.error(
4326
+ `[whatsapp-persist] resolved-account-id waname=${accountId} uuid=${platformAccountId}`
4327
+ );
4328
+ } catch (err) {
4329
+ const reason = err instanceof Error ? err.message : String(err);
4330
+ console.error(
4331
+ `[whatsapp-persist] resolved-account-id FAIL waname=${accountId} reason=${reason}`
4332
+ );
4333
+ throw err;
4334
+ }
4252
4335
  await stopConnection(accountId);
4253
4336
  const conn = {
4254
4337
  accountId,
4338
+ platformAccountId,
4255
4339
  accountName: whatsAppConfig.accounts?.[accountId]?.name,
4256
4340
  authDir,
4257
4341
  sock: null,
@@ -4306,11 +4390,25 @@ function getSocket(accountId) {
4306
4390
  }
4307
4391
  async function registerLoginSocket(accountId, sock, authDir) {
4308
4392
  if (!configDir) throw new Error("WhatsApp manager not initialized");
4393
+ let platformAccountId;
4394
+ try {
4395
+ platformAccountId = resolvePlatformAccountId();
4396
+ console.error(
4397
+ `[whatsapp-persist] resolved-account-id waname=${accountId} uuid=${platformAccountId}`
4398
+ );
4399
+ } catch (err) {
4400
+ const reason = err instanceof Error ? err.message : String(err);
4401
+ console.error(
4402
+ `[whatsapp-persist] resolved-account-id FAIL waname=${accountId} reason=${reason}`
4403
+ );
4404
+ throw err;
4405
+ }
4309
4406
  await stopConnection(accountId);
4310
4407
  const selfId = readSelfId(authDir);
4311
4408
  const lidMapping = sock.signalRepository?.lidMapping ?? null;
4312
4409
  const conn = {
4313
4410
  accountId,
4411
+ platformAccountId,
4314
4412
  accountName: whatsAppConfig.accounts?.[accountId]?.name,
4315
4413
  authDir,
4316
4414
  sock,
@@ -4476,11 +4574,11 @@ async function connectWithReconnect(conn) {
4476
4574
  console.error(
4477
4575
  `${TAG13} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
4478
4576
  );
4479
- await new Promise((resolve25) => {
4480
- const timer2 = setTimeout(resolve25, delay);
4577
+ await new Promise((resolve26) => {
4578
+ const timer2 = setTimeout(resolve26, delay);
4481
4579
  conn.abortController.signal.addEventListener("abort", () => {
4482
4580
  clearTimeout(timer2);
4483
- resolve25();
4581
+ resolve26();
4484
4582
  }, { once: true });
4485
4583
  });
4486
4584
  }
@@ -4488,16 +4586,16 @@ async function connectWithReconnect(conn) {
4488
4586
  }
4489
4587
  }
4490
4588
  function waitForDisconnectEvent(conn) {
4491
- return new Promise((resolve25) => {
4589
+ return new Promise((resolve26) => {
4492
4590
  if (!conn.sock) {
4493
- resolve25();
4591
+ resolve26();
4494
4592
  return;
4495
4593
  }
4496
4594
  const sock = conn.sock;
4497
4595
  const handler = (update) => {
4498
4596
  if (update.connection === "close") {
4499
4597
  sock.ev.off("connection.update", handler);
4500
- resolve25();
4598
+ resolve26();
4501
4599
  }
4502
4600
  };
4503
4601
  sock.ev.on("connection.update", handler);
@@ -4518,8 +4616,8 @@ function watchForDisconnect(conn) {
4518
4616
  });
4519
4617
  }
4520
4618
  async function getGroupMeta(conn, jid) {
4521
- const cached = conn.groupMetaCache.get(jid);
4522
- if (cached && cached.expires > Date.now()) return cached;
4619
+ const cached2 = conn.groupMetaCache.get(jid);
4620
+ if (cached2 && cached2.expires > Date.now()) return cached2;
4523
4621
  if (!conn.sock) return null;
4524
4622
  console.error(`${TAG13} group metadata cache miss for ${jid}, fetching from Baileys account=${conn.accountId}`);
4525
4623
  try {
@@ -4631,6 +4729,7 @@ function monitorInbound(conn) {
4631
4729
  if (msg.key.id) {
4632
4730
  const merged = await ensureWhatsAppConversation({
4633
4731
  accountId: conn.accountId,
4732
+ platformAccountId: conn.platformAccountId,
4634
4733
  sessionKey,
4635
4734
  agentType: sessionKeyAgentType,
4636
4735
  groupJid: isGroup ? remoteJid : void 0
@@ -4638,6 +4737,7 @@ function monitorInbound(conn) {
4638
4737
  if (merged) {
4639
4738
  await persistWhatsAppMessage({
4640
4739
  accountId: conn.accountId,
4740
+ platformAccountId: conn.platformAccountId,
4641
4741
  remoteJid,
4642
4742
  sessionKey,
4643
4743
  msgKeyId: msg.key.id,
@@ -4756,8 +4856,8 @@ async function handleInboundMessage(conn, msg) {
4756
4856
  const conversationKey = isGroup ? remoteJid : senderPhone;
4757
4857
  const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
4758
4858
  let resolvePending;
4759
- const sttPending = new Promise((resolve25) => {
4760
- resolvePending = resolve25;
4859
+ const sttPending = new Promise((resolve26) => {
4860
+ resolvePending = resolve26;
4761
4861
  });
4762
4862
  if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
4763
4863
  try {
@@ -4870,20 +4970,20 @@ async function probeApiKey() {
4870
4970
  return result.status;
4871
4971
  }
4872
4972
  function checkPort(port2, timeoutMs = 500) {
4873
- return new Promise((resolve25) => {
4973
+ return new Promise((resolve26) => {
4874
4974
  const socket = createConnection(port2, "127.0.0.1");
4875
4975
  socket.setTimeout(timeoutMs);
4876
4976
  socket.once("connect", () => {
4877
4977
  socket.destroy();
4878
- resolve25(true);
4978
+ resolve26(true);
4879
4979
  });
4880
4980
  socket.once("error", () => {
4881
4981
  socket.destroy();
4882
- resolve25(false);
4982
+ resolve26(false);
4883
4983
  });
4884
4984
  socket.once("timeout", () => {
4885
4985
  socket.destroy();
4886
- resolve25(false);
4986
+ resolve26(false);
4887
4987
  });
4888
4988
  });
4889
4989
  }
@@ -4893,7 +4993,7 @@ app.get("/", async (c) => {
4893
4993
  let pinConfigured = false;
4894
4994
  try {
4895
4995
  if (existsSync6(USERS_FILE)) {
4896
- const raw = readFileSync5(USERS_FILE, "utf-8").trim();
4996
+ const raw = readFileSync6(USERS_FILE, "utf-8").trim();
4897
4997
  if (raw) {
4898
4998
  const users = JSON.parse(raw);
4899
4999
  pinConfigured = Array.isArray(users) && users.length > 0;
@@ -4978,14 +5078,14 @@ app.get("/", async (c) => {
4978
5078
  var health_default = app;
4979
5079
 
4980
5080
  // server/routes/session.ts
4981
- import { resolve as resolve6 } from "path";
5081
+ import { resolve as resolve7 } from "path";
4982
5082
  import { existsSync as existsSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4 } from "fs";
4983
- var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
5083
+ var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
4984
5084
  function writeBrandingCache(accountId, agentSlug, branding) {
4985
5085
  try {
4986
- const cacheDir = resolve6(MAXY_DIR, "branding-cache", accountId);
5086
+ const cacheDir = resolve7(MAXY_DIR, "branding-cache", accountId);
4987
5087
  mkdirSync4(cacheDir, { recursive: true });
4988
- writeFileSync5(resolve6(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
5088
+ writeFileSync5(resolve7(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
4989
5089
  } catch (err) {
4990
5090
  console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
4991
5091
  }
@@ -4995,7 +5095,7 @@ function parseVisitorCookie(cookieHeader) {
4995
5095
  const match = cookieHeader.match(/(?:^|;\s*)maxy_visitor=([^;]*)/);
4996
5096
  if (!match) return null;
4997
5097
  const value = decodeURIComponent(match[1]).trim();
4998
- return UUID_RE.test(value) ? value : null;
5098
+ return UUID_RE2.test(value) ? value : null;
4999
5099
  }
5000
5100
  function withVisitorCookie(response, visitorId) {
5001
5101
  if (!visitorId) return response;
@@ -5055,8 +5155,8 @@ app2.post("/", async (c) => {
5055
5155
  }
5056
5156
  let agentConfig = null;
5057
5157
  if (account) {
5058
- const agentDir = resolve6(account.accountDir, "agents", agentSlug);
5059
- const agentConfigPath = resolve6(agentDir, "config.json");
5158
+ const agentDir = resolve7(account.accountDir, "agents", agentSlug);
5159
+ const agentConfigPath = resolve7(agentDir, "config.json");
5060
5160
  if (!existsSync7(agentDir) || !existsSync7(agentConfigPath)) {
5061
5161
  return c.json({ error: "Agent not found" }, 404);
5062
5162
  }
@@ -5310,9 +5410,9 @@ ${raw}`;
5310
5410
  import { randomUUID as randomUUID4 } from "crypto";
5311
5411
  import { mkdir as mkdir2, readFile, stat as stat2, writeFile as writeFile2 } from "fs/promises";
5312
5412
  import { realpathSync } from "fs";
5313
- import { resolve as resolve7, extname, basename as basename3 } from "path";
5314
- var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve7(process.cwd(), "../platform");
5315
- var ATTACHMENTS_ROOT = resolve7(PLATFORM_ROOT2, "..", "data/uploads");
5413
+ import { resolve as resolve8, extname, basename as basename3 } from "path";
5414
+ var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve8(process.cwd(), "../platform");
5415
+ var ATTACHMENTS_ROOT = resolve8(PLATFORM_ROOT2, "..", "data/uploads");
5316
5416
  var SUPPORTED_MIME_TYPES = /* @__PURE__ */ new Set([
5317
5417
  "image/jpeg",
5318
5418
  "image/png",
@@ -5339,11 +5439,11 @@ function assertSupportedMime(mimeType) {
5339
5439
  }
5340
5440
  async function writeAttachment(scope, filename, mimeType, sizeBytes, buffer) {
5341
5441
  const attachmentId = randomUUID4();
5342
- const dir = resolve7(ATTACHMENTS_ROOT, scope, attachmentId);
5442
+ const dir = resolve8(ATTACHMENTS_ROOT, scope, attachmentId);
5343
5443
  await mkdir2(dir, { recursive: true });
5344
5444
  const ext = extname(filename) || "";
5345
- const storagePath = resolve7(dir, `${attachmentId}${ext}`);
5346
- const metaPath = resolve7(dir, `${attachmentId}.meta.json`);
5445
+ const storagePath = resolve8(dir, `${attachmentId}${ext}`);
5446
+ const metaPath = resolve8(dir, `${attachmentId}.meta.json`);
5347
5447
  const meta = {
5348
5448
  attachmentId,
5349
5449
  scope,
@@ -6021,16 +6121,16 @@ var group_default = app4;
6021
6121
 
6022
6122
  // app/lib/access-gate.ts
6023
6123
  import neo4j from "neo4j-driver";
6024
- import { readFileSync as readFileSync6 } from "fs";
6025
- import { resolve as resolve8 } from "path";
6124
+ import { readFileSync as readFileSync7 } from "fs";
6125
+ import { resolve as resolve9 } from "path";
6026
6126
  import { randomUUID as randomUUID5, randomInt } from "crypto";
6027
- var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT ?? resolve8(process.cwd(), "..");
6127
+ var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT ?? resolve9(process.cwd(), "..");
6028
6128
  var driver = null;
6029
6129
  function readPassword() {
6030
6130
  if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
6031
- const passwordFile = resolve8(PLATFORM_ROOT3, "config/.neo4j-password");
6131
+ const passwordFile = resolve9(PLATFORM_ROOT3, "config/.neo4j-password");
6032
6132
  try {
6033
- return readFileSync6(passwordFile, "utf-8").trim();
6133
+ return readFileSync7(passwordFile, "utf-8").trim();
6034
6134
  } catch {
6035
6135
  throw new Error(
6036
6136
  `Neo4j password not found. Expected at ${passwordFile} or in NEO4J_PASSWORD env var.`
@@ -6341,19 +6441,19 @@ async function findActiveGrantByContact(contactValue, agentSlug, accountId) {
6341
6441
  }
6342
6442
 
6343
6443
  // app/lib/brevo-sms.ts
6344
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, existsSync as existsSync8, chmodSync } from "fs";
6444
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, mkdirSync as mkdirSync5, existsSync as existsSync8, chmodSync } from "fs";
6345
6445
  import { dirname as dirname4 } from "path";
6346
- import { resolve as resolve9 } from "path";
6347
- var BREVO_API_KEY_FILE = resolve9(MAXY_DIR, ".brevo-api-key");
6446
+ import { resolve as resolve10 } from "path";
6447
+ var BREVO_API_KEY_FILE = resolve10(MAXY_DIR, ".brevo-api-key");
6348
6448
  var BREVO_API_URL = "https://api.brevo.com/v3/transactionalSMS/sms";
6349
6449
  var BREVO_TIMEOUT_MS = 1e4;
6350
6450
  var BREVO_SENDER = "Maxy";
6351
6451
  var platformRoot = process.env.MAXY_PLATFORM_ROOT;
6352
6452
  if (platformRoot) {
6353
6453
  try {
6354
- const brandPath = resolve9(platformRoot, "config", "brand.json");
6454
+ const brandPath = resolve10(platformRoot, "config", "brand.json");
6355
6455
  if (existsSync8(brandPath)) {
6356
- const brand = JSON.parse(readFileSync7(brandPath, "utf-8"));
6456
+ const brand = JSON.parse(readFileSync8(brandPath, "utf-8"));
6357
6457
  if (brand.productName) BREVO_SENDER = brand.productName;
6358
6458
  }
6359
6459
  } catch {
@@ -6361,7 +6461,7 @@ if (platformRoot) {
6361
6461
  }
6362
6462
  function readBrevoApiKey() {
6363
6463
  try {
6364
- const key = readFileSync7(BREVO_API_KEY_FILE, "utf-8").trim();
6464
+ const key = readFileSync8(BREVO_API_KEY_FILE, "utf-8").trim();
6365
6465
  if (!key) {
6366
6466
  throw new Error(`Brevo API key file is empty: ${BREVO_API_KEY_FILE}`);
6367
6467
  }
@@ -6792,7 +6892,7 @@ app5.post("/send-otp", async (c) => {
6792
6892
  var access_default = app5;
6793
6893
 
6794
6894
  // server/routes/telegram.ts
6795
- import { existsSync as existsSync9, readFileSync as readFileSync8 } from "fs";
6895
+ import { existsSync as existsSync9, readFileSync as readFileSync9 } from "fs";
6796
6896
  import { timingSafeEqual } from "crypto";
6797
6897
 
6798
6898
  // app/lib/telegram/access-control.ts
@@ -6830,7 +6930,7 @@ function getWebhookSecret(botType) {
6830
6930
  const filePath = botType === "admin" ? TELEGRAM_ADMIN_WEBHOOK_SECRET_FILE : TELEGRAM_WEBHOOK_SECRET_FILE;
6831
6931
  try {
6832
6932
  if (!existsSync9(filePath)) return null;
6833
- const secret = readFileSync8(filePath, "utf-8").trim();
6933
+ const secret = readFileSync9(filePath, "utf-8").trim();
6834
6934
  return secret || null;
6835
6935
  } catch {
6836
6936
  return null;
@@ -6988,9 +7088,9 @@ app6.post("/webhook", async (c) => {
6988
7088
  var telegram_default = app6;
6989
7089
 
6990
7090
  // server/routes/whatsapp.ts
6991
- import { join as join6, resolve as resolve10, basename as basename4 } from "path";
7091
+ import { join as join6, resolve as resolve11, basename as basename4 } from "path";
6992
7092
  import { readFile as readFile2, stat as stat3 } from "fs/promises";
6993
- import { realpathSync as realpathSync2, readdirSync as readdirSync2, readFileSync as readFileSync9, existsSync as existsSync10 } from "fs";
7093
+ import { realpathSync as realpathSync2, readdirSync as readdirSync3, readFileSync as readFileSync10, existsSync as existsSync10 } from "fs";
6994
7094
 
6995
7095
  // app/lib/whatsapp/login.ts
6996
7096
  import { randomUUID as randomUUID6 } from "crypto";
@@ -7096,8 +7196,8 @@ async function startLogin(opts) {
7096
7196
  resetActiveLogin(accountId);
7097
7197
  let resolveQr = null;
7098
7198
  let rejectQr = null;
7099
- const qrPromise = new Promise((resolve25, reject) => {
7100
- resolveQr = resolve25;
7199
+ const qrPromise = new Promise((resolve26, reject) => {
7200
+ resolveQr = resolve26;
7101
7201
  rejectQr = reject;
7102
7202
  });
7103
7203
  const qrTimer = setTimeout(
@@ -7491,17 +7591,17 @@ app7.post("/config", async (c) => {
7491
7591
  return c.json(result, result.ok ? 200 : 400);
7492
7592
  }
7493
7593
  case "list-public-agents": {
7494
- const agentsDir = resolve10(account.accountDir, "agents");
7594
+ const agentsDir = resolve11(account.accountDir, "agents");
7495
7595
  const agents = [];
7496
7596
  if (existsSync10(agentsDir)) {
7497
7597
  try {
7498
- const entries = readdirSync2(agentsDir, { withFileTypes: true });
7598
+ const entries = readdirSync3(agentsDir, { withFileTypes: true });
7499
7599
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
7500
7600
  if (!entry.isDirectory() || entry.name === "admin") continue;
7501
- const configPath2 = resolve10(agentsDir, entry.name, "config.json");
7601
+ const configPath2 = resolve11(agentsDir, entry.name, "config.json");
7502
7602
  if (!existsSync10(configPath2)) continue;
7503
7603
  try {
7504
- const config = JSON.parse(readFileSync9(configPath2, "utf-8"));
7604
+ const config = JSON.parse(readFileSync10(configPath2, "utf-8"));
7505
7605
  agents.push({ slug: entry.name, displayName: config.displayName ?? entry.name });
7506
7606
  } catch {
7507
7607
  console.error(`${TAG18} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
@@ -7576,7 +7676,7 @@ app7.post("/send-document", async (c) => {
7576
7676
  if (!maxyAccountId || !PLATFORM_ROOT4) {
7577
7677
  return c.json({ error: "Cannot validate file path: missing account or platform context" }, 400);
7578
7678
  }
7579
- const accountDir = resolve10(PLATFORM_ROOT4, "..", "data/accounts", maxyAccountId);
7679
+ const accountDir = resolve11(PLATFORM_ROOT4, "..", "data/accounts", maxyAccountId);
7580
7680
  let resolvedPath;
7581
7681
  try {
7582
7682
  resolvedPath = realpathSync2(filePath);
@@ -7713,8 +7813,8 @@ var whatsapp_default = app7;
7713
7813
 
7714
7814
  // server/routes/onboarding.ts
7715
7815
  import { spawn, execFileSync } from "child_process";
7716
- import { openSync as openSync2, closeSync as closeSync2, writeFileSync as writeFileSync7, writeSync, existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync10, unlinkSync } from "fs";
7717
- import { resolve as resolve11, dirname as dirname5 } from "path";
7816
+ import { openSync as openSync2, closeSync as closeSync2, writeFileSync as writeFileSync7, writeSync, existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync11, unlinkSync } from "fs";
7817
+ import { resolve as resolve12, dirname as dirname5 } from "path";
7718
7818
  import { createHash, randomUUID as randomUUID7 } from "crypto";
7719
7819
  var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT || "";
7720
7820
  function hashPin(pin) {
@@ -7722,7 +7822,7 @@ function hashPin(pin) {
7722
7822
  }
7723
7823
  function readUsersFile() {
7724
7824
  if (!existsSync11(USERS_FILE)) return null;
7725
- const raw = readFileSync10(USERS_FILE, "utf-8").trim();
7825
+ const raw = readFileSync11(USERS_FILE, "utf-8").trim();
7726
7826
  if (!raw) return [];
7727
7827
  return JSON.parse(raw);
7728
7828
  }
@@ -7843,7 +7943,7 @@ app8.post("/set-pin", async (c) => {
7843
7943
  console.log(`[set-pin] wrote users.json: userId=${userId.slice(0, 8)}\u2026 hash=${hash.slice(0, 8)}\u2026`);
7844
7944
  if (account) {
7845
7945
  try {
7846
- const config = JSON.parse(readFileSync10(`${account.accountDir}/account.json`, "utf-8"));
7946
+ const config = JSON.parse(readFileSync11(`${account.accountDir}/account.json`, "utf-8"));
7847
7947
  if (!config.admins) config.admins = [];
7848
7948
  if (!config.admins.some((a) => a.userId === userId)) {
7849
7949
  config.admins.push({ userId, role: "owner" });
@@ -7919,16 +8019,16 @@ app8.post("/skip", async (c) => {
7919
8019
  }
7920
8020
  const { accountId, accountDir } = account;
7921
8021
  let agentName = "Maxy";
7922
- const brandPath = PLATFORM_ROOT5 ? resolve11(PLATFORM_ROOT5, "config", "brand.json") : "";
8022
+ const brandPath = PLATFORM_ROOT5 ? resolve12(PLATFORM_ROOT5, "config", "brand.json") : "";
7923
8023
  if (brandPath && existsSync11(brandPath)) {
7924
8024
  try {
7925
- const brand = JSON.parse(readFileSync10(brandPath, "utf-8"));
8025
+ const brand = JSON.parse(readFileSync11(brandPath, "utf-8"));
7926
8026
  if (brand.productName) agentName = brand.productName;
7927
8027
  } catch (err) {
7928
8028
  console.error(`[onboarding-skip] brand.json read failed: ${err instanceof Error ? err.message : String(err)}`);
7929
8029
  }
7930
8030
  }
7931
- const soulPath = resolve11(accountDir, "agents", "admin", "SOUL.md");
8031
+ const soulPath = resolve12(accountDir, "agents", "admin", "SOUL.md");
7932
8032
  try {
7933
8033
  mkdirSync6(dirname5(soulPath), { recursive: true });
7934
8034
  writeFileSync7(soulPath, `You are ${agentName}, an AI operations manager.
@@ -8070,7 +8170,8 @@ app9.post("/", async (c) => {
8070
8170
  "unknown",
8071
8171
  "graph-labels-in-graph",
8072
8172
  "graph-default-view",
8073
- "graph-nav"
8173
+ "graph-nav",
8174
+ "event"
8074
8175
  ]);
8075
8176
  const kind = allowedKinds.has(kindRaw) ? kindRaw : "unknown";
8076
8177
  const msg = truncate(body.msg, MAX_MSG_LEN);
@@ -8084,36 +8185,56 @@ app9.post("/", async (c) => {
8084
8185
  const stackTrunc = truncate(body.stack, MAX_STACK_LEN);
8085
8186
  const head = stackHead(body.stack);
8086
8187
  const ts = (/* @__PURE__ */ new Date()).toISOString();
8087
- console.error(
8088
- `[client-error] ts=${ts} ip=${ip} kind=${kind} url=${JSON.stringify(url)} version=${version || "unknown"} ua=${JSON.stringify(ua)} msg=${JSON.stringify(msg)} stack-head=${JSON.stringify(head)} file-line-col=${JSON.stringify(fileLineCol)}`
8089
- );
8090
- rotateIfNeeded();
8091
- try {
8092
- const payload = {
8093
- ts,
8094
- ip,
8095
- kind,
8096
- url,
8097
- version,
8098
- ua,
8099
- msg,
8100
- stack: stackTrunc,
8101
- filename: file,
8102
- lineno: line,
8103
- colno: col,
8104
- tag: typeof body.tag === "string" ? truncate(body.tag, 32) : void 0,
8105
- status: typeof body.status === "number" ? body.status : void 0
8106
- };
8107
- appendFileSync2(CLIENT_ERRORS_LOG, JSON.stringify(payload) + "\n", "utf-8");
8108
- } catch (err) {
8109
- console.error(`[client-error] append failed: ${err instanceof Error ? err.message : String(err)}`);
8188
+ if (kind === "event") {
8189
+ const source = typeof body.source === "string" ? truncate(body.source, 64) : "unknown";
8190
+ const extra = Object.entries(body).filter(([k]) => !["kind", "source", "msg", "url", "ua", "version", "stack", "filename", "lineno", "colno", "tag", "status"].includes(k)).map(([k, v]) => {
8191
+ const safe = typeof v === "string" ? truncate(v, 200) : typeof v === "number" || typeof v === "boolean" ? String(v) : JSON.stringify(v).slice(0, 200);
8192
+ return `${k}=${safe}`;
8193
+ }).join(" ");
8194
+ const TAGGED_PREFIX_SOURCES = /* @__PURE__ */ new Set(["admin-chat-relay-poll"]);
8195
+ if (TAGGED_PREFIX_SOURCES.has(source)) {
8196
+ console.log(
8197
+ `[${source}] ts=${ts} ip=${ip} version=${version || "unknown"}${extra ? " " + extra : ""}`
8198
+ );
8199
+ } else {
8200
+ console.log(
8201
+ `[client-event] ts=${ts} ip=${ip} source=${source} version=${version || "unknown"}${extra ? " " + extra : ""}`
8202
+ );
8203
+ }
8204
+ } else {
8205
+ console.error(
8206
+ `[client-error] ts=${ts} ip=${ip} kind=${kind} url=${JSON.stringify(url)} version=${version || "unknown"} ua=${JSON.stringify(ua)} msg=${JSON.stringify(msg)} stack-head=${JSON.stringify(head)} file-line-col=${JSON.stringify(fileLineCol)}`
8207
+ );
8208
+ }
8209
+ if (kind !== "event") {
8210
+ rotateIfNeeded();
8211
+ try {
8212
+ const payload = {
8213
+ ts,
8214
+ ip,
8215
+ kind,
8216
+ url,
8217
+ version,
8218
+ ua,
8219
+ msg,
8220
+ stack: stackTrunc,
8221
+ filename: file,
8222
+ lineno: line,
8223
+ colno: col,
8224
+ tag: typeof body.tag === "string" ? truncate(body.tag, 32) : void 0,
8225
+ status: typeof body.status === "number" ? body.status : void 0
8226
+ };
8227
+ appendFileSync2(CLIENT_ERRORS_LOG, JSON.stringify(payload) + "\n", "utf-8");
8228
+ } catch (err) {
8229
+ console.error(`[client-error] append failed: ${err instanceof Error ? err.message : String(err)}`);
8230
+ }
8110
8231
  }
8111
8232
  return c.json({ ok: true });
8112
8233
  });
8113
8234
  var client_error_default = app9;
8114
8235
 
8115
8236
  // server/routes/admin/session.ts
8116
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync8, existsSync as existsSync13 } from "fs";
8237
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync8, existsSync as existsSync13 } from "fs";
8117
8238
  import { createHash as createHash2 } from "crypto";
8118
8239
  var deprecationLogged = /* @__PURE__ */ new Set();
8119
8240
  function hashPin2(pin) {
@@ -8121,7 +8242,7 @@ function hashPin2(pin) {
8121
8242
  }
8122
8243
  function readUsersFile2() {
8123
8244
  if (!existsSync13(USERS_FILE)) return null;
8124
- const raw = readFileSync11(USERS_FILE, "utf-8").trim();
8245
+ const raw = readFileSync12(USERS_FILE, "utf-8").trim();
8125
8246
  if (!raw) return [];
8126
8247
  return JSON.parse(raw);
8127
8248
  }
@@ -8295,7 +8416,7 @@ app10.post("/", async (c) => {
8295
8416
  var session_default2 = app10;
8296
8417
 
8297
8418
  // server/routes/admin/chat.ts
8298
- import { resolve as resolve12 } from "path";
8419
+ import { resolve as resolve13 } from "path";
8299
8420
  import { appendFileSync as appendFileSync4 } from "fs";
8300
8421
 
8301
8422
  // app/lib/script-stream-tailer.ts
@@ -8586,7 +8707,7 @@ var app11 = new Hono();
8586
8707
  app11.post("/cancel", requireAdminSession, async (c) => {
8587
8708
  const session_key = c.var.sessionKey;
8588
8709
  try {
8589
- const { interruptClient: interruptClient2 } = await import("./client-pool-LXE7RIRT.js");
8710
+ const { interruptClient: interruptClient2 } = await import("./client-pool-N2Y57223.js");
8590
8711
  await interruptClient2(session_key);
8591
8712
  return c.json({ ok: true });
8592
8713
  } catch (err) {
@@ -8750,7 +8871,7 @@ app11.post("/", requireAdminSession, async (c) => {
8750
8871
  function resolveTeeStreamLogPath() {
8751
8872
  const liveConvId = getConversationIdForSession(session_key);
8752
8873
  const key = liveConvId ?? preflushStreamLogKey(session_key);
8753
- return resolve12(account.accountDir, "logs", `claude-agent-stream-${key}.log`);
8874
+ return resolve13(account.accountDir, "logs", `claude-agent-stream-${key}.log`);
8754
8875
  }
8755
8876
  try {
8756
8877
  appendFileSync4(resolveTeeStreamLogPath(), `[${(/* @__PURE__ */ new Date()).toISOString()}] [chat-route-version=task606-tee-path-resolve] sessionKey=${session_key.slice(0, 12)}\u2026
@@ -8852,7 +8973,7 @@ app11.post("/", requireAdminSession, async (c) => {
8852
8973
  try {
8853
8974
  registerAdminSSE(sseEntry);
8854
8975
  if (sseConvId) {
8855
- const streamLogPath = resolve12(account.accountDir, "logs", `claude-agent-stream-${sseConvId}.log`);
8976
+ const streamLogPath = resolve13(account.accountDir, "logs", `claude-agent-stream-${sseConvId}.log`);
8856
8977
  tailer = startScriptStreamTailer({
8857
8978
  path: streamLogPath,
8858
8979
  onEvent: (event) => {
@@ -8981,8 +9102,8 @@ app12.post("/", requireAdminSession, async (c) => {
8981
9102
  var compact_default = app12;
8982
9103
 
8983
9104
  // server/routes/admin/logs.ts
8984
- import { existsSync as existsSync15, readdirSync as readdirSync3, readFileSync as readFileSync12, statSync as statSync7 } from "fs";
8985
- import { resolve as resolve13, basename as basename5 } from "path";
9105
+ import { existsSync as existsSync15, readdirSync as readdirSync4, readFileSync as readFileSync13, statSync as statSync7 } from "fs";
9106
+ import { resolve as resolve14, basename as basename5 } from "path";
8986
9107
 
8987
9108
  // app/lib/logs-read-resolve.ts
8988
9109
  import { existsSync as existsSync14 } from "fs";
@@ -9026,7 +9147,7 @@ app13.get("/", async (c) => {
9026
9147
  const sessionKeyParam = c.req.query("sessionKey");
9027
9148
  const download = c.req.query("download") === "1";
9028
9149
  const account = resolveAccount();
9029
- const accountLogDir2 = account ? resolve13(account.accountDir, "logs") : null;
9150
+ const accountLogDir2 = account ? resolve14(account.accountDir, "logs") : null;
9030
9151
  const logDirs = [];
9031
9152
  if (accountLogDir2) logDirs.push(accountLogDir2);
9032
9153
  logDirs.push(LOG_DIR);
@@ -9034,10 +9155,10 @@ app13.get("/", async (c) => {
9034
9155
  const safe = basename5(fileParam);
9035
9156
  const searched = [];
9036
9157
  for (const dir of logDirs) {
9037
- const filePath = resolve13(dir, safe);
9158
+ const filePath = resolve14(dir, safe);
9038
9159
  searched.push(filePath);
9039
9160
  try {
9040
- const buffer = readFileSync12(filePath);
9161
+ const buffer = readFileSync13(filePath);
9041
9162
  const onDiskBytes = statSync7(filePath).size;
9042
9163
  const headers = {
9043
9164
  "Content-Type": "text/plain; charset=utf-8",
@@ -9112,7 +9233,7 @@ app13.get("/", async (c) => {
9112
9233
  try {
9113
9234
  const filename = basename5(hit.path);
9114
9235
  if (stalePreflushCount > 0 && !download) {
9115
- const content = readFileSync12(hit.path, "utf-8");
9236
+ const content = readFileSync13(hit.path, "utf-8");
9116
9237
  return c.json({
9117
9238
  log: content,
9118
9239
  filename,
@@ -9120,7 +9241,7 @@ app13.get("/", async (c) => {
9120
9241
  warnings: stalePreflushPaths.map((path2) => ({ kind: "stale-preflush", path: path2 }))
9121
9242
  });
9122
9243
  }
9123
- const buffer = readFileSync12(hit.path);
9244
+ const buffer = readFileSync13(hit.path);
9124
9245
  const onDiskBytes = statSync7(hit.path).size;
9125
9246
  const headers = {
9126
9247
  "Content-Type": "text/plain; charset=utf-8",
@@ -9157,16 +9278,16 @@ app13.get("/", async (c) => {
9157
9278
  if (!existsSync15(dir)) continue;
9158
9279
  let files;
9159
9280
  try {
9160
- files = readdirSync3(dir).filter((f) => f.endsWith(".log"));
9281
+ files = readdirSync4(dir).filter((f) => f.endsWith(".log"));
9161
9282
  } catch (err) {
9162
9283
  const reason = err instanceof Error ? err.message : String(err);
9163
9284
  console.warn(`[admin/logs] readdir-fail dir=${dir} reason=${reason}`);
9164
9285
  continue;
9165
9286
  }
9166
- files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync7(resolve13(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
9287
+ files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync7(resolve14(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
9167
9288
  seen.add(name);
9168
9289
  try {
9169
- const content = readFileSync12(resolve13(dir, name));
9290
+ const content = readFileSync13(resolve14(dir, name));
9170
9291
  const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
9171
9292
  logs[name] = tail.trim() || "(empty)";
9172
9293
  } catch (err) {
@@ -9206,7 +9327,7 @@ var claude_info_default = app14;
9206
9327
  // server/routes/admin/attachment.ts
9207
9328
  import { readFile as readFile3, readdir } from "fs/promises";
9208
9329
  import { existsSync as existsSync16 } from "fs";
9209
- import { resolve as resolve14 } from "path";
9330
+ import { resolve as resolve15 } from "path";
9210
9331
  var app15 = new Hono();
9211
9332
  app15.get("/:attachmentId", requireAdminSession, async (c) => {
9212
9333
  const attachmentId = c.req.param("attachmentId");
@@ -9218,11 +9339,11 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
9218
9339
  if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(attachmentId)) {
9219
9340
  return new Response("Not found", { status: 404 });
9220
9341
  }
9221
- const dir = resolve14(ATTACHMENTS_ROOT, accountId, attachmentId);
9342
+ const dir = resolve15(ATTACHMENTS_ROOT, accountId, attachmentId);
9222
9343
  if (!existsSync16(dir)) {
9223
9344
  return new Response("Not found", { status: 404 });
9224
9345
  }
9225
- const metaPath = resolve14(dir, `${attachmentId}.meta.json`);
9346
+ const metaPath = resolve15(dir, `${attachmentId}.meta.json`);
9226
9347
  if (!existsSync16(metaPath)) {
9227
9348
  return new Response("Not found", { status: 404 });
9228
9349
  }
@@ -9237,7 +9358,7 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
9237
9358
  if (!dataFile) {
9238
9359
  return new Response("Not found", { status: 404 });
9239
9360
  }
9240
- const filePath = resolve14(dir, dataFile);
9361
+ const filePath = resolve15(dir, dataFile);
9241
9362
  const buffer = await readFile3(filePath);
9242
9363
  return new Response(new Uint8Array(buffer), {
9243
9364
  headers: {
@@ -9250,24 +9371,24 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
9250
9371
  var attachment_default = app15;
9251
9372
 
9252
9373
  // server/routes/admin/agents.ts
9253
- import { resolve as resolve15 } from "path";
9254
- import { readdirSync as readdirSync4, readFileSync as readFileSync13, existsSync as existsSync17, rmSync } from "fs";
9374
+ import { resolve as resolve16 } from "path";
9375
+ import { readdirSync as readdirSync5, readFileSync as readFileSync14, existsSync as existsSync17, rmSync } from "fs";
9255
9376
  var app16 = new Hono();
9256
9377
  app16.get("/", (c) => {
9257
9378
  const account = resolveAccount();
9258
9379
  if (!account) return c.json({ agents: [] });
9259
- const agentsDir = resolve15(account.accountDir, "agents");
9380
+ const agentsDir = resolve16(account.accountDir, "agents");
9260
9381
  if (!existsSync17(agentsDir)) return c.json({ agents: [] });
9261
9382
  const agents = [];
9262
9383
  try {
9263
- const entries = readdirSync4(agentsDir, { withFileTypes: true });
9384
+ const entries = readdirSync5(agentsDir, { withFileTypes: true });
9264
9385
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
9265
9386
  if (!entry.isDirectory()) continue;
9266
9387
  if (entry.name === "admin") continue;
9267
- const configPath2 = resolve15(agentsDir, entry.name, "config.json");
9388
+ const configPath2 = resolve16(agentsDir, entry.name, "config.json");
9268
9389
  if (!existsSync17(configPath2)) continue;
9269
9390
  try {
9270
- const config = JSON.parse(readFileSync13(configPath2, "utf-8"));
9391
+ const config = JSON.parse(readFileSync14(configPath2, "utf-8"));
9271
9392
  agents.push({
9272
9393
  slug: entry.name,
9273
9394
  displayName: config.displayName ?? entry.name,
@@ -9293,7 +9414,7 @@ app16.delete("/:slug", async (c) => {
9293
9414
  if (slug.includes("/") || slug.includes("..") || slug.includes("\\")) {
9294
9415
  return c.json({ error: "Invalid agent slug" }, 400);
9295
9416
  }
9296
- const agentDir = resolve15(account.accountDir, "agents", slug);
9417
+ const agentDir = resolve16(account.accountDir, "agents", slug);
9297
9418
  if (!existsSync17(agentDir)) {
9298
9419
  return c.json({ error: "Agent not found" }, 404);
9299
9420
  }
@@ -9323,7 +9444,7 @@ app16.post("/:slug/project", async (c) => {
9323
9444
  if (slug.includes("/") || slug.includes("..") || slug.includes("\\")) {
9324
9445
  return c.json({ error: "Invalid agent slug" }, 400);
9325
9446
  }
9326
- const agentDir = resolve15(account.accountDir, "agents", slug);
9447
+ const agentDir = resolve16(account.accountDir, "agents", slug);
9327
9448
  if (!existsSync17(agentDir)) {
9328
9449
  return c.json({ error: "Agent not found on disk" }, 404);
9329
9450
  }
@@ -9749,13 +9870,13 @@ async function cdpNavigateNewTab(url, opts = {}) {
9749
9870
  // server/routes/admin/device-browser.ts
9750
9871
  var app19 = new Hono();
9751
9872
  app19.post("/navigate", async (c) => {
9752
- const TAG19 = "[device-url:click]";
9873
+ const TAG20 = "[device-url:click]";
9753
9874
  let body;
9754
9875
  try {
9755
9876
  body = await c.req.json();
9756
9877
  } catch (err) {
9757
9878
  const detail = err instanceof Error ? err.message : String(err);
9758
- console.error(`${TAG19} reject reason=body-not-json detail=${detail} browser=fallback navigateResult=error`);
9879
+ console.error(`${TAG20} reject reason=body-not-json detail=${detail} browser=fallback navigateResult=error`);
9759
9880
  return c.json(
9760
9881
  { ok: false, navigateResult: "error", browser: "fallback", detail: "Request body was not valid JSON" },
9761
9882
  400
@@ -9765,7 +9886,7 @@ app19.post("/navigate", async (c) => {
9765
9886
  const intent = typeof body.intent === "string" ? body.intent : "";
9766
9887
  const hostname2 = typeof body.hostname === "string" ? body.hostname : "";
9767
9888
  if (!url) {
9768
- console.error(`${TAG19} reject reason=missing-url intent=${JSON.stringify(intent)} browser=fallback navigateResult=error`);
9889
+ console.error(`${TAG20} reject reason=missing-url intent=${JSON.stringify(intent)} browser=fallback navigateResult=error`);
9769
9890
  return c.json(
9770
9891
  { ok: false, navigateResult: "error", browser: "fallback", detail: "url field is required" },
9771
9892
  400
@@ -9775,7 +9896,7 @@ app19.post("/navigate", async (c) => {
9775
9896
  try {
9776
9897
  parsed = new URL(url);
9777
9898
  } catch {
9778
- console.error(`${TAG19} reject reason=url-malformed intent=${JSON.stringify(intent)} url=${url} browser=fallback navigateResult=error`);
9899
+ console.error(`${TAG20} reject reason=url-malformed intent=${JSON.stringify(intent)} url=${url} browser=fallback navigateResult=error`);
9779
9900
  return c.json(
9780
9901
  { ok: false, navigateResult: "error", browser: "fallback", detail: "url is not a valid URL" },
9781
9902
  400
@@ -9783,7 +9904,7 @@ app19.post("/navigate", async (c) => {
9783
9904
  }
9784
9905
  if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
9785
9906
  console.error(
9786
- `${TAG19} reject reason=scheme-not-allowed scheme=${parsed.protocol} intent=${JSON.stringify(intent)} browser=fallback navigateResult=error`
9907
+ `${TAG20} reject reason=scheme-not-allowed scheme=${parsed.protocol} intent=${JSON.stringify(intent)} browser=fallback navigateResult=error`
9787
9908
  );
9788
9909
  return c.json(
9789
9910
  {
@@ -9799,7 +9920,7 @@ app19.post("/navigate", async (c) => {
9799
9920
  const cdpOk = await ensureCdp(transport);
9800
9921
  if (!cdpOk) {
9801
9922
  console.error(
9802
- `${TAG19} intent=${JSON.stringify(intent)} browser=fallback navigateResult=cdp-unreachable hostname=${JSON.stringify(hostname2)}`
9923
+ `${TAG20} intent=${JSON.stringify(intent)} browser=fallback navigateResult=cdp-unreachable hostname=${JSON.stringify(hostname2)}`
9803
9924
  );
9804
9925
  return c.json(
9805
9926
  {
@@ -9815,7 +9936,7 @@ app19.post("/navigate", async (c) => {
9815
9936
  const browser = outcome.result === "ok" ? "vnc" : "fallback";
9816
9937
  const detailStr = outcome.detail ? ` detail=${JSON.stringify(outcome.detail.length > 230 ? outcome.detail.slice(0, 227) + "..." : outcome.detail)}` : "";
9817
9938
  console.error(
9818
- `${TAG19} intent=${JSON.stringify(intent)} browser=${browser} navigateResult=${outcome.result} hostname=${JSON.stringify(hostname2)} targetId=${outcome.targetId ?? "none"}${detailStr}`
9939
+ `${TAG20} intent=${JSON.stringify(intent)} browser=${browser} navigateResult=${outcome.result} hostname=${JSON.stringify(hostname2)} targetId=${outcome.targetId ?? "none"}${detailStr}`
9819
9940
  );
9820
9941
  if (outcome.result !== "ok") {
9821
9942
  return c.json(
@@ -9846,18 +9967,18 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
9846
9967
  ]);
9847
9968
  var app20 = new Hono();
9848
9969
  app20.post("/", async (c) => {
9849
- const TAG19 = "[admin:events]";
9970
+ const TAG20 = "[admin:events]";
9850
9971
  let body;
9851
9972
  try {
9852
9973
  body = await c.req.json();
9853
9974
  } catch (err) {
9854
9975
  const detail = err instanceof Error ? err.message : String(err);
9855
- console.error(`${TAG19} reject reason=body-not-json detail=${detail}`);
9976
+ console.error(`${TAG20} reject reason=body-not-json detail=${detail}`);
9856
9977
  return c.json({ ok: false, detail: "Request body was not valid JSON" }, 400);
9857
9978
  }
9858
9979
  const event = typeof body.event === "string" ? body.event : "";
9859
9980
  if (!ALLOWED_EVENTS.has(event)) {
9860
- console.error(`${TAG19} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
9981
+ console.error(`${TAG20} reject reason=event-not-allowed event=${JSON.stringify(event)}`);
9861
9982
  return c.json({ ok: false, detail: `Event "${event}" is not allowed` }, 400);
9862
9983
  }
9863
9984
  const rawFields = body.fields && typeof body.fields === "object" ? body.fields : {};
@@ -9880,8 +10001,8 @@ var events_default = app20;
9880
10001
 
9881
10002
  // server/routes/admin/cloudflare.ts
9882
10003
  import { homedir } from "os";
9883
- import { resolve as resolve17 } from "path";
9884
- import { readFileSync as readFileSync15 } from "fs";
10004
+ import { resolve as resolve18 } from "path";
10005
+ import { readFileSync as readFileSync17 } from "fs";
9885
10006
 
9886
10007
  // app/lib/dns-label.ts
9887
10008
  var VALID_LABEL = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
@@ -9897,14 +10018,14 @@ function isValidDomain(value) {
9897
10018
  }
9898
10019
 
9899
10020
  // app/lib/alias-domains.ts
9900
- import { existsSync as existsSync19, mkdirSync as mkdirSync8, readFileSync as readFileSync14, writeFileSync as writeFileSync9 } from "fs";
10021
+ import { existsSync as existsSync19, mkdirSync as mkdirSync8, readFileSync as readFileSync15, writeFileSync as writeFileSync9 } from "fs";
9901
10022
  import { dirname as dirname7 } from "path";
9902
- import { resolve as resolve16 } from "path";
9903
- var ALIAS_DOMAINS_PATH = resolve16(MAXY_DIR, "alias-domains.json");
10023
+ import { resolve as resolve17 } from "path";
10024
+ var ALIAS_DOMAINS_PATH = resolve17(MAXY_DIR, "alias-domains.json");
9904
10025
  function readExisting() {
9905
10026
  if (!existsSync19(ALIAS_DOMAINS_PATH)) return /* @__PURE__ */ new Set();
9906
10027
  try {
9907
- const parsed = JSON.parse(readFileSync14(ALIAS_DOMAINS_PATH, "utf-8"));
10028
+ const parsed = JSON.parse(readFileSync15(ALIAS_DOMAINS_PATH, "utf-8"));
9908
10029
  if (!Array.isArray(parsed)) return /* @__PURE__ */ new Set();
9909
10030
  return new Set(parsed.filter((h) => typeof h === "string"));
9910
10031
  } catch {
@@ -9919,14 +10040,131 @@ function addAliasDomain(hostname2) {
9919
10040
  writeFileSync9(ALIAS_DOMAINS_PATH, JSON.stringify([...existing], null, 2) + "\n", "utf-8");
9920
10041
  }
9921
10042
 
10043
+ // app/lib/action-relay-queue.ts
10044
+ import {
10045
+ existsSync as existsSync20,
10046
+ mkdirSync as mkdirSync9,
10047
+ readdirSync as readdirSync6,
10048
+ readFileSync as readFileSync16,
10049
+ unlinkSync as unlinkSync2,
10050
+ writeFileSync as writeFileSync10
10051
+ } from "fs";
10052
+ import { join as join9 } from "path";
10053
+ var TAG19 = "[action-relay-queue]";
10054
+ var FILE_PREFIX = "action-completion-relay-";
10055
+ var FILE_SUFFIX = ".json";
10056
+ function queueDir(accountDir) {
10057
+ return join9(accountDir, "queue");
10058
+ }
10059
+ function relayFilePath(accountDir, actionId) {
10060
+ return join9(queueDir(accountDir), `${FILE_PREFIX}${actionId}${FILE_SUFFIX}`);
10061
+ }
10062
+ function enqueueActionCompletionRelay(opts) {
10063
+ const dir = queueDir(opts.accountDir);
10064
+ mkdirSync9(dir, { recursive: true });
10065
+ const filePath = relayFilePath(opts.accountDir, opts.actionId);
10066
+ const record = {
10067
+ actionId: opts.actionId,
10068
+ conversationId: opts.conversationId,
10069
+ message: opts.message,
10070
+ queuedAt: (/* @__PURE__ */ new Date()).toISOString()
10071
+ };
10072
+ const body = JSON.stringify(record);
10073
+ try {
10074
+ writeFileSync10(filePath, body, { flag: "wx", encoding: "utf-8" });
10075
+ console.log(
10076
+ `${TAG19} phase=enqueued actionId=${opts.actionId} conversationId=${opts.conversationId} path=${filePath} bytes=${Buffer.byteLength(body, "utf-8")}`
10077
+ );
10078
+ return { enqueued: true, path: filePath };
10079
+ } catch (e) {
10080
+ if (e.code === "EEXIST") {
10081
+ console.log(
10082
+ `${TAG19} phase=enqueue-skipped actionId=${opts.actionId} reason=already-queued`
10083
+ );
10084
+ return { enqueued: false, reason: "already-queued", path: filePath };
10085
+ }
10086
+ throw e;
10087
+ }
10088
+ }
10089
+ function consumeActionCompletionRelays(accountDir) {
10090
+ const dir = queueDir(accountDir);
10091
+ if (!existsSync20(dir)) return [];
10092
+ let entries;
10093
+ try {
10094
+ entries = readdirSync6(dir);
10095
+ } catch (e) {
10096
+ console.error(
10097
+ `${TAG19} phase=readdir-failed dir=${dir} error=${e instanceof Error ? e.message : String(e)}`
10098
+ );
10099
+ return [];
10100
+ }
10101
+ const records = [];
10102
+ for (const name of entries) {
10103
+ if (!name.startsWith(FILE_PREFIX) || !name.endsWith(FILE_SUFFIX)) continue;
10104
+ const filePath = join9(dir, name);
10105
+ let raw;
10106
+ try {
10107
+ raw = readFileSync16(filePath, "utf-8");
10108
+ } catch (e) {
10109
+ console.error(
10110
+ `${TAG19} phase=read-failed file=${name} error=${e instanceof Error ? e.message : String(e)}`
10111
+ );
10112
+ continue;
10113
+ }
10114
+ let parsed;
10115
+ try {
10116
+ parsed = JSON.parse(raw);
10117
+ } catch (e) {
10118
+ console.error(
10119
+ `${TAG19} phase=parse-failed file=${name} error=${e instanceof Error ? e.message : String(e)}`
10120
+ );
10121
+ continue;
10122
+ }
10123
+ if (!isActionCompletionRelay(parsed)) {
10124
+ console.error(
10125
+ `${TAG19} phase=shape-invalid file=${name} keys=${parsed && typeof parsed === "object" ? Object.keys(parsed).join(",") : "non-object"}`
10126
+ );
10127
+ continue;
10128
+ }
10129
+ records.push({ rec: parsed, filePath });
10130
+ }
10131
+ records.sort((a, b) => a.rec.queuedAt.localeCompare(b.rec.queuedAt));
10132
+ const out = [];
10133
+ const now = Date.now();
10134
+ for (const { rec, filePath } of records) {
10135
+ const queuedAtMs = Date.parse(rec.queuedAt);
10136
+ const ageMs = Number.isFinite(queuedAtMs) ? now - queuedAtMs : 0;
10137
+ out.push({ ...rec, ageMs, filePath });
10138
+ }
10139
+ return out;
10140
+ }
10141
+ function deleteConsumedRelay(filePath) {
10142
+ try {
10143
+ unlinkSync2(filePath);
10144
+ } catch (e) {
10145
+ const code = e.code;
10146
+ if (code !== "ENOENT") {
10147
+ console.error(
10148
+ `${TAG19} phase=unlink-failed file=${filePath} error=${e instanceof Error ? e.message : String(e)}`
10149
+ );
10150
+ }
10151
+ }
10152
+ }
10153
+ function isActionCompletionRelay(value) {
10154
+ if (!value || typeof value !== "object") return false;
10155
+ const v = value;
10156
+ return typeof v.actionId === "string" && typeof v.conversationId === "string" && typeof v.message === "string" && typeof v.queuedAt === "string";
10157
+ }
10158
+
9922
10159
  // server/routes/admin/cloudflare.ts
10160
+ import { existsSync as existsSyncFs, readFileSync as readFileSyncFs } from "fs";
9923
10161
  var SETUP_TIMEOUT_MS = 10 * 60 * 1e3;
9924
10162
  var DOMAINS_TIMEOUT_MS = 40 * 1e3;
9925
10163
  function loadBrandInfo() {
9926
- const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve17(process.cwd(), "..");
9927
- const brandPath = resolve17(platformRoot2, "config", "brand.json");
10164
+ const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve18(process.cwd(), "..");
10165
+ const brandPath = resolve18(platformRoot2, "config", "brand.json");
9928
10166
  try {
9929
- const parsed = JSON.parse(readFileSync15(brandPath, "utf-8"));
10167
+ const parsed = JSON.parse(readFileSync17(brandPath, "utf-8"));
9930
10168
  const hostname2 = typeof parsed.hostname === "string" && parsed.hostname ? parsed.hostname : "maxy";
9931
10169
  const configDir2 = typeof parsed.configDir === "string" && parsed.configDir ? parsed.configDir : ".maxy";
9932
10170
  return { hostname: hostname2, configDir: configDir2 };
@@ -10029,7 +10267,7 @@ app21.get("/domains", requireAdminSession, async (c) => {
10029
10267
  streamLogPath = streamLogPathFor(accountId, correlationId).streamLogPath;
10030
10268
  log(`phase=stream-log-resolved path=${streamLogPath}`);
10031
10269
  const brand = loadBrandInfo();
10032
- const scriptPath = resolve17(homedir(), "list-cf-domains.sh");
10270
+ const scriptPath = resolve18(homedir(), "list-cf-domains.sh");
10033
10271
  const result = await runFormSpawn({
10034
10272
  scriptPath,
10035
10273
  args: [brand.hostname],
@@ -10222,23 +10460,89 @@ actionId: ${actionId}`,
10222
10460
  };
10223
10461
  return ok(success);
10224
10462
  });
10463
+ var RELAY_MAX_BODY = 8 * 1024;
10464
+ var RELAY_MAX_MESSAGE = 2048;
10465
+ var ACTION_ID_RE = /^[a-z0-9-]+$/;
10466
+ app21.post("/relay-completion", requireAdminSession, async (c) => {
10467
+ const sessionKey = c.var.sessionKey;
10468
+ let raw;
10469
+ try {
10470
+ raw = await c.req.text();
10471
+ } catch {
10472
+ return c.json({ ok: false, reason: "invalid-body" }, 400);
10473
+ }
10474
+ if (Buffer.byteLength(raw, "utf-8") > RELAY_MAX_BODY) {
10475
+ return c.json({ ok: false, reason: "body-too-large" }, 413);
10476
+ }
10477
+ let body;
10478
+ try {
10479
+ body = JSON.parse(raw);
10480
+ } catch {
10481
+ return c.json({ ok: false, reason: "invalid-json" }, 400);
10482
+ }
10483
+ if (!body || typeof body !== "object") {
10484
+ return c.json({ ok: false, reason: "invalid-body" }, 400);
10485
+ }
10486
+ const { actionId, message } = body;
10487
+ if (typeof actionId !== "string" || !ACTION_ID_RE.test(actionId) || actionId.length > 200) {
10488
+ console.error(`[cloudflare-relay-completion] phase=enqueue-skipped reason=invalid-actionid sessionKey=${sessionKey.slice(-8)}`);
10489
+ return c.json({ ok: false, reason: "invalid-actionid" }, 400);
10490
+ }
10491
+ if (typeof message !== "string" || message.length === 0 || message.length > RELAY_MAX_MESSAGE) {
10492
+ return c.json({ ok: false, reason: "invalid-message" }, 400);
10493
+ }
10494
+ const conversationId = getConversationIdForSession(sessionKey);
10495
+ if (!conversationId) {
10496
+ console.error(`[cloudflare-relay-completion] phase=enqueue-skipped reason=missing-conversation-id sessionKey=${sessionKey.slice(-8)}`);
10497
+ return c.json({ ok: false, reason: "missing-conversation-id" }, 400);
10498
+ }
10499
+ const account = resolveAccount();
10500
+ if (!account) {
10501
+ console.error(`[cloudflare-relay-completion] phase=enqueue-skipped reason=missing-account-dir actionId=${actionId}`);
10502
+ return c.json({ ok: false, reason: "missing-account-dir" }, 500);
10503
+ }
10504
+ const logPath2 = actionLogPath(actionId);
10505
+ let auditOutcome = "log-absent";
10506
+ if (existsSyncFs(logPath2)) {
10507
+ try {
10508
+ const lines = readFileSyncFs(logPath2, "utf-8").split("\n");
10509
+ const reconciled = reconcileCloudflareSetupFromLog(lines);
10510
+ auditOutcome = reconciled ? reconciled.kind : "log-incomplete";
10511
+ } catch (e) {
10512
+ auditOutcome = `read-failed:${e instanceof Error ? e.message : String(e)}`;
10513
+ }
10514
+ }
10515
+ console.log(`[cloudflare-relay-completion] phase=enqueue-attempt actionId=${actionId} auditOutcome=${auditOutcome} conversationId=${conversationId}`);
10516
+ try {
10517
+ enqueueActionCompletionRelay({
10518
+ accountDir: account.accountDir,
10519
+ actionId,
10520
+ conversationId,
10521
+ message
10522
+ });
10523
+ } catch (e) {
10524
+ console.error(`[cloudflare-relay-completion] phase=enqueue-failed actionId=${actionId} error=${e instanceof Error ? e.message : String(e)}`);
10525
+ return c.json({ ok: false, reason: "enqueue-failed" }, 500);
10526
+ }
10527
+ return c.body(null, 204);
10528
+ });
10225
10529
  var cloudflare_default = app21;
10226
10530
 
10227
10531
  // server/routes/admin/files.ts
10228
10532
  import { createReadStream as createReadStream3 } from "fs";
10229
10533
  import { readdir as readdir2, readFile as readFile4, stat as stat4, mkdir as mkdir3, writeFile as writeFile4, unlink as unlink2 } from "fs/promises";
10230
10534
  import { realpathSync as realpathSync4 } from "fs";
10231
- import { basename as basename6, dirname as dirname8, join as join9, resolve as resolve19, sep as sep2 } from "path";
10535
+ import { basename as basename6, dirname as dirname8, join as join10, resolve as resolve20, sep as sep2 } from "path";
10232
10536
  import { Readable as Readable2 } from "stream";
10233
10537
 
10234
10538
  // app/lib/data-path.ts
10235
10539
  import { realpathSync as realpathSync3 } from "fs";
10236
- import { resolve as resolve18, normalize, sep, relative } from "path";
10237
- var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve18(process.cwd(), "../platform");
10238
- var DATA_ROOT = resolve18(PLATFORM_ROOT6, "..", "data");
10540
+ import { resolve as resolve19, normalize, sep, relative } from "path";
10541
+ var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve19(process.cwd(), "../platform");
10542
+ var DATA_ROOT = resolve19(PLATFORM_ROOT6, "..", "data");
10239
10543
  function resolveDataPath(raw) {
10240
10544
  const cleaned = normalize("/" + (raw ?? "").replace(/\\/g, "/")).replace(/^\/+/, "");
10241
- const absolute = resolve18(DATA_ROOT, cleaned);
10545
+ const absolute = resolve19(DATA_ROOT, cleaned);
10242
10546
  let dataRootReal;
10243
10547
  try {
10244
10548
  dataRootReal = realpathSync3(DATA_ROOT);
@@ -10503,7 +10807,7 @@ async function restoreNode(params) {
10503
10807
  }
10504
10808
 
10505
10809
  // app/lib/file-delete-cascade.ts
10506
- var UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
10810
+ var UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
10507
10811
  function parseAttachmentPath(relPath2) {
10508
10812
  const segments = relPath2.split("/").filter(Boolean);
10509
10813
  if (segments.length !== 4) return null;
@@ -10511,7 +10815,7 @@ function parseAttachmentPath(relPath2) {
10511
10815
  const accountId = segments[1];
10512
10816
  const attachmentId = segments[2];
10513
10817
  const filename = segments[3];
10514
- if (!UUID_RE2.test(accountId) || !UUID_RE2.test(attachmentId)) return null;
10818
+ if (!UUID_RE3.test(accountId) || !UUID_RE3.test(attachmentId)) return null;
10515
10819
  const dot = filename.lastIndexOf(".");
10516
10820
  if (dot === -1) return null;
10517
10821
  const stem = filename.slice(0, dot);
@@ -10583,10 +10887,10 @@ async function cascadeDeleteDocument(params) {
10583
10887
  }
10584
10888
 
10585
10889
  // server/routes/admin/files.ts
10586
- var UUID_RE3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
10890
+ var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
10587
10891
  async function readMeta(absDir, baseName) {
10588
10892
  try {
10589
- const raw = await readFile4(join9(absDir, `${baseName}.meta.json`), "utf8");
10893
+ const raw = await readFile4(join10(absDir, `${baseName}.meta.json`), "utf8");
10590
10894
  const parsed = JSON.parse(raw);
10591
10895
  if (typeof parsed?.filename === "string") {
10592
10896
  return { filename: parsed.filename, mimeType: typeof parsed.mimeType === "string" ? parsed.mimeType : void 0 };
@@ -10597,7 +10901,7 @@ async function readMeta(absDir, baseName) {
10597
10901
  }
10598
10902
  async function readAccountNames() {
10599
10903
  const map = /* @__PURE__ */ new Map();
10600
- const accountsDir = resolve19(DATA_ROOT, "accounts");
10904
+ const accountsDir = resolve20(DATA_ROOT, "accounts");
10601
10905
  let names;
10602
10906
  try {
10603
10907
  names = await readdir2(accountsDir);
@@ -10605,8 +10909,8 @@ async function readAccountNames() {
10605
10909
  return map;
10606
10910
  }
10607
10911
  for (const name of names) {
10608
- if (!UUID_RE3.test(name)) continue;
10609
- const configPath2 = resolve19(accountsDir, name, "account.json");
10912
+ if (!UUID_RE4.test(name)) continue;
10913
+ const configPath2 = resolve20(accountsDir, name, "account.json");
10610
10914
  try {
10611
10915
  const raw = await readFile4(configPath2, "utf8");
10612
10916
  const parsed = JSON.parse(raw);
@@ -10623,8 +10927,8 @@ async function readAccountNames() {
10623
10927
  return map;
10624
10928
  }
10625
10929
  async function enrich(absolute, entry, accountNames) {
10626
- if (entry.kind === "directory" && UUID_RE3.test(entry.name)) {
10627
- const meta = await readMeta(join9(absolute, entry.name), entry.name);
10930
+ if (entry.kind === "directory" && UUID_RE4.test(entry.name)) {
10931
+ const meta = await readMeta(join10(absolute, entry.name), entry.name);
10628
10932
  if (meta?.filename) {
10629
10933
  entry.displayName = meta.filename;
10630
10934
  entry.mimeType = meta.mimeType;
@@ -10639,7 +10943,7 @@ async function enrich(absolute, entry, accountNames) {
10639
10943
  if (entry.kind === "file") {
10640
10944
  const dot = entry.name.lastIndexOf(".");
10641
10945
  const base = dot === -1 ? entry.name : entry.name.slice(0, dot);
10642
- if (UUID_RE3.test(base)) {
10946
+ if (UUID_RE4.test(base)) {
10643
10947
  const meta = await readMeta(absolute, base);
10644
10948
  if (meta?.filename) {
10645
10949
  entry.displayName = meta.filename;
@@ -10651,7 +10955,7 @@ async function enrich(absolute, entry, accountNames) {
10651
10955
  function buildDisplayPath(relPath2, accountNames) {
10652
10956
  if (relPath2 === "." || relPath2 === "") return [];
10653
10957
  return relPath2.split("/").filter(Boolean).map((seg) => {
10654
- const dn = UUID_RE3.test(seg) ? accountNames.get(seg) : void 0;
10958
+ const dn = UUID_RE4.test(seg) ? accountNames.get(seg) : void 0;
10655
10959
  return dn ? { name: seg, displayName: dn } : { name: seg };
10656
10960
  });
10657
10961
  }
@@ -10679,11 +10983,11 @@ app22.get("/", requireAdminSession, async (c) => {
10679
10983
  const names = await readdir2(absolute);
10680
10984
  const entries = [];
10681
10985
  for (const name of names) {
10682
- if (UUID_RE3.test(name.replace(/\.meta\.json$/, "")) && name.endsWith(".meta.json")) {
10986
+ if (UUID_RE4.test(name.replace(/\.meta\.json$/, "")) && name.endsWith(".meta.json")) {
10683
10987
  continue;
10684
10988
  }
10685
10989
  try {
10686
- const entryPath = join9(absolute, name);
10990
+ const entryPath = join10(absolute, name);
10687
10991
  const s = await stat4(entryPath);
10688
10992
  entries.push({
10689
10993
  name,
@@ -10797,8 +11101,8 @@ app22.post("/upload", requireAdminSession, async (c) => {
10797
11101
  }
10798
11102
  const safeName = basename6(file.name).replace(/[\0/\\]/g, "_");
10799
11103
  const finalName = `${Date.now()}-${safeName}`;
10800
- const destDir = resolve19(DATA_ROOT, "uploads", accountId);
10801
- const destPath = resolve19(destDir, finalName);
11104
+ const destDir = resolve20(DATA_ROOT, "uploads", accountId);
11105
+ const destPath = resolve20(destDir, finalName);
10802
11106
  try {
10803
11107
  await mkdir3(destDir, { recursive: true });
10804
11108
  const dataRootReal = realpathSync4(DATA_ROOT);
@@ -10856,7 +11160,7 @@ app22.delete("/", requireAdminSession, async (c) => {
10856
11160
  }
10857
11161
  const dot = base.lastIndexOf(".");
10858
11162
  const stem = dot === -1 ? base : base.slice(0, dot);
10859
- const sidecarPath = UUID_RE3.test(stem) && base !== `${stem}.meta.json` ? join9(dirname8(absolute), `${stem}.meta.json`) : null;
11163
+ const sidecarPath = UUID_RE4.test(stem) && base !== `${stem}.meta.json` ? join10(dirname8(absolute), `${stem}.meta.json`) : null;
10860
11164
  await unlink2(absolute);
10861
11165
  if (sidecarPath) {
10862
11166
  try {
@@ -12313,8 +12617,8 @@ var adherence_default = app30;
12313
12617
  // server/routes/admin/sidebar-artefacts.ts
12314
12618
  import neo4j3 from "neo4j-driver";
12315
12619
  import { readFile as readFile5, readdir as readdir3, stat as stat5 } from "fs/promises";
12316
- import { resolve as resolve20, relative as relative2, isAbsolute } from "path";
12317
- import { existsSync as existsSync20 } from "fs";
12620
+ import { resolve as resolve21, relative as relative2, isAbsolute } from "path";
12621
+ import { existsSync as existsSync21 } from "fs";
12318
12622
  var LIMIT = 50;
12319
12623
  var TEXT_MIME_PREFIXES = ["text/", "application/json", "application/markdown"];
12320
12624
  var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
@@ -12330,7 +12634,7 @@ app31.get("/", requireAdminSession, async (c) => {
12330
12634
  if (docs === null) {
12331
12635
  return c.json({ error: "Failed to load artefacts" }, 500);
12332
12636
  }
12333
- const accountDir = resolve20(ACCOUNTS_DIR, accountId);
12637
+ const accountDir = resolve21(ACCOUNTS_DIR, accountId);
12334
12638
  const agents = await fetchAgentTemplateRows(accountDir);
12335
12639
  const artefacts = [...docs, ...agents].sort(
12336
12640
  (a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? "")
@@ -12393,8 +12697,8 @@ async function readArtefactContent(accountId, attachmentId, mimeType, displayNam
12393
12697
  logSkip(displayName, "non-text-mime", mimeType);
12394
12698
  return { content: "", skipReason: "non-text-mime" };
12395
12699
  }
12396
- const accountDir = resolve20(ATTACHMENTS_ROOT, accountId);
12397
- const dir = resolve20(accountDir, attachmentId);
12700
+ const accountDir = resolve21(ATTACHMENTS_ROOT, accountId);
12701
+ const dir = resolve21(accountDir, attachmentId);
12398
12702
  try {
12399
12703
  validateFilePathInAccount(dir, accountDir);
12400
12704
  } catch {
@@ -12408,7 +12712,7 @@ async function readArtefactContent(accountId, attachmentId, mimeType, displayNam
12408
12712
  logSkip(displayName, "missing-on-disk", mimeType);
12409
12713
  return { content: "", skipReason: "missing-on-disk" };
12410
12714
  }
12411
- return { content: await readFile5(resolve20(dir, dataFile), "utf-8"), skipReason: null };
12715
+ return { content: await readFile5(resolve21(dir, dataFile), "utf-8"), skipReason: null };
12412
12716
  } catch (err) {
12413
12717
  const message = err instanceof Error ? err.message : String(err);
12414
12718
  console.error(`[admin/sidebar-artefacts] read-failed attachmentId=${attachmentId.slice(0, 8)} error="${message}"`);
@@ -12424,8 +12728,8 @@ function logSkip(name, reason, mimeType) {
12424
12728
  async function fetchAgentTemplateRows(accountDir) {
12425
12729
  const rows = [];
12426
12730
  for (const filename of ADMIN_AGENT_FILES) {
12427
- const overridePath = resolve20(accountDir, "agents", "admin", filename);
12428
- const bundledPath = resolve20(PLATFORM_ROOT, "templates", "agents", "admin", filename);
12731
+ const overridePath = resolve21(accountDir, "agents", "admin", filename);
12732
+ const bundledPath = resolve21(PLATFORM_ROOT, "templates", "agents", "admin", filename);
12429
12733
  const labelStem = filename.replace(/\.md$/, "");
12430
12734
  const row = await readAgentTemplateRow({
12431
12735
  id: `agent-template:admin:${filename}`,
@@ -12439,12 +12743,12 @@ async function fetchAgentTemplateRows(accountDir) {
12439
12743
  });
12440
12744
  if (row) rows.push(row);
12441
12745
  }
12442
- const overrideDir = resolve20(accountDir, "specialists", "agents");
12443
- const bundledDir = resolve20(PLATFORM_ROOT, "templates", "specialists", "agents");
12746
+ const overrideDir = resolve21(accountDir, "specialists", "agents");
12747
+ const bundledDir = resolve21(PLATFORM_ROOT, "templates", "specialists", "agents");
12444
12748
  const specialistNames = await unionSpecialistFilenames(overrideDir, bundledDir);
12445
12749
  for (const filename of specialistNames) {
12446
- const overridePath = resolve20(overrideDir, filename);
12447
- const bundledPath = resolve20(bundledDir, filename);
12750
+ const overridePath = resolve21(overrideDir, filename);
12751
+ const bundledPath = resolve21(bundledDir, filename);
12448
12752
  const row = await readAgentTemplateRow({
12449
12753
  id: `agent-template:specialist:${filename}`,
12450
12754
  displayName: filename.replace(/\.md$/, ""),
@@ -12462,7 +12766,7 @@ async function fetchAgentTemplateRows(accountDir) {
12462
12766
  async function unionSpecialistFilenames(overrideDir, bundledDir) {
12463
12767
  const names = /* @__PURE__ */ new Set();
12464
12768
  for (const dir of [overrideDir, bundledDir]) {
12465
- if (!existsSync20(dir)) continue;
12769
+ if (!existsSync21(dir)) continue;
12466
12770
  try {
12467
12771
  const entries = await readdir3(dir);
12468
12772
  for (const entry of entries) {
@@ -12477,7 +12781,7 @@ async function unionSpecialistFilenames(overrideDir, bundledDir) {
12477
12781
  }
12478
12782
  async function readAgentTemplateRow(inp) {
12479
12783
  let chosenPath = null;
12480
- if (existsSync20(inp.overridePath)) {
12784
+ if (existsSync21(inp.overridePath)) {
12481
12785
  try {
12482
12786
  validateFilePathInAccount(inp.overridePath, inp.overrideRoot);
12483
12787
  chosenPath = inp.overridePath;
@@ -12488,7 +12792,7 @@ async function readAgentTemplateRow(inp) {
12488
12792
  );
12489
12793
  return null;
12490
12794
  }
12491
- } else if (existsSync20(inp.bundledPath)) {
12795
+ } else if (existsSync21(inp.bundledPath)) {
12492
12796
  if (!isWithin(inp.bundledPath, inp.bundledRoot)) {
12493
12797
  console.error(
12494
12798
  `[admin/sidebar-artefacts] agent-template-read-failed agent=${inp.displayName} kind=${inp.logName} error="bundled path outside PLATFORM_ROOT"`
@@ -12529,10 +12833,10 @@ var sidebar_artefacts_default = app31;
12529
12833
 
12530
12834
  // server/routes/admin/sidebar-artefact-save.ts
12531
12835
  import { mkdir as mkdir4, readdir as readdir4, stat as stat6, writeFile as writeFile5 } from "fs/promises";
12532
- import { resolve as resolve21 } from "path";
12533
- import { existsSync as existsSync21 } from "fs";
12836
+ import { resolve as resolve22 } from "path";
12837
+ import { existsSync as existsSync22 } from "fs";
12534
12838
  var ADMIN_AGENT_FILES2 = /* @__PURE__ */ new Set(["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"]);
12535
- var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
12839
+ var UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
12536
12840
  var app32 = new Hono();
12537
12841
  app32.post("/", requireAdminSession, async (c) => {
12538
12842
  const sessionKey = c.var.sessionKey;
@@ -12542,7 +12846,7 @@ app32.post("/", requireAdminSession, async (c) => {
12542
12846
  if (!body || typeof body.id !== "string" || typeof body.content !== "string") {
12543
12847
  return c.json({ error: "id and content required" }, 400);
12544
12848
  }
12545
- const accountDir = resolve21(ACCOUNTS_DIR, accountId);
12849
+ const accountDir = resolve22(ACCOUNTS_DIR, accountId);
12546
12850
  const resolved = await resolveSavePath(body.id, accountId, accountDir);
12547
12851
  if (resolved.kind === "reject") {
12548
12852
  console.error(
@@ -12583,22 +12887,22 @@ async function resolveSavePath(id, accountId, accountDir) {
12583
12887
  if (role !== "admin" || !ADMIN_AGENT_FILES2.has(filename)) {
12584
12888
  return { kind: "reject", status: 400, reason: "invalid-id" };
12585
12889
  }
12586
- const parent = resolve21(accountDir, "agents", "admin");
12890
+ const parent = resolve22(accountDir, "agents", "admin");
12587
12891
  await mkdir4(parent, { recursive: true });
12588
12892
  try {
12589
12893
  validateFilePathInAccount(parent, accountDir);
12590
12894
  } catch {
12591
12895
  return { kind: "reject", status: 400, reason: "containment-rejected" };
12592
12896
  }
12593
- return { kind: "admin-template", path: resolve21(parent, filename) };
12897
+ return { kind: "admin-template", path: resolve22(parent, filename) };
12594
12898
  }
12595
- if (UUID_RE4.test(id)) {
12596
- const dir = resolve21(ATTACHMENTS_ROOT, accountId, id);
12597
- if (!existsSync21(dir)) {
12899
+ if (UUID_RE5.test(id)) {
12900
+ const dir = resolve22(ATTACHMENTS_ROOT, accountId, id);
12901
+ if (!existsSync22(dir)) {
12598
12902
  return { kind: "reject", status: 400, reason: "not-found" };
12599
12903
  }
12600
12904
  try {
12601
- validateFilePathInAccount(dir, resolve21(ATTACHMENTS_ROOT, accountId));
12905
+ validateFilePathInAccount(dir, resolve22(ATTACHMENTS_ROOT, accountId));
12602
12906
  } catch {
12603
12907
  return { kind: "reject", status: 400, reason: "containment-rejected" };
12604
12908
  }
@@ -12607,7 +12911,7 @@ async function resolveSavePath(id, accountId, accountDir) {
12607
12911
  if (!dataFile) {
12608
12912
  return { kind: "reject", status: 400, reason: "not-found" };
12609
12913
  }
12610
- return { kind: "knowledge-doc", path: resolve21(dir, dataFile) };
12914
+ return { kind: "knowledge-doc", path: resolve22(dir, dataFile) };
12611
12915
  }
12612
12916
  return { kind: "reject", status: 400, reason: "invalid-id" };
12613
12917
  }
@@ -12618,27 +12922,27 @@ var sidebar_artefact_save_default = app32;
12618
12922
 
12619
12923
  // server/routes/admin/sidebar-artefact-content.ts
12620
12924
  import { readFile as readFile6, readdir as readdir5 } from "fs/promises";
12621
- import { existsSync as existsSync22 } from "fs";
12622
- import { resolve as resolve22 } from "path";
12623
- var UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
12925
+ import { existsSync as existsSync23 } from "fs";
12926
+ import { resolve as resolve23 } from "path";
12927
+ var UUID_RE6 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
12624
12928
  var app33 = new Hono();
12625
12929
  app33.get("/", requireAdminSession, async (c) => {
12626
12930
  const sessionKey = c.var.sessionKey;
12627
12931
  const accountId = getAccountIdForSession(sessionKey);
12628
12932
  if (!accountId) return new Response("Unauthorized", { status: 401 });
12629
12933
  const id = c.req.query("id") ?? "";
12630
- if (!UUID_RE5.test(id)) {
12934
+ if (!UUID_RE6.test(id)) {
12631
12935
  console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
12632
12936
  return new Response("Not found", { status: 404 });
12633
12937
  }
12634
- const dir = resolve22(ATTACHMENTS_ROOT, accountId, id);
12635
- if (!existsSync22(dir)) {
12938
+ const dir = resolve23(ATTACHMENTS_ROOT, accountId, id);
12939
+ if (!existsSync23(dir)) {
12636
12940
  console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
12637
12941
  return new Response("Not found", { status: 404 });
12638
12942
  }
12639
12943
  let meta;
12640
12944
  try {
12641
- meta = JSON.parse(await readFile6(resolve22(dir, `${id}.meta.json`), "utf-8"));
12945
+ meta = JSON.parse(await readFile6(resolve23(dir, `${id}.meta.json`), "utf-8"));
12642
12946
  } catch {
12643
12947
  console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
12644
12948
  return new Response("Not found", { status: 404 });
@@ -12650,7 +12954,7 @@ app33.get("/", requireAdminSession, async (c) => {
12650
12954
  return new Response("Not found", { status: 404 });
12651
12955
  }
12652
12956
  const start = Date.now();
12653
- const buffer = await readFile6(resolve22(dir, dataFile));
12957
+ const buffer = await readFile6(resolve23(dir, dataFile));
12654
12958
  const ms = Date.now() - start;
12655
12959
  console.log(
12656
12960
  `[admin/sidebar-artefact-content] account=${accountId} id=${id.slice(0, 8)} mime=${meta.mimeType} bytes=${buffer.length} ms=${ms}`
@@ -12694,8 +12998,8 @@ app34.route("/sidebar-artefact-content", sidebar_artefact_content_default);
12694
12998
  var admin_default = app34;
12695
12999
 
12696
13000
  // server/routes/sites.ts
12697
- import { existsSync as existsSync23, readFileSync as readFileSync16, realpathSync as realpathSync5, statSync as statSync8 } from "fs";
12698
- import { resolve as resolve23 } from "path";
13001
+ import { existsSync as existsSync24, readFileSync as readFileSync18, realpathSync as realpathSync5, statSync as statSync8 } from "fs";
13002
+ import { resolve as resolve24 } from "path";
12699
13003
  var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
12700
13004
  var MIME = {
12701
13005
  ".html": "text/html; charset=utf-8",
@@ -12753,28 +13057,28 @@ app35.get("/:rel{.*}", (c) => {
12753
13057
  }
12754
13058
  segments.push(seg);
12755
13059
  }
12756
- const rootDir = resolve23(account.accountDir, "sites");
12757
- let filePath = segments.length === 0 ? rootDir : resolve23(rootDir, ...segments);
13060
+ const rootDir = resolve24(account.accountDir, "sites");
13061
+ let filePath = segments.length === 0 ? rootDir : resolve24(rootDir, ...segments);
12758
13062
  if (filePath !== rootDir && !filePath.startsWith(rootDir + "/")) {
12759
13063
  console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
12760
13064
  return c.text("Forbidden", 403);
12761
13065
  }
12762
13066
  let stat7;
12763
13067
  try {
12764
- stat7 = existsSync23(filePath) ? statSync8(filePath) : null;
13068
+ stat7 = existsSync24(filePath) ? statSync8(filePath) : null;
12765
13069
  } catch {
12766
13070
  stat7 = null;
12767
13071
  }
12768
13072
  if (stat7?.isDirectory()) {
12769
- filePath = resolve23(filePath, "index.html");
13073
+ filePath = resolve24(filePath, "index.html");
12770
13074
  } else if (stat7 === null && isDirRequest) {
12771
- filePath = resolve23(filePath, "index.html");
13075
+ filePath = resolve24(filePath, "index.html");
12772
13076
  }
12773
13077
  if (!filePath.startsWith(rootDir + "/")) {
12774
13078
  console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
12775
13079
  return c.text("Forbidden", 403);
12776
13080
  }
12777
- if (!existsSync23(filePath)) {
13081
+ if (!existsSync24(filePath)) {
12778
13082
  console.error(`[sites] not-found path=${reqPath} status=404`);
12779
13083
  return c.text("Not found", 404);
12780
13084
  }
@@ -12793,7 +13097,7 @@ app35.get("/:rel{.*}", (c) => {
12793
13097
  }
12794
13098
  let body;
12795
13099
  try {
12796
- body = readFileSync16(realPath);
13100
+ body = readFileSync18(realPath);
12797
13101
  } catch (err) {
12798
13102
  const code = err?.code;
12799
13103
  if (code === "EISDIR") {
@@ -12925,14 +13229,14 @@ function clientFrom(c) {
12925
13229
  );
12926
13230
  }
12927
13231
  var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT || "";
12928
- var BRAND_JSON_PATH = PLATFORM_ROOT7 ? join10(PLATFORM_ROOT7, "config", "brand.json") : "";
13232
+ var BRAND_JSON_PATH = PLATFORM_ROOT7 ? join11(PLATFORM_ROOT7, "config", "brand.json") : "";
12929
13233
  var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", domain: "getmaxy.com" };
12930
- if (BRAND_JSON_PATH && !existsSync24(BRAND_JSON_PATH)) {
13234
+ if (BRAND_JSON_PATH && !existsSync25(BRAND_JSON_PATH)) {
12931
13235
  console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
12932
13236
  }
12933
- if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
13237
+ if (BRAND_JSON_PATH && existsSync25(BRAND_JSON_PATH)) {
12934
13238
  try {
12935
- const parsed = JSON.parse(readFileSync17(BRAND_JSON_PATH, "utf-8"));
13239
+ const parsed = JSON.parse(readFileSync19(BRAND_JSON_PATH, "utf-8"));
12936
13240
  BRAND = { ...BRAND, ...parsed };
12937
13241
  } catch (err) {
12938
13242
  console.error(`[brand] Failed to parse brand.json: ${err.message}`);
@@ -12951,11 +13255,11 @@ var brandLoginOpts = {
12951
13255
  bodyFont: BRAND.defaultFonts?.body,
12952
13256
  logoContainsName: !!BRAND.logoContainsName
12953
13257
  };
12954
- var ALIAS_DOMAINS_PATH2 = join10(homedir2(), BRAND.configDir, "alias-domains.json");
13258
+ var ALIAS_DOMAINS_PATH2 = join11(homedir2(), BRAND.configDir, "alias-domains.json");
12955
13259
  function loadAliasDomains() {
12956
13260
  try {
12957
- if (!existsSync24(ALIAS_DOMAINS_PATH2)) return null;
12958
- const parsed = JSON.parse(readFileSync17(ALIAS_DOMAINS_PATH2, "utf-8"));
13261
+ if (!existsSync25(ALIAS_DOMAINS_PATH2)) return null;
13262
+ const parsed = JSON.parse(readFileSync19(ALIAS_DOMAINS_PATH2, "utf-8"));
12959
13263
  if (!Array.isArray(parsed)) {
12960
13264
  console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
12961
13265
  return null;
@@ -13295,20 +13599,20 @@ app36.get("/agent-assets/:slug/:filename", (c) => {
13295
13599
  console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
13296
13600
  return c.text("Not found", 404);
13297
13601
  }
13298
- const filePath = resolve24(account.accountDir, "agents", slug, "assets", filename);
13299
- const expectedDir = resolve24(account.accountDir, "agents", slug, "assets");
13602
+ const filePath = resolve25(account.accountDir, "agents", slug, "assets", filename);
13603
+ const expectedDir = resolve25(account.accountDir, "agents", slug, "assets");
13300
13604
  if (!filePath.startsWith(expectedDir + "/")) {
13301
13605
  console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
13302
13606
  return c.text("Forbidden", 403);
13303
13607
  }
13304
- if (!existsSync24(filePath)) {
13608
+ if (!existsSync25(filePath)) {
13305
13609
  console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
13306
13610
  return c.text("Not found", 404);
13307
13611
  }
13308
13612
  const ext = "." + filename.split(".").pop()?.toLowerCase();
13309
13613
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
13310
13614
  console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
13311
- const body = readFileSync17(filePath);
13615
+ const body = readFileSync19(filePath);
13312
13616
  return c.body(body, 200, {
13313
13617
  "Content-Type": contentType,
13314
13618
  "Cache-Control": "public, max-age=3600"
@@ -13325,20 +13629,20 @@ app36.get("/generated/:filename", (c) => {
13325
13629
  console.error(`[generated] serve file=${filename} status=404`);
13326
13630
  return c.text("Not found", 404);
13327
13631
  }
13328
- const filePath = resolve24(account.accountDir, "generated", filename);
13329
- const expectedDir = resolve24(account.accountDir, "generated");
13632
+ const filePath = resolve25(account.accountDir, "generated", filename);
13633
+ const expectedDir = resolve25(account.accountDir, "generated");
13330
13634
  if (!filePath.startsWith(expectedDir + "/")) {
13331
13635
  console.error(`[generated] serve file=${filename} status=403`);
13332
13636
  return c.text("Forbidden", 403);
13333
13637
  }
13334
- if (!existsSync24(filePath)) {
13638
+ if (!existsSync25(filePath)) {
13335
13639
  console.error(`[generated] serve file=${filename} status=404`);
13336
13640
  return c.text("Not found", 404);
13337
13641
  }
13338
13642
  const ext = "." + filename.split(".").pop()?.toLowerCase();
13339
13643
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
13340
13644
  console.log(`[generated] serve file=${filename} status=200`);
13341
- const body = readFileSync17(filePath);
13645
+ const body = readFileSync19(filePath);
13342
13646
  return c.body(body, 200, {
13343
13647
  "Content-Type": contentType,
13344
13648
  "Cache-Control": "public, max-age=86400"
@@ -13348,9 +13652,9 @@ app36.route("/sites", sites_default);
13348
13652
  var htmlCache = /* @__PURE__ */ new Map();
13349
13653
  var brandLogoPath = "/brand/maxy-monochrome.png";
13350
13654
  var brandIconPath = "/brand/maxy-monochrome.png";
13351
- if (BRAND_JSON_PATH && existsSync24(BRAND_JSON_PATH)) {
13655
+ if (BRAND_JSON_PATH && existsSync25(BRAND_JSON_PATH)) {
13352
13656
  try {
13353
- const fullBrand = JSON.parse(readFileSync17(BRAND_JSON_PATH, "utf-8"));
13657
+ const fullBrand = JSON.parse(readFileSync19(BRAND_JSON_PATH, "utf-8"));
13354
13658
  if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
13355
13659
  brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
13356
13660
  } catch {
@@ -13367,9 +13671,9 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
13367
13671
  function readInstalledVersion() {
13368
13672
  try {
13369
13673
  if (!PLATFORM_ROOT7) return "unknown";
13370
- const versionFile = join10(PLATFORM_ROOT7, "config", `.${BRAND.hostname}-version`);
13371
- if (!existsSync24(versionFile)) return "unknown";
13372
- const content = readFileSync17(versionFile, "utf-8").trim();
13674
+ const versionFile = join11(PLATFORM_ROOT7, "config", `.${BRAND.hostname}-version`);
13675
+ if (!existsSync25(versionFile)) return "unknown";
13676
+ const content = readFileSync19(versionFile, "utf-8").trim();
13373
13677
  return content || "unknown";
13374
13678
  } catch {
13375
13679
  return "unknown";
@@ -13410,7 +13714,7 @@ var clientErrorReporterScript = `<script>
13410
13714
  function cachedHtml(file) {
13411
13715
  let html = htmlCache.get(file);
13412
13716
  if (!html) {
13413
- html = readFileSync17(resolve24(process.cwd(), "public", file), "utf-8");
13717
+ html = readFileSync19(resolve25(process.cwd(), "public", file), "utf-8");
13414
13718
  const productNameEsc = escapeHtml(BRAND.productName);
13415
13719
  html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
13416
13720
  html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
@@ -13426,26 +13730,26 @@ ${clientErrorReporterScript}
13426
13730
  }
13427
13731
  var brandedHtmlCache = /* @__PURE__ */ new Map();
13428
13732
  function loadBrandingCache(agentSlug) {
13429
- const configDir2 = join10(homedir2(), BRAND.configDir);
13733
+ const configDir2 = join11(homedir2(), BRAND.configDir);
13430
13734
  try {
13431
- const accountJsonPath = join10(configDir2, "account.json");
13432
- if (!existsSync24(accountJsonPath)) return null;
13433
- const account = JSON.parse(readFileSync17(accountJsonPath, "utf-8"));
13735
+ const accountJsonPath = join11(configDir2, "account.json");
13736
+ if (!existsSync25(accountJsonPath)) return null;
13737
+ const account = JSON.parse(readFileSync19(accountJsonPath, "utf-8"));
13434
13738
  const accountId = account.accountId;
13435
13739
  if (!accountId) return null;
13436
- const cachePath = join10(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
13437
- if (!existsSync24(cachePath)) return null;
13438
- return JSON.parse(readFileSync17(cachePath, "utf-8"));
13740
+ const cachePath = join11(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
13741
+ if (!existsSync25(cachePath)) return null;
13742
+ return JSON.parse(readFileSync19(cachePath, "utf-8"));
13439
13743
  } catch {
13440
13744
  return null;
13441
13745
  }
13442
13746
  }
13443
13747
  function resolveDefaultSlug() {
13444
13748
  try {
13445
- const configDir2 = join10(homedir2(), BRAND.configDir);
13446
- const accountJsonPath = join10(configDir2, "account.json");
13447
- if (!existsSync24(accountJsonPath)) return null;
13448
- const account = JSON.parse(readFileSync17(accountJsonPath, "utf-8"));
13749
+ const configDir2 = join11(homedir2(), BRAND.configDir);
13750
+ const accountJsonPath = join11(configDir2, "account.json");
13751
+ if (!existsSync25(accountJsonPath)) return null;
13752
+ const account = JSON.parse(readFileSync19(accountJsonPath, "utf-8"));
13449
13753
  return account.defaultAgent || null;
13450
13754
  } catch {
13451
13755
  return null;
@@ -13454,11 +13758,11 @@ function resolveDefaultSlug() {
13454
13758
  function brandedPublicHtml(agentSlug) {
13455
13759
  const baseHtml = cachedHtml("public.html");
13456
13760
  if (!agentSlug) return baseHtml;
13457
- const cached = brandedHtmlCache.get(agentSlug);
13761
+ const cached2 = brandedHtmlCache.get(agentSlug);
13458
13762
  const branding = loadBrandingCache(agentSlug);
13459
13763
  if (!branding) return baseHtml;
13460
13764
  const brandHash = JSON.stringify(branding).length;
13461
- if (cached && cached.mtime === brandHash) return cached.html;
13765
+ if (cached2 && cached2.mtime === brandHash) return cached2.html;
13462
13766
  const title = escapeHtml(branding.name);
13463
13767
  const description = branding.tagline ? escapeHtml(branding.tagline) : "";
13464
13768
  const themeColor = branding.primaryColor || "";
@@ -13518,7 +13822,7 @@ app36.use("/vnc-popout.html", logViewerFetch);
13518
13822
  app36.get("/vnc-popout.html", (c) => {
13519
13823
  let html = htmlCache.get("vnc-popout.html");
13520
13824
  if (!html) {
13521
- html = readFileSync17(resolve24(process.cwd(), "public", "vnc-popout.html"), "utf-8");
13825
+ html = readFileSync19(resolve25(process.cwd(), "public", "vnc-popout.html"), "utf-8");
13522
13826
  const name = escapeHtml(BRAND.productName);
13523
13827
  html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
13524
13828
  html = html.replace("</head>", ` ${brandScript}
@@ -13608,8 +13912,8 @@ try {
13608
13912
  (async () => {
13609
13913
  try {
13610
13914
  let userId = "";
13611
- if (existsSync24(USERS_FILE)) {
13612
- const users = JSON.parse(readFileSync17(USERS_FILE, "utf-8").trim() || "[]");
13915
+ if (existsSync25(USERS_FILE)) {
13916
+ const users = JSON.parse(readFileSync19(USERS_FILE, "utf-8").trim() || "[]");
13613
13917
  userId = users[0]?.userId ?? "";
13614
13918
  }
13615
13919
  await backfillNullUserIdConversations(userId);
@@ -13669,6 +13973,48 @@ for (const dir of bootEnabled) {
13669
13973
  bootDelivered.push(dir);
13670
13974
  }
13671
13975
  console.error(`[plugins] readiness enabled=${bootEnabled.length} delivered=${bootDelivered.length} dist-missing=[${bootDistMissing.join(",")}] missing=[${bootMissing.join(",")}]`);
13976
+ (async () => {
13977
+ if (!bootAccount) {
13978
+ console.log("[action-completion-relay] phase=consumed-skip reason=no-account");
13979
+ return;
13980
+ }
13981
+ let records;
13982
+ try {
13983
+ records = consumeActionCompletionRelays(bootAccount.accountDir);
13984
+ } catch (err) {
13985
+ console.error(`[action-completion-relay] phase=consume-failed error=${err instanceof Error ? err.message : String(err)}`);
13986
+ return;
13987
+ }
13988
+ if (records.length === 0) {
13989
+ console.log(`[action-completion-relay] phase=consumed-empty accountId=${bootAccount.accountId.slice(0, 8)}\u2026`);
13990
+ return;
13991
+ }
13992
+ console.log(`[action-completion-relay] phase=consume-batch count=${records.length} accountId=${bootAccount.accountId.slice(0, 8)}\u2026`);
13993
+ for (const rec of records) {
13994
+ const sessionKey = `cloudflare-relay-boot:${rec.actionId}`;
13995
+ let outcome = "injected";
13996
+ let dispatchError;
13997
+ try {
13998
+ registerSession(sessionKey, "admin", bootAccount.accountId);
13999
+ setConversationIdForSession(sessionKey, rec.conversationId);
14000
+ for await (const _ev of invokeAgent({ type: "admin" }, rec.message, sessionKey)) {
14001
+ }
14002
+ } catch (err) {
14003
+ outcome = "dispatch-failed";
14004
+ dispatchError = err instanceof Error ? err.message : String(err);
14005
+ } finally {
14006
+ unregisterSession(sessionKey);
14007
+ }
14008
+ if (outcome === "injected") {
14009
+ deleteConsumedRelay(rec.filePath);
14010
+ console.log(`[action-completion-relay] phase=consumed actionId=${rec.actionId} conversationId=${rec.conversationId} ageMs=${rec.ageMs} outcome=injected`);
14011
+ } else {
14012
+ 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)}`);
14013
+ }
14014
+ }
14015
+ })().catch((err) => {
14016
+ console.error(`[action-completion-relay] boot-drain rejected: ${err instanceof Error ? err.message : String(err)}`);
14017
+ });
13672
14018
  if (bootAccountConfig?.whatsapp) {
13673
14019
  console.error(`[whatsapp:boot] loading whatsapp config from account.json publicAgent=${bootPublicAgent ?? "none"}`);
13674
14020
  } else {
@@ -13676,7 +14022,7 @@ if (bootAccountConfig?.whatsapp) {
13676
14022
  }
13677
14023
  init({
13678
14024
  configDir: configDirForWhatsApp,
13679
- platformRoot: resolve24(process.env.MAXY_PLATFORM_ROOT ?? join10(__dirname, "..")),
14025
+ platformRoot: resolve25(process.env.MAXY_PLATFORM_ROOT ?? join11(__dirname, "..")),
13680
14026
  accountConfig: bootAccountConfig,
13681
14027
  onMessage: async (msg) => {
13682
14028
  try {