@rubytech/create-realagent 1.0.676 → 1.0.678

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 (29) hide show
  1. package/dist/index.js +77 -166
  2. package/dist/uninstall.js +24 -0
  3. package/package.json +1 -1
  4. package/payload/platform/plugins/docs/references/deployment.md +2 -3
  5. package/payload/platform/plugins/docs/references/internals.md +4 -0
  6. package/payload/platform/plugins/docs/references/platform.md +1 -1
  7. package/payload/platform/plugins/docs/references/troubleshooting.md +2 -2
  8. package/payload/platform/scripts/vnc.sh +34 -13
  9. package/payload/platform/templates/systemd/maxy-edge.service +33 -0
  10. package/payload/server/maxy-edge.js +2 -0
  11. package/payload/server/public/assets/{admin-DQmUdTBa.js → admin-BBL1no_g.js} +1 -1
  12. package/payload/server/public/assets/{data-DVlvxbTt.js → data-DUSyrydY.js} +1 -1
  13. package/payload/server/public/assets/{file-OY_hX2wu.js → file-CDJ6dUV3.js} +1 -1
  14. package/payload/server/public/assets/graph-CWcYp5bE.js +50 -0
  15. package/payload/server/public/assets/{house-CgENfOCP.js → house-CNP_bwvT.js} +1 -1
  16. package/payload/server/public/assets/{jsx-runtime-Bu4vXoe7.css → jsx-runtime-BFFQvkdQ.css} +1 -1
  17. package/payload/server/public/assets/{public-Clp4VPwo.js → public-sHoAccvb.js} +1 -1
  18. package/payload/server/public/assets/{share-2-RSIR3MmX.js → share-2-DBcb9j6E.js} +1 -1
  19. package/payload/server/public/assets/{useVoiceRecorder-B0FI_hts.js → useVoiceRecorder-CtSgpc95.js} +1 -1
  20. package/payload/server/public/assets/{x-DKZ5NR3n.js → x-CTVJaC_u.js} +1 -1
  21. package/payload/server/public/data.html +6 -6
  22. package/payload/server/public/graph.html +6 -6
  23. package/payload/server/public/index.html +7 -7
  24. package/payload/server/public/public.html +4 -4
  25. package/payload/server/server.js +427 -649
  26. package/payload/platform/templates/dotfiles/.tmux.conf +0 -1
  27. package/payload/platform/templates/systemd/maxy-ttyd.service +0 -25
  28. package/payload/server/public/assets/graph-BDaM4Qer.js +0 -49
  29. /package/payload/server/public/assets/{jsx-runtime-C_VUlXvu.js → jsx-runtime-BVKWELH6.js} +0 -0
@@ -16,7 +16,6 @@ import {
16
16
  invalidateRemoteSession,
17
17
  isPasswordValid,
18
18
  isRemoteAuthConfigured,
19
- newCorrId,
20
19
  recordFailedAttempt,
21
20
  renderLoginPage,
22
21
  resolveClientIp,
@@ -2530,7 +2529,7 @@ var responseViaResponseObject = async (res, outgoing, options = {}) => {
2530
2529
  });
2531
2530
  if (!chunk) {
2532
2531
  if (i === 1) {
2533
- await new Promise((resolve29) => setTimeout(resolve29));
2532
+ await new Promise((resolve28) => setTimeout(resolve28));
2534
2533
  maxReadCount = 3;
2535
2534
  continue;
2536
2535
  }
@@ -2897,317 +2896,9 @@ var serveStatic = (options = { root: "" }) => {
2897
2896
 
2898
2897
  // server/index.ts
2899
2898
  import { readFileSync as readFileSync24, existsSync as existsSync23, watchFile } from "fs";
2900
- import { resolve as resolve28, join as join12, basename as basename7 } from "path";
2899
+ import { resolve as resolve27, join as join12, basename as basename7 } from "path";
2901
2900
  import { homedir as homedir4 } from "os";
2902
2901
 
2903
- // server/ws-proxy-terminal.ts
2904
- import { createConnection } from "net";
2905
-
2906
- // app/lib/terminal-logger.ts
2907
- import { appendFileSync, mkdirSync } from "fs";
2908
- import { resolve } from "path";
2909
- var TERMINAL_LOG_FILE = resolve(LOG_DIR, "terminal.log");
2910
- try {
2911
- mkdirSync(LOG_DIR, { recursive: true });
2912
- } catch (err) {
2913
- console.error(`[terminal-log-fail] mkdir ${LOG_DIR} failed: ${err.message}`);
2914
- }
2915
- function terminalLog(phase, fields = {}) {
2916
- const ts = (/* @__PURE__ */ new Date()).toISOString();
2917
- const kv = Object.entries(fields).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(" ");
2918
- const line = kv.length > 0 ? `[${ts}] [terminal-${phase}] ${kv}
2919
- ` : `[${ts}] [terminal-${phase}]
2920
- `;
2921
- try {
2922
- appendFileSync(TERMINAL_LOG_FILE, line);
2923
- } catch (err) {
2924
- console.error(`[terminal-log-fail] ${err.message} \u2014 dropped: ${line.slice(0, 300).trim()}`);
2925
- }
2926
- }
2927
-
2928
- // server/ws-proxy-terminal.ts
2929
- var WS_PATH = "/admin/terminal/ws";
2930
- var UPSTREAM_TIMEOUT_MS = 5e3;
2931
- var FLOW_TICK_MS = 5e3;
2932
- var FLOW_IDLE_EMIT_MS = 3e4;
2933
- var HOP_BY_HOP = /* @__PURE__ */ new Set([
2934
- "connection",
2935
- "keep-alive",
2936
- "proxy-authenticate",
2937
- "proxy-authorization",
2938
- "te",
2939
- "trailer",
2940
- "transfer-encoding",
2941
- "upgrade"
2942
- ]);
2943
- function attachTerminalWsProxy(server, opts) {
2944
- const upstreamHost = opts.upstreamHost ?? "127.0.0.1";
2945
- const upstreamPort = opts.upstreamPort ?? 7681;
2946
- server.on("upgrade", (req, clientSocket, head) => {
2947
- try {
2948
- handleUpgrade(req, clientSocket, head, {
2949
- isPublicHost: opts.isPublicHost,
2950
- upstreamHost,
2951
- upstreamPort
2952
- });
2953
- } catch (err) {
2954
- terminalLog("ws-upgrade", {
2955
- decision: "rejected",
2956
- reason: "handler-exception",
2957
- err: err.message
2958
- });
2959
- clientSocket.destroy();
2960
- }
2961
- });
2962
- }
2963
- function handleUpgrade(req, clientSocket, head, opts) {
2964
- const url = req.url ?? "";
2965
- const qsIndex = url.indexOf("?");
2966
- const pathname = qsIndex === -1 ? url : url.slice(0, qsIndex);
2967
- if (pathname !== WS_PATH) {
2968
- return;
2969
- }
2970
- const corrId = newCorrId();
2971
- const query = qsIndex === -1 ? "" : url.slice(qsIndex + 1);
2972
- const rawClientCorrId = parseQueryParam(query, "corrId");
2973
- const clientCorrId = sanitizeClientCorrId(rawClientCorrId);
2974
- const hostHeader = (req.headers.host ?? "").split(":")[0];
2975
- const originHeader = headerString(req.headers.origin);
2976
- const remote = req.socket.remoteAddress;
2977
- const xff = headerString(req.headers["x-forwarded-for"]);
2978
- const decision = canAccessAdmin({
2979
- host: hostHeader,
2980
- remoteAddress: remote,
2981
- xForwardedFor: xff,
2982
- cookieHeader: headerString(req.headers.cookie),
2983
- isPublicHost: opts.isPublicHost
2984
- });
2985
- if (!decision.allow) {
2986
- const status = decision.reason === "public-host" ? 404 : 401;
2987
- terminalLog("ws-upgrade", {
2988
- corrId,
2989
- clientCorrId: clientCorrId ?? null,
2990
- decision: "rejected",
2991
- reason: decision.reason,
2992
- ip: remote,
2993
- xff: xff ?? null,
2994
- origin: originHeader ?? null,
2995
- host: hostHeader
2996
- });
2997
- writeStatusAndDestroy(clientSocket, status, decision.reason === "public-host" ? "Not Found" : "Unauthorized");
2998
- return;
2999
- }
3000
- const originHost = parseOriginHost(originHeader);
3001
- if (!originHost) {
3002
- terminalLog("ws-upgrade", {
3003
- corrId,
3004
- clientCorrId: clientCorrId ?? null,
3005
- decision: "rejected",
3006
- reason: "origin-missing-or-invalid",
3007
- origin: originHeader ?? null,
3008
- host: hostHeader,
3009
- ip: remote
3010
- });
3011
- writeStatusAndDestroy(clientSocket, 403, "Forbidden");
3012
- return;
3013
- }
3014
- if (originHost !== hostHeader) {
3015
- terminalLog("ws-upgrade", {
3016
- corrId,
3017
- clientCorrId: clientCorrId ?? null,
3018
- decision: "rejected",
3019
- reason: "origin-mismatch",
3020
- origin_host: originHost,
3021
- host: hostHeader,
3022
- ip: remote
3023
- });
3024
- writeStatusAndDestroy(clientSocket, 403, "Forbidden");
3025
- return;
3026
- }
3027
- if (opts.isPublicHost(originHost)) {
3028
- terminalLog("ws-upgrade", {
3029
- corrId,
3030
- clientCorrId: clientCorrId ?? null,
3031
- decision: "rejected",
3032
- reason: "origin-public-host",
3033
- origin_host: originHost,
3034
- host: hostHeader,
3035
- ip: remote
3036
- });
3037
- writeStatusAndDestroy(clientSocket, 403, "Forbidden");
3038
- return;
3039
- }
3040
- terminalLog("ws-upgrade", {
3041
- corrId,
3042
- clientCorrId: clientCorrId ?? null,
3043
- decision: "accepted",
3044
- ip: remote,
3045
- xff: xff ?? null,
3046
- origin: originHeader ?? null,
3047
- host: hostHeader,
3048
- sec_ws_version: headerString(req.headers["sec-websocket-version"]) ?? null,
3049
- sec_ws_protocol: headerString(req.headers["sec-websocket-protocol"]) ?? null
3050
- });
3051
- const upstream = createConnection({ host: opts.upstreamHost, port: opts.upstreamPort });
3052
- upstream.setTimeout(UPSTREAM_TIMEOUT_MS);
3053
- let bytesClientToUpstream = 0;
3054
- let bytesUpstreamToClient = 0;
3055
- let closedBy = null;
3056
- let proxyOpened = false;
3057
- let lastUpstreamByteTs = 0;
3058
- let flowInterval = null;
3059
- let lastEmittedAt = 0;
3060
- let lastEmittedUpstreamBytes = 0;
3061
- const finish = (side, reason) => {
3062
- if (closedBy) return;
3063
- closedBy = side;
3064
- if (flowInterval) {
3065
- clearInterval(flowInterval);
3066
- flowInterval = null;
3067
- }
3068
- if (proxyOpened) {
3069
- terminalLog("proxy-close", {
3070
- corrId,
3071
- closedBy: side,
3072
- reason,
3073
- clientBytes: bytesClientToUpstream,
3074
- upstreamBytes: bytesUpstreamToClient
3075
- });
3076
- }
3077
- clientSocket.destroy();
3078
- upstream.destroy();
3079
- };
3080
- upstream.once("connect", () => {
3081
- upstream.setTimeout(0);
3082
- proxyOpened = true;
3083
- terminalLog("proxy-open", {
3084
- corrId,
3085
- upstream: `${opts.upstreamHost}:${opts.upstreamPort}`
3086
- });
3087
- const lines = [];
3088
- lines.push(`${req.method ?? "GET"} ${WS_PATH} HTTP/${req.httpVersion}`);
3089
- lines.push(`host: ${opts.upstreamHost}:${opts.upstreamPort}`);
3090
- for (const [name, value] of Object.entries(req.headers)) {
3091
- if (name === "host") continue;
3092
- if (HOP_BY_HOP.has(name)) continue;
3093
- if (value == null) continue;
3094
- if (Array.isArray(value)) {
3095
- for (const v of value) lines.push(`${name}: ${v}`);
3096
- } else {
3097
- lines.push(`${name}: ${value}`);
3098
- }
3099
- }
3100
- const upgradeHeader = headerString(req.headers.upgrade);
3101
- const connectionHeader = headerString(req.headers.connection);
3102
- if (upgradeHeader) lines.push(`upgrade: ${upgradeHeader}`);
3103
- if (connectionHeader) lines.push(`connection: ${connectionHeader}`);
3104
- upstream.write(lines.join("\r\n") + "\r\n\r\n");
3105
- if (head && head.length > 0) upstream.write(head);
3106
- clientSocket.on("data", (chunk) => {
3107
- bytesClientToUpstream += chunk.length;
3108
- });
3109
- upstream.on("data", (chunk) => {
3110
- bytesUpstreamToClient += chunk.length;
3111
- lastUpstreamByteTs = Date.now();
3112
- });
3113
- clientSocket.pipe(upstream);
3114
- upstream.pipe(clientSocket);
3115
- lastEmittedAt = Date.now();
3116
- lastEmittedUpstreamBytes = 0;
3117
- flowInterval = setInterval(() => {
3118
- const now = Date.now();
3119
- const idleMs = lastUpstreamByteTs === 0 ? now - lastEmittedAt : now - lastUpstreamByteTs;
3120
- const upstreamMoved = bytesUpstreamToClient !== lastEmittedUpstreamBytes;
3121
- const sinceLastEmit = now - lastEmittedAt;
3122
- if (upstreamMoved || sinceLastEmit >= FLOW_IDLE_EMIT_MS) {
3123
- terminalLog("proxy-flow", {
3124
- corrId,
3125
- clientBytes: bytesClientToUpstream,
3126
- upstreamBytes: bytesUpstreamToClient,
3127
- idleMs
3128
- });
3129
- lastEmittedAt = now;
3130
- lastEmittedUpstreamBytes = bytesUpstreamToClient;
3131
- }
3132
- }, FLOW_TICK_MS);
3133
- clientSocket.once("close", (hadError) => {
3134
- finish("client", hadError ? "error" : "normal");
3135
- });
3136
- upstream.once("close", (hadError) => {
3137
- finish("upstream", hadError ? "error" : "normal");
3138
- });
3139
- clientSocket.once("error", (err) => {
3140
- terminalLog("proxy-error", { corrId, side: "client", err: err.message });
3141
- finish("client", "error");
3142
- });
3143
- upstream.once("error", (err) => {
3144
- terminalLog("proxy-error", { corrId, side: "upstream", err: err.message });
3145
- finish("upstream", "error");
3146
- });
3147
- });
3148
- upstream.once("timeout", () => {
3149
- if (proxyOpened) return;
3150
- terminalLog("proxy-error", {
3151
- corrId,
3152
- side: "upstream-connect",
3153
- err: "timeout",
3154
- timeout_ms: UPSTREAM_TIMEOUT_MS
3155
- });
3156
- writeStatusAndDestroy(clientSocket, 504, "Gateway Timeout");
3157
- upstream.destroy();
3158
- });
3159
- upstream.once("error", (err) => {
3160
- if (proxyOpened) return;
3161
- terminalLog("proxy-error", {
3162
- corrId,
3163
- side: "upstream-connect",
3164
- err: err.message
3165
- });
3166
- writeStatusAndDestroy(clientSocket, 502, "Bad Gateway");
3167
- upstream.destroy();
3168
- });
3169
- }
3170
- function parseQueryParam(query, key) {
3171
- if (!query) return null;
3172
- for (const pair of query.split("&")) {
3173
- const eq = pair.indexOf("=");
3174
- const k = eq === -1 ? pair : pair.slice(0, eq);
3175
- if (k !== key) continue;
3176
- const v = eq === -1 ? "" : pair.slice(eq + 1);
3177
- try {
3178
- return decodeURIComponent(v);
3179
- } catch {
3180
- return null;
3181
- }
3182
- }
3183
- return null;
3184
- }
3185
- function headerString(value) {
3186
- if (value == null) return void 0;
3187
- return Array.isArray(value) ? value[0] : value;
3188
- }
3189
- function parseOriginHost(origin) {
3190
- if (!origin) return null;
3191
- try {
3192
- return new URL(origin).hostname;
3193
- } catch {
3194
- return null;
3195
- }
3196
- }
3197
- function writeStatusAndDestroy(socket, status, statusText) {
3198
- try {
3199
- socket.write(
3200
- `HTTP/1.1 ${status} ${statusText}\r
3201
- Connection: close\r
3202
- Content-Length: 0\r
3203
- \r
3204
- `
3205
- );
3206
- } catch {
3207
- }
3208
- socket.destroy();
3209
- }
3210
-
3211
2902
  // app/lib/agent-slug-pattern.ts
3212
2903
  var AGENT_SLUG_PATTERN = /^\/([a-z][a-z0-9-]{2,49})$/;
3213
2904
 
@@ -3215,9 +2906,9 @@ var AGENT_SLUG_PATTERN = /^\/([a-z][a-z0-9-]{2,49})$/;
3215
2906
  import Anthropic3 from "@anthropic-ai/sdk";
3216
2907
  import { spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
3217
2908
  import { randomUUID as randomUUID2 } from "crypto";
3218
- import { resolve as resolve6, join as join3 } from "path";
2909
+ import { resolve as resolve5, join as join3 } from "path";
3219
2910
  import { platform as osPlatform } from "os";
3220
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, readdirSync as readdirSync2, existsSync as existsSync5, mkdirSync as mkdirSync5, createWriteStream, statSync as statSync3, unlinkSync as unlinkSync3, cpSync, rmSync as rmSync2, appendFileSync as appendFileSync2, openSync as openSync2, readSync as readSync2, closeSync as closeSync2 } from "fs";
2911
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, readdirSync as readdirSync2, existsSync as existsSync5, mkdirSync as mkdirSync4, createWriteStream, statSync as statSync3, unlinkSync as unlinkSync3, cpSync, rmSync as rmSync2, appendFileSync, openSync as openSync2, readSync as readSync2, closeSync as closeSync2 } from "fs";
3221
2912
  import { lookup as dnsLookup } from "dns/promises";
3222
2913
  import { createConnection as netConnect } from "net";
3223
2914
  import { StringDecoder } from "string_decoder";
@@ -3226,12 +2917,12 @@ import { StringDecoder } from "string_decoder";
3226
2917
  var import_dist = __toESM(require_dist());
3227
2918
  import {
3228
2919
  existsSync as existsSync2,
3229
- mkdirSync as mkdirSync2,
2920
+ mkdirSync,
3230
2921
  readFileSync,
3231
2922
  unlinkSync,
3232
2923
  writeFileSync
3233
2924
  } from "fs";
3234
- import { resolve as resolve2, join as join2, dirname } from "path";
2925
+ import { resolve, join as join2, dirname } from "path";
3235
2926
  import { homedir } from "os";
3236
2927
  var cachedKeyFilePath = null;
3237
2928
  function resolveKeyFilePath() {
@@ -3262,7 +2953,7 @@ function resolveKeyFilePath() {
3262
2953
  throw err;
3263
2954
  }
3264
2955
  }
3265
- cachedKeyFilePath = resolve2(homedir(), configDirName, ".anthropic-api-key");
2956
+ cachedKeyFilePath = resolve(homedir(), configDirName, ".anthropic-api-key");
3266
2957
  return cachedKeyFilePath;
3267
2958
  }
3268
2959
  function readKey() {
@@ -3548,11 +3239,11 @@ async function ensureAuth() {
3548
3239
 
3549
3240
  // app/lib/vnc.ts
3550
3241
  import { spawnSync, execFileSync } from "child_process";
3551
- import { createConnection as createConnection2 } from "net";
3552
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
3553
- import { resolve as resolve3 } from "path";
3554
- var PLATFORM_ROOT = process.env.MAXY_PLATFORM_ROOT ?? resolve3(process.cwd(), "..");
3555
- var VNC_SCRIPT = resolve3(PLATFORM_ROOT, "scripts/vnc.sh");
3242
+ import { createConnection } from "net";
3243
+ import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
3244
+ import { resolve as resolve2 } from "path";
3245
+ var PLATFORM_ROOT = process.env.MAXY_PLATFORM_ROOT ?? resolve2(process.cwd(), "..");
3246
+ var VNC_SCRIPT = resolve2(PLATFORM_ROOT, "scripts/vnc.sh");
3556
3247
  var displayMode = process.env.DISPLAY_MODE ?? "virtual";
3557
3248
  if (displayMode === "native") {
3558
3249
  console.log(`[vnc] DISPLAY_MODE=native \u2014 local requests use desktop display, remote requests use VNC`);
@@ -3644,7 +3335,7 @@ async function waitForPort(port2, timeoutMs = 12e3) {
3644
3335
  const deadline = Date.now() + timeoutMs;
3645
3336
  while (Date.now() < deadline) {
3646
3337
  const ready = await new Promise((res) => {
3647
- const socket = createConnection2(port2, "127.0.0.1");
3338
+ const socket = createConnection(port2, "127.0.0.1");
3648
3339
  socket.setTimeout(500);
3649
3340
  socket.once("connect", () => {
3650
3341
  socket.destroy();
@@ -3665,10 +3356,10 @@ async function waitForPort(port2, timeoutMs = 12e3) {
3665
3356
  return false;
3666
3357
  }
3667
3358
  function ensureLogDir() {
3668
- mkdirSync3(LOG_DIR, { recursive: true });
3359
+ mkdirSync2(LOG_DIR, { recursive: true });
3669
3360
  }
3670
3361
  function logPath(name) {
3671
- return resolve3(LOG_DIR, `${name}.log`);
3362
+ return resolve2(LOG_DIR, `${name}.log`);
3672
3363
  }
3673
3364
  async function ensureVnc() {
3674
3365
  const up = await waitForPort(5900, 1e3);
@@ -3877,8 +3568,8 @@ async function ensureTerminalUpgrade(transport = "vnc") {
3877
3568
  return { ok: true };
3878
3569
  }
3879
3570
  function writeChromiumWrapper() {
3880
- mkdirSync3(BIN_DIR, { recursive: true });
3881
- const wrapperPath = resolve3(BIN_DIR, "chromium");
3571
+ mkdirSync2(BIN_DIR, { recursive: true });
3572
+ const wrapperPath = resolve2(BIN_DIR, "chromium");
3882
3573
  writeFileSync3(wrapperPath, `#!/bin/bash
3883
3574
  LOG="${LOG_DIR}/chromium.log"
3884
3575
  echo "==== [$(date)] chromium wrapper ====" >> "$LOG"
@@ -3964,12 +3655,12 @@ import neo4j from "neo4j-driver";
3964
3655
  import { randomUUID } from "crypto";
3965
3656
  import { spawn } from "child_process";
3966
3657
  import { readFileSync as readFileSync4, readdirSync, existsSync as existsSync3, openSync, readSync, closeSync, statSync as statSync2, rmSync } from "fs";
3967
- import { resolve as resolve4 } from "path";
3968
- var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve4(process.cwd(), "..");
3658
+ import { resolve as resolve3 } from "path";
3659
+ var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve3(process.cwd(), "..");
3969
3660
  var driver = null;
3970
3661
  function readPassword() {
3971
3662
  if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
3972
- const passwordFile = resolve4(PLATFORM_ROOT2, "config/.neo4j-password");
3663
+ const passwordFile = resolve3(PLATFORM_ROOT2, "config/.neo4j-password");
3973
3664
  try {
3974
3665
  return readFileSync4(passwordFile, "utf-8").trim();
3975
3666
  } catch {
@@ -4434,8 +4125,8 @@ async function persistMessage(conversationId, role, content, accountId, tokens,
4434
4125
  const prev = persistMessageLocks.get(conversationId);
4435
4126
  const waited = prev !== void 0;
4436
4127
  let release;
4437
- const mine = new Promise((resolve29) => {
4438
- release = resolve29;
4128
+ const mine = new Promise((resolve28) => {
4129
+ release = resolve28;
4439
4130
  });
4440
4131
  const chained = (prev ?? Promise.resolve()).then(() => mine);
4441
4132
  persistMessageLocks.set(conversationId, chained);
@@ -4694,7 +4385,7 @@ ${userContent}`;
4694
4385
  "dontAsk",
4695
4386
  prompt
4696
4387
  ];
4697
- return new Promise((resolve29) => {
4388
+ return new Promise((resolve28) => {
4698
4389
  let stdout = "";
4699
4390
  let stderr = "";
4700
4391
  const spawnFn = _spawnOverride ?? spawn;
@@ -4712,35 +4403,35 @@ ${userContent}`;
4712
4403
  const timer = setTimeout(() => {
4713
4404
  proc.kill("SIGTERM");
4714
4405
  console.error("[persist] autoLabel: haiku subprocess timed out");
4715
- resolve29(null);
4406
+ resolve28(null);
4716
4407
  }, SESSION_LABEL_TIMEOUT_MS);
4717
4408
  proc.on("error", (err) => {
4718
4409
  clearTimeout(timer);
4719
4410
  console.error(`[persist] autoLabel: subprocess error \u2014 ${err.message}`);
4720
- resolve29(null);
4411
+ resolve28(null);
4721
4412
  });
4722
4413
  proc.on("close", (code) => {
4723
4414
  clearTimeout(timer);
4724
4415
  if (code !== 0) {
4725
4416
  console.error(`[persist] autoLabel: subprocess exited code=${code}${stderr ? ` stderr=${stderr.trim().slice(0, 200)}` : ""}`);
4726
- resolve29(null);
4417
+ resolve28(null);
4727
4418
  return;
4728
4419
  }
4729
4420
  const text = stdout.trim();
4730
4421
  if (!text) {
4731
4422
  console.error("[persist] autoLabel: haiku returned empty response");
4732
- resolve29(null);
4423
+ resolve28(null);
4733
4424
  return;
4734
4425
  }
4735
4426
  if (text === "SKIP") {
4736
4427
  console.error("[persist] autoLabel: haiku returned SKIP \u2014 messages too vague");
4737
- resolve29(null);
4428
+ resolve28(null);
4738
4429
  return;
4739
4430
  }
4740
4431
  const words = text.split(/\s+/).slice(0, SESSION_LABEL_MAX_WORDS);
4741
4432
  const label = words.join(" ");
4742
4433
  console.error(`[persist] autoLabel: haiku response="${label}"`);
4743
- resolve29(label);
4434
+ resolve28(label);
4744
4435
  });
4745
4436
  });
4746
4437
  }
@@ -5012,9 +4703,9 @@ var MAX_RECENT_TOOL_FAILURES = 3;
5012
4703
  var RECENT_FAILURES_TAIL_BYTES = 10 * 1024;
5013
4704
  function readRecentToolFailures(accountId, conversationId) {
5014
4705
  try {
5015
- const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve4(process.cwd(), "..");
5016
- const logDir = resolve4(platformRoot2, "..", "data/accounts", accountId, "logs");
5017
- const logPath2 = resolve4(logDir, `claude-agent-stream-${conversationId}.log`);
4706
+ const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve3(process.cwd(), "..");
4707
+ const logDir = resolve3(platformRoot2, "..", "data/accounts", accountId, "logs");
4708
+ const logPath2 = resolve3(logDir, `claude-agent-stream-${conversationId}.log`);
5018
4709
  if (!existsSync3(logPath2)) {
5019
4710
  console.error(`[review-tail-skip] path=${logPath2} reason=file-missing \u2014 first turn of conversation, subprocess not yet spawned, or log rotated`);
5020
4711
  return [];
@@ -5171,13 +4862,13 @@ ${taskLines.join("\n")}`);
5171
4862
  let pendingCount = 0;
5172
4863
  let pendingLines = [];
5173
4864
  try {
5174
- const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve4(process.cwd(), "..");
5175
- const pendingDir = resolve4(platformRoot2, "..", "data/accounts", accountId, "pending-actions");
4865
+ const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve3(process.cwd(), "..");
4866
+ const pendingDir = resolve3(platformRoot2, "..", "data/accounts", accountId, "pending-actions");
5176
4867
  if (existsSync3(pendingDir)) {
5177
4868
  const files = readdirSync(pendingDir).filter((f) => f.endsWith(".json") && !f.startsWith("."));
5178
4869
  for (const file of files) {
5179
4870
  try {
5180
- const raw2 = readFileSync4(resolve4(pendingDir, file), "utf-8");
4871
+ const raw2 = readFileSync4(resolve3(pendingDir, file), "utf-8");
5181
4872
  const action = JSON.parse(raw2);
5182
4873
  if (action.state === "pending") {
5183
4874
  const inputSummary = JSON.stringify(action.hookPayload?.tool_input ?? {}).slice(0, 150);
@@ -5244,8 +4935,8 @@ ${sections.join("\n\n")}
5244
4935
  }
5245
4936
  }
5246
4937
  async function consumeStep7FlagUI(session, accountId) {
5247
- const accountDir = resolve4(PLATFORM_ROOT2, "..", "data/accounts", accountId);
5248
- const flagPath = resolve4(accountDir, "onboarding", "step7-complete");
4938
+ const accountDir = resolve3(PLATFORM_ROOT2, "..", "data/accounts", accountId);
4939
+ const flagPath = resolve3(accountDir, "onboarding", "step7-complete");
5249
4940
  if (!existsSync3(flagPath)) return false;
5250
4941
  let completedAt = (/* @__PURE__ */ new Date()).toISOString();
5251
4942
  try {
@@ -5687,8 +5378,8 @@ ${items}
5687
5378
  }
5688
5379
 
5689
5380
  // app/lib/adherence-ledger.ts
5690
- import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync5, renameSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
5691
- import { resolve as resolve5, dirname as dirname2 } from "path";
5381
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readFileSync as readFileSync5, renameSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync4 } from "fs";
5382
+ import { resolve as resolve4, dirname as dirname2 } from "path";
5692
5383
  import lockfile from "proper-lockfile";
5693
5384
  var LOG_TAG = "[adherence-ledger]";
5694
5385
  var ADHERENCE_BLOCK_THRESHOLD = 5;
@@ -5703,7 +5394,7 @@ function nowIso() {
5703
5394
  return (/* @__PURE__ */ new Date()).toISOString();
5704
5395
  }
5705
5396
  function ledgerPath(accountDir, agentName) {
5706
- return resolve5(accountDir, "agents", agentName, "adherence-ledger.json");
5397
+ return resolve4(accountDir, "agents", agentName, "adherence-ledger.json");
5707
5398
  }
5708
5399
  function lockPath(accountDir, agentName) {
5709
5400
  return `${ledgerPath(accountDir, agentName)}.lock`;
@@ -5748,7 +5439,7 @@ function writeAtomic(path2, data) {
5748
5439
  }
5749
5440
  data.updated_at = nowIso();
5750
5441
  const dir = dirname2(path2);
5751
- mkdirSync4(dir, { recursive: true });
5442
+ mkdirSync3(dir, { recursive: true });
5752
5443
  const tmp = `${path2}.tmp-${process.pid}-${Date.now()}`;
5753
5444
  try {
5754
5445
  writeFileSync4(tmp, JSON.stringify(data, null, 2), "utf-8");
@@ -5764,7 +5455,7 @@ function writeAtomic(path2, data) {
5764
5455
  async function withLock(accountDir, agentName, fn) {
5765
5456
  const path2 = ledgerPath(accountDir, agentName);
5766
5457
  const lock = lockPath(accountDir, agentName);
5767
- mkdirSync4(dirname2(lock), { recursive: true });
5458
+ mkdirSync3(dirname2(lock), { recursive: true });
5768
5459
  if (!existsSync4(lock)) {
5769
5460
  writeFileSync4(lock, "", "utf-8");
5770
5461
  }
@@ -6252,20 +5943,20 @@ function agentLogStream(name, accountDir, conversationId) {
6252
5943
  if (!conversationId) {
6253
5944
  throw new Error(`agentLogStream: conversationId is required (name=${name}) \u2014 use preConversationLogStream for pre-session events`);
6254
5945
  }
6255
- const logDir = resolve6(accountDir, "logs");
6256
- mkdirSync5(logDir, { recursive: true });
5946
+ const logDir = resolve5(accountDir, "logs");
5947
+ mkdirSync4(logDir, { recursive: true });
6257
5948
  purgeOldLogs(logDir, `${name}-`);
6258
- const logPath2 = resolve6(logDir, `${name}-${conversationId}.log`);
5949
+ const logPath2 = resolve5(logDir, `${name}-${conversationId}.log`);
6259
5950
  const stream = createWriteStream(logPath2, { flags: "a" });
6260
5951
  registerStreamLog(stream, { path: logPath2, conversationId, name });
6261
5952
  return stream;
6262
5953
  }
6263
5954
  function preConversationLogStream(name, accountDir) {
6264
- const logDir = resolve6(accountDir, "logs");
6265
- mkdirSync5(logDir, { recursive: true });
5955
+ const logDir = resolve5(accountDir, "logs");
5956
+ mkdirSync4(logDir, { recursive: true });
6266
5957
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
6267
5958
  purgeOldLogs(logDir, `preconversation-${name}-`);
6268
- const logPath2 = resolve6(logDir, `preconversation-${name}-${date}.log`);
5959
+ const logPath2 = resolve5(logDir, `preconversation-${name}-${date}.log`);
6269
5960
  const stream = createWriteStream(logPath2, { flags: "a" });
6270
5961
  registerStreamLog(stream, { path: logPath2, conversationId: null, name: `preconversation-${name}` });
6271
5962
  return stream;
@@ -6284,7 +5975,7 @@ function sigtermFlushStreamLogs(reason, source) {
6284
5975
  const line = `[${ts}] [server-sigterm] reason=${reason}${convPart} name=${entry.name} source=${source}
6285
5976
  `;
6286
5977
  try {
6287
- appendFileSync2(entry.path, line);
5978
+ appendFileSync(entry.path, line);
6288
5979
  } catch (err) {
6289
5980
  const msg = err instanceof Error ? err.message : String(err);
6290
5981
  console.error(`[server-sigterm-flush-err] path=${entry.path} reason=${msg}`);
@@ -6303,7 +5994,7 @@ function purgeOldLogs(logDir, prefix) {
6303
5994
  }
6304
5995
  for (const file of entries) {
6305
5996
  if (!file.startsWith(prefix)) continue;
6306
- const filePath = resolve6(logDir, file);
5997
+ const filePath = resolve5(logDir, file);
6307
5998
  try {
6308
5999
  if (statSync3(filePath).mtimeMs < cutoff) unlinkSync3(filePath);
6309
6000
  } catch (err) {
@@ -6421,8 +6112,8 @@ function sampleProcState(pid) {
6421
6112
  return `proc_err=${JSON.stringify(msg.slice(0, 60))}`;
6422
6113
  }
6423
6114
  }
6424
- var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT ?? resolve6(process.cwd(), "..");
6425
- var ACCOUNTS_DIR = resolve6(PLATFORM_ROOT3, "..", "data/accounts");
6115
+ var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT ?? resolve5(process.cwd(), "..");
6116
+ var ACCOUNTS_DIR = resolve5(PLATFORM_ROOT3, "..", "data/accounts");
6426
6117
  if (!existsSync5(PLATFORM_ROOT3)) {
6427
6118
  throw new Error(
6428
6119
  `PLATFORM_ROOT does not exist: ${PLATFORM_ROOT3}
@@ -6431,7 +6122,7 @@ Set the MAXY_PLATFORM_ROOT environment variable to the absolute path of the plat
6431
6122
  }
6432
6123
  function resolveAccount() {
6433
6124
  if (!existsSync5(ACCOUNTS_DIR)) return null;
6434
- const usersFilePath = resolve6(PLATFORM_ROOT3, "config", "users.json");
6125
+ const usersFilePath = resolve5(PLATFORM_ROOT3, "config", "users.json");
6435
6126
  let usersJsonUserId = null;
6436
6127
  if (existsSync5(usersFilePath)) {
6437
6128
  try {
@@ -6449,7 +6140,7 @@ function resolveAccount() {
6449
6140
  let fallback = null;
6450
6141
  for (const entry of entries) {
6451
6142
  if (!entry.isDirectory()) continue;
6452
- const configPath2 = resolve6(ACCOUNTS_DIR, entry.name, "account.json");
6143
+ const configPath2 = resolve5(ACCOUNTS_DIR, entry.name, "account.json");
6453
6144
  if (!existsSync5(configPath2)) continue;
6454
6145
  const raw2 = readFileSync6(configPath2, "utf-8");
6455
6146
  let config;
@@ -6466,7 +6157,7 @@ function resolveAccount() {
6466
6157
  }
6467
6158
  const result = {
6468
6159
  accountId: config.accountId,
6469
- accountDir: resolve6(ACCOUNTS_DIR, entry.name),
6160
+ accountDir: resolve5(ACCOUNTS_DIR, entry.name),
6470
6161
  config
6471
6162
  };
6472
6163
  if (usersJsonUserId && config.admins?.some((a) => a.userId === usersJsonUserId)) {
@@ -6484,7 +6175,7 @@ function resolveAccount() {
6484
6175
  return fallback;
6485
6176
  }
6486
6177
  function readAgentFile(accountDir, agentName, filename) {
6487
- const filePath = resolve6(accountDir, "agents", agentName, filename);
6178
+ const filePath = resolve5(accountDir, "agents", agentName, filename);
6488
6179
  if (!existsSync5(filePath)) return null;
6489
6180
  return readFileSync6(filePath, "utf-8");
6490
6181
  }
@@ -6521,7 +6212,7 @@ function validateAgentSlug(slug) {
6521
6212
  return true;
6522
6213
  }
6523
6214
  function resolveDefaultAgentSlug(accountDir) {
6524
- const configPath2 = resolve6(accountDir, "account.json");
6215
+ const configPath2 = resolve5(accountDir, "account.json");
6525
6216
  if (!existsSync5(configPath2)) {
6526
6217
  console.error("[agent-resolve] account.json not found \u2014 cannot resolve defaultAgent");
6527
6218
  return null;
@@ -6537,7 +6228,7 @@ function resolveDefaultAgentSlug(accountDir) {
6537
6228
  console.error("[agent-resolve] defaultAgent not configured in account.json \u2014 set it via the connect-whatsapp skill");
6538
6229
  return null;
6539
6230
  }
6540
- const agentConfigPath = resolve6(accountDir, "agents", config.defaultAgent, "config.json");
6231
+ const agentConfigPath = resolve5(accountDir, "agents", config.defaultAgent, "config.json");
6541
6232
  if (!existsSync5(agentConfigPath)) {
6542
6233
  console.error(`[agent-resolve] defaultAgent="${config.defaultAgent}" has no config.json at ${agentConfigPath}`);
6543
6234
  return null;
@@ -6610,9 +6301,9 @@ function resolveAgentConfig(accountDir, agentName) {
6610
6301
  }
6611
6302
  let knowledge = null;
6612
6303
  let knowledgeBaked = false;
6613
- const agentDir = resolve6(accountDir, "agents", agentName);
6614
- const knowledgePath = resolve6(agentDir, "KNOWLEDGE.md");
6615
- const summaryPath = resolve6(agentDir, "KNOWLEDGE-SUMMARY.md");
6304
+ const agentDir = resolve5(accountDir, "agents", agentName);
6305
+ const knowledgePath = resolve5(agentDir, "KNOWLEDGE.md");
6306
+ const summaryPath = resolve5(agentDir, "KNOWLEDGE-SUMMARY.md");
6616
6307
  const hasKnowledge = existsSync5(knowledgePath);
6617
6308
  const hasSummary = existsSync5(summaryPath);
6618
6309
  if (hasKnowledge && hasSummary) {
@@ -6648,7 +6339,7 @@ function resolveAgentConfig(accountDir, agentName) {
6648
6339
  return { model, plugins, status, displayName, image, imageShape, showAgentName, knowledge, knowledgeBaked, liveMemory, knowledgeKeywords, budget, accessMode };
6649
6340
  }
6650
6341
  function parsePluginFrontmatter(pluginDir) {
6651
- const pluginPath = resolve6(PLATFORM_ROOT3, "plugins", pluginDir, "PLUGIN.md");
6342
+ const pluginPath = resolve5(PLATFORM_ROOT3, "plugins", pluginDir, "PLUGIN.md");
6652
6343
  if (!existsSync5(pluginPath)) return null;
6653
6344
  let raw2;
6654
6345
  try {
@@ -6711,14 +6402,14 @@ function parsePluginFrontmatter(pluginDir) {
6711
6402
  function autoDeliverPremiumPlugins(purchasedPlugins) {
6712
6403
  if (!purchasedPlugins || purchasedPlugins.length === 0) return;
6713
6404
  const TAG19 = "[premium-auto-deliver]";
6714
- const stagingRoot = resolve6(PLATFORM_ROOT3, "../premium-plugins");
6715
- const pluginsDir = resolve6(PLATFORM_ROOT3, "plugins");
6405
+ const stagingRoot = resolve5(PLATFORM_ROOT3, "../premium-plugins");
6406
+ const pluginsDir = resolve5(PLATFORM_ROOT3, "plugins");
6716
6407
  if (!existsSync5(stagingRoot)) {
6717
6408
  console.log(`${TAG19} no staging directory \u2014 skipping`);
6718
6409
  return;
6719
6410
  }
6720
6411
  for (const pluginName of purchasedPlugins) {
6721
- const stagingDir = resolve6(stagingRoot, pluginName);
6412
+ const stagingDir = resolve5(stagingRoot, pluginName);
6722
6413
  if (!existsSync5(stagingDir)) {
6723
6414
  console.log(`${TAG19} ${pluginName}: not in staging \u2014 skipping`);
6724
6415
  continue;
@@ -6759,12 +6450,12 @@ function autoDeliverPremiumPlugins(purchasedPlugins) {
6759
6450
  let delivered = 0;
6760
6451
  let skipped = 0;
6761
6452
  for (const sub of subPlugins) {
6762
- const target = resolve6(pluginsDir, sub);
6763
- if (existsSync5(resolve6(target, "PLUGIN.md"))) {
6453
+ const target = resolve5(pluginsDir, sub);
6454
+ if (existsSync5(resolve5(target, "PLUGIN.md"))) {
6764
6455
  skipped++;
6765
6456
  continue;
6766
6457
  }
6767
- const source = resolve6(stagingDir, "plugins", sub);
6458
+ const source = resolve5(stagingDir, "plugins", sub);
6768
6459
  if (!existsSync5(source)) {
6769
6460
  console.log(`${TAG19} ${pluginName}/${sub}: source missing in staging \u2014 skipping`);
6770
6461
  continue;
@@ -6778,8 +6469,8 @@ function autoDeliverPremiumPlugins(purchasedPlugins) {
6778
6469
  }
6779
6470
  console.log(`${TAG19} ${pluginName} (bundle): ${delivered} delivered, ${skipped} already present`);
6780
6471
  } else {
6781
- const target = resolve6(pluginsDir, pluginName);
6782
- if (existsSync5(resolve6(target, "PLUGIN.md"))) {
6472
+ const target = resolve5(pluginsDir, pluginName);
6473
+ if (existsSync5(resolve5(target, "PLUGIN.md"))) {
6783
6474
  console.log(`${TAG19} ${pluginName}: already present \u2014 skipping`);
6784
6475
  continue;
6785
6476
  }
@@ -6819,7 +6510,7 @@ function migratePluginRenames(accountDir, config) {
6819
6510
  return name;
6820
6511
  });
6821
6512
  if (!changed) return;
6822
- const configPath2 = resolve6(accountDir, "account.json");
6513
+ const configPath2 = resolve5(accountDir, "account.json");
6823
6514
  try {
6824
6515
  const raw2 = readFileSync6(configPath2, "utf-8");
6825
6516
  const parsed = JSON.parse(raw2);
@@ -6830,9 +6521,9 @@ function migratePluginRenames(accountDir, config) {
6830
6521
  } catch (err) {
6831
6522
  console.error(`${TAG19} failed to update account.json \u2014 ${err instanceof Error ? err.message : String(err)}`);
6832
6523
  }
6833
- const pluginsDir = resolve6(PLATFORM_ROOT3, "plugins");
6524
+ const pluginsDir = resolve5(PLATFORM_ROOT3, "plugins");
6834
6525
  for (const oldName of Object.keys(PLUGIN_RENAMES)) {
6835
- const orphan = resolve6(pluginsDir, oldName);
6526
+ const orphan = resolve5(pluginsDir, oldName);
6836
6527
  if (existsSync5(orphan)) {
6837
6528
  try {
6838
6529
  rmSync2(orphan, { recursive: true });
@@ -6846,13 +6537,13 @@ function migratePluginRenames(accountDir, config) {
6846
6537
  function autoDeliverBundleAgents(accountDir, purchasedPlugins) {
6847
6538
  if (!purchasedPlugins || purchasedPlugins.length === 0) return;
6848
6539
  const TAG19 = "[bundle-agent-deliver]";
6849
- const stagingRoot = resolve6(PLATFORM_ROOT3, "../premium-plugins");
6850
- const specialistsDir = resolve6(accountDir, "specialists", "agents");
6540
+ const stagingRoot = resolve5(PLATFORM_ROOT3, "../premium-plugins");
6541
+ const specialistsDir = resolve5(accountDir, "specialists", "agents");
6851
6542
  if (!existsSync5(stagingRoot)) return;
6852
6543
  if (!existsSync5(specialistsDir)) {
6853
- mkdirSync5(specialistsDir, { recursive: true });
6544
+ mkdirSync4(specialistsDir, { recursive: true });
6854
6545
  }
6855
- const agentsmdPath = resolve6(accountDir, "agents", "admin", "AGENTS.md");
6546
+ const agentsmdPath = resolve5(accountDir, "agents", "admin", "AGENTS.md");
6856
6547
  let agentsmd = "";
6857
6548
  try {
6858
6549
  agentsmd = existsSync5(agentsmdPath) ? readFileSync6(agentsmdPath, "utf-8") : "";
@@ -6860,7 +6551,7 @@ function autoDeliverBundleAgents(accountDir, purchasedPlugins) {
6860
6551
  }
6861
6552
  let delivered = 0;
6862
6553
  for (const pluginName of purchasedPlugins) {
6863
- const bundleAgentsDir = resolve6(stagingRoot, pluginName, "agents");
6554
+ const bundleAgentsDir = resolve5(stagingRoot, pluginName, "agents");
6864
6555
  if (!existsSync5(bundleAgentsDir)) continue;
6865
6556
  let entries;
6866
6557
  try {
@@ -6869,9 +6560,9 @@ function autoDeliverBundleAgents(accountDir, purchasedPlugins) {
6869
6560
  continue;
6870
6561
  }
6871
6562
  for (const filename of entries) {
6872
- const target = resolve6(specialistsDir, filename);
6563
+ const target = resolve5(specialistsDir, filename);
6873
6564
  if (existsSync5(target)) continue;
6874
- const source = resolve6(bundleAgentsDir, filename);
6565
+ const source = resolve5(bundleAgentsDir, filename);
6875
6566
  try {
6876
6567
  cpSync(source, target);
6877
6568
  } catch (err) {
@@ -6911,8 +6602,8 @@ function autoDeliverBundleAgents(accountDir, purchasedPlugins) {
6911
6602
  }
6912
6603
  }
6913
6604
  function assemblePublicPluginContent(pluginDir) {
6914
- const pluginRoot = resolve6(PLATFORM_ROOT3, "plugins", pluginDir);
6915
- const pluginPath = resolve6(pluginRoot, "PLUGIN.md");
6605
+ const pluginRoot = resolve5(PLATFORM_ROOT3, "plugins", pluginDir);
6606
+ const pluginPath = resolve5(pluginRoot, "PLUGIN.md");
6916
6607
  let raw2;
6917
6608
  try {
6918
6609
  raw2 = readFileSync6(pluginPath, "utf-8");
@@ -6924,7 +6615,7 @@ function assemblePublicPluginContent(pluginDir) {
6924
6615
  const parts = [pluginBody];
6925
6616
  let skillCount = 0;
6926
6617
  let refCount = 0;
6927
- const skillsDir = resolve6(pluginRoot, "skills");
6618
+ const skillsDir = resolve5(pluginRoot, "skills");
6928
6619
  let skillDirs;
6929
6620
  try {
6930
6621
  skillDirs = readdirSync2(skillsDir).sort();
@@ -6932,8 +6623,8 @@ function assemblePublicPluginContent(pluginDir) {
6932
6623
  return { body: pluginBody, skillCount: 0, refCount: 0 };
6933
6624
  }
6934
6625
  for (const skillName of skillDirs) {
6935
- const skillDir = resolve6(skillsDir, skillName);
6936
- const skillMdPath = resolve6(skillDir, "SKILL.md");
6626
+ const skillDir = resolve5(skillsDir, skillName);
6627
+ const skillMdPath = resolve5(skillDir, "SKILL.md");
6937
6628
  let skillRaw;
6938
6629
  try {
6939
6630
  skillRaw = readFileSync6(skillMdPath, "utf-8");
@@ -6976,7 +6667,7 @@ function assemblePublicPluginContent(pluginDir) {
6976
6667
  parts.push(`
6977
6668
  <!-- skill: ${skillName} -->`);
6978
6669
  parts.push(skillBody);
6979
- const refsDir = resolve6(skillDir, "references");
6670
+ const refsDir = resolve5(skillDir, "references");
6980
6671
  let refFiles;
6981
6672
  try {
6982
6673
  refFiles = readdirSync2(refsDir).filter((f) => f.endsWith(".md")).filter((f) => !publicExcludeReferences.includes(f)).sort();
@@ -6987,7 +6678,7 @@ function assemblePublicPluginContent(pluginDir) {
6987
6678
  }
6988
6679
  for (const refFile of refFiles) {
6989
6680
  try {
6990
- const refContent = readFileSync6(resolve6(refsDir, refFile), "utf-8").trim();
6681
+ const refContent = readFileSync6(resolve5(refsDir, refFile), "utf-8").trim();
6991
6682
  if (refContent) {
6992
6683
  parts.push(`
6993
6684
  <!-- reference: ${refFile} -->`);
@@ -7005,7 +6696,7 @@ function assemblePublicPluginContent(pluginDir) {
7005
6696
  return { body: parts.join("\n"), skillCount, refCount };
7006
6697
  }
7007
6698
  function loadEmbeddedPlugins(agentType, selectedPlugins, enabledPlugins) {
7008
- const pluginsDir = resolve6(PLATFORM_ROOT3, "plugins");
6699
+ const pluginsDir = resolve5(PLATFORM_ROOT3, "plugins");
7009
6700
  let dirs;
7010
6701
  try {
7011
6702
  dirs = readdirSync2(pluginsDir);
@@ -7058,7 +6749,7 @@ function loadEmbeddedPlugins(agentType, selectedPlugins, enabledPlugins) {
7058
6749
  console.log(`[plugins] loaded ${dir} for public (${assembled.body.length} chars, ${assembled.skillCount} skills, ${assembled.refCount} refs)`);
7059
6750
  }
7060
6751
  } else {
7061
- const pluginPath = resolve6(pluginsDir, dir, "PLUGIN.md");
6752
+ const pluginPath = resolve5(pluginsDir, dir, "PLUGIN.md");
7062
6753
  let raw2;
7063
6754
  try {
7064
6755
  raw2 = readFileSync6(pluginPath, "utf-8");
@@ -7085,7 +6776,7 @@ var mcpToolsCache = /* @__PURE__ */ new Map();
7085
6776
  function fetchMcpToolsList(pluginDir) {
7086
6777
  const cached = mcpToolsCache.get(pluginDir);
7087
6778
  if (cached) return Promise.resolve(cached);
7088
- const serverPath = resolve6(PLATFORM_ROOT3, "plugins", pluginDir, "mcp/dist/index.js");
6779
+ const serverPath = resolve5(PLATFORM_ROOT3, "plugins", pluginDir, "mcp/dist/index.js");
7089
6780
  if (!existsSync5(serverPath)) return Promise.resolve([]);
7090
6781
  const startMs = Date.now();
7091
6782
  return new Promise((resolvePromise) => {
@@ -7198,7 +6889,7 @@ var SPECIALIST_PLUGIN_DOMAINS = {
7198
6889
  // agent, so it retains a full manifest entry for routing clarity.
7199
6890
  };
7200
6891
  async function buildPluginManifest(enabledPlugins) {
7201
- const pluginsDir = resolve6(PLATFORM_ROOT3, "plugins");
6892
+ const pluginsDir = resolve5(PLATFORM_ROOT3, "plugins");
7202
6893
  let dirs;
7203
6894
  try {
7204
6895
  dirs = readdirSync2(pluginsDir);
@@ -7285,16 +6976,16 @@ ${specialist}: ${plugins.join(", ")}`);
7285
6976
  for (let j = 0; j < adminPlugins.length; j++) {
7286
6977
  const { dir, parsed } = adminPlugins[j];
7287
6978
  const mcpTools = adminMcpResults[j];
7288
- const pluginRoot = resolve6(pluginsDir, dir);
6979
+ const pluginRoot = resolve5(pluginsDir, dir);
7289
6980
  const skills = [];
7290
6981
  const references = [];
7291
6982
  const scanDir = (base, prefix, target) => {
7292
- const scanPath = resolve6(pluginRoot, base);
6983
+ const scanPath = resolve5(pluginRoot, base);
7293
6984
  if (!existsSync5(scanPath)) return;
7294
6985
  try {
7295
6986
  const walk = (current, rel) => {
7296
6987
  for (const entry of readdirSync2(current)) {
7297
- const full = resolve6(current, entry);
6988
+ const full = resolve5(current, entry);
7298
6989
  try {
7299
6990
  const stat5 = statSync3(full);
7300
6991
  if (stat5.isDirectory()) {
@@ -7322,7 +7013,7 @@ ${specialist}: ${plugins.join(", ")}`);
7322
7013
  toolLines.push(desc ? ` ${tool.name} \u2014 ${desc}` : ` ${tool.name}`);
7323
7014
  }
7324
7015
  } else if (parsed.tools.length > 0) {
7325
- const serverPath = resolve6(PLATFORM_ROOT3, "plugins", dir, "mcp/dist/index.js");
7016
+ const serverPath = resolve5(PLATFORM_ROOT3, "plugins", dir, "mcp/dist/index.js");
7326
7017
  if (existsSync5(serverPath)) {
7327
7018
  fallbackSourced++;
7328
7019
  console.error(`[plugin-manifest] ${dir}: tools/list empty \u2014 fallback to frontmatter (${parsed.tools.length} tools)`);
@@ -7403,7 +7094,7 @@ function resolveUserAccounts(userId) {
7403
7094
  const entries = readdirSync2(ACCOUNTS_DIR, { withFileTypes: true });
7404
7095
  for (const entry of entries) {
7405
7096
  if (!entry.isDirectory()) continue;
7406
- const configPath2 = resolve6(ACCOUNTS_DIR, entry.name, "account.json");
7097
+ const configPath2 = resolve5(ACCOUNTS_DIR, entry.name, "account.json");
7407
7098
  if (!existsSync5(configPath2)) continue;
7408
7099
  let config;
7409
7100
  try {
@@ -7416,7 +7107,7 @@ function resolveUserAccounts(userId) {
7416
7107
  if (adminEntry) {
7417
7108
  results.push({
7418
7109
  accountId: config.accountId,
7419
- accountDir: resolve6(ACCOUNTS_DIR, entry.name),
7110
+ accountDir: resolve5(ACCOUNTS_DIR, entry.name),
7420
7111
  config,
7421
7112
  role: adminEntry.role
7422
7113
  });
@@ -7477,8 +7168,75 @@ function clearSessionHistory(sessionKey) {
7477
7168
  session.stalledSubagents = void 0;
7478
7169
  session.pendingTrimmedMessages = void 0;
7479
7170
  session.pendingCommitmentOffers = void 0;
7171
+ session.pendingTurns = void 0;
7480
7172
  return previousConversationId;
7481
7173
  }
7174
+ function bufferPendingTurn(sessionKey, turn) {
7175
+ const session = sessionStore.get(sessionKey);
7176
+ if (!session) {
7177
+ console.error(`[conversation-gate] bufferPendingTurn: session not found sessionKey=${sessionKey.slice(0, 8)}\u2026`);
7178
+ return;
7179
+ }
7180
+ if (!session.pendingTurns) session.pendingTurns = [];
7181
+ session.pendingTurns.push(turn);
7182
+ console.log(`[conversation-gate] ${(/* @__PURE__ */ new Date()).toISOString()} buffered sessionKey=${sessionKey.slice(0, 8)} role=${turn.role} turnCount=${session.pendingTurns.filter((t) => t.role === "user").length}`);
7183
+ }
7184
+ function getPendingTurnCount(sessionKey) {
7185
+ const buf = sessionStore.get(sessionKey)?.pendingTurns;
7186
+ if (!buf) return 0;
7187
+ let n = 0;
7188
+ for (const t of buf) if (t.role === "user") n++;
7189
+ return n;
7190
+ }
7191
+ function drainPendingTurns(sessionKey) {
7192
+ const session = sessionStore.get(sessionKey);
7193
+ if (!session?.pendingTurns || session.pendingTurns.length === 0) return void 0;
7194
+ const drained = session.pendingTurns;
7195
+ session.pendingTurns = void 0;
7196
+ return drained;
7197
+ }
7198
+ async function maybeFlushConversationBuffer(sessionKey, agentType, accountId) {
7199
+ const session = sessionStore.get(sessionKey);
7200
+ if (!session) return null;
7201
+ if (session.conversationId) return session.conversationId;
7202
+ if (getPendingTurnCount(sessionKey) < 2) return null;
7203
+ if (session.flushInFlight) return session.flushInFlight;
7204
+ const attempt = (async () => {
7205
+ let conversationId = null;
7206
+ if (agentType === "admin") {
7207
+ const userId = session.userId;
7208
+ if (!userId) {
7209
+ console.error(`[conversation-gate] flush aborted: admin session missing userId sessionKey=${sessionKey.slice(0, 8)}\u2026`);
7210
+ return null;
7211
+ }
7212
+ conversationId = await createNewAdminConversation(userId, accountId, sessionKey);
7213
+ } else {
7214
+ conversationId = await ensureConversation(accountId, "public", sessionKey, void 0, void 0, void 0);
7215
+ }
7216
+ if (!conversationId) return null;
7217
+ session.conversationId = conversationId;
7218
+ const buffered = drainPendingTurns(sessionKey) ?? [];
7219
+ for (const turn of buffered) {
7220
+ persistMessage(conversationId, turn.role, turn.content, accountId, turn.tokens, turn.timestamp, turn.sender).catch((err) => {
7221
+ console.error(`[conversation-gate] replay persistMessage failed role=${turn.role}: ${err instanceof Error ? err.message : String(err)}`);
7222
+ });
7223
+ }
7224
+ console.log(`[conversation-gate] ${(/* @__PURE__ */ new Date()).toISOString()} flushed sessionKey=${sessionKey.slice(0, 8)} conversationId=${conversationId.slice(0, 8)} bufferedMessages=${buffered.length} agentType=${agentType}`);
7225
+ return conversationId;
7226
+ })();
7227
+ session.flushInFlight = attempt;
7228
+ try {
7229
+ return await attempt;
7230
+ } finally {
7231
+ if (session.flushInFlight === attempt) session.flushInFlight = void 0;
7232
+ }
7233
+ }
7234
+ function isDmChannelSessionKey(sessionKey) {
7235
+ return sessionKey.startsWith("whatsapp:") || sessionKey.startsWith("telegram:");
7236
+ }
7237
+ function preflushStreamLogKey(sessionKey) {
7238
+ return `preflush-${sessionKey.slice(0, 12)}`;
7239
+ }
7482
7240
  function getAgentNameForSession(sessionKey) {
7483
7241
  return sessionStore.get(sessionKey)?.agentName;
7484
7242
  }
@@ -7633,8 +7391,8 @@ function consumeStalledSubagents(sessionKey) {
7633
7391
  return stalls && stalls.length > 0 ? stalls : void 0;
7634
7392
  }
7635
7393
  function streamLogPathFor(accountId, conversationId) {
7636
- const logDir = resolve6(ACCOUNTS_DIR, accountId, "logs");
7637
- const streamLogPath = resolve6(logDir, `claude-agent-stream-${conversationId}.log`);
7394
+ const logDir = resolve5(ACCOUNTS_DIR, accountId, "logs");
7395
+ const streamLogPath = resolve5(logDir, `claude-agent-stream-${conversationId}.log`);
7638
7396
  return { logDir, streamLogPath };
7639
7397
  }
7640
7398
  function buildSpawnEnv(accountId, accountDir, conversationId) {
@@ -7655,7 +7413,7 @@ var cachedBrandHostname = null;
7655
7413
  function readBrandHostname() {
7656
7414
  if (cachedBrandHostname !== null) return cachedBrandHostname;
7657
7415
  try {
7658
- const brandPath = resolve6(PLATFORM_ROOT3, "config", "brand.json");
7416
+ const brandPath = resolve5(PLATFORM_ROOT3, "config", "brand.json");
7659
7417
  const parsed = JSON.parse(readFileSync6(brandPath, "utf-8"));
7660
7418
  cachedBrandHostname = typeof parsed.hostname === "string" && parsed.hostname.length > 0 ? parsed.hostname : "maxy";
7661
7419
  } catch {
@@ -7693,37 +7451,37 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
7693
7451
  const servers = {
7694
7452
  "memory": {
7695
7453
  command: "node",
7696
- args: [resolve6(PLATFORM_ROOT3, "plugins/memory/mcp/dist/index.js")],
7454
+ args: [resolve5(PLATFORM_ROOT3, "plugins/memory/mcp/dist/index.js")],
7697
7455
  env: { ...baseEnv, ...userId ? { USER_ID: userId } : {} }
7698
7456
  },
7699
7457
  "contacts": {
7700
7458
  command: "node",
7701
- args: [resolve6(PLATFORM_ROOT3, "plugins/contacts/mcp/dist/index.js")],
7459
+ args: [resolve5(PLATFORM_ROOT3, "plugins/contacts/mcp/dist/index.js")],
7702
7460
  env: { ...baseEnv }
7703
7461
  },
7704
7462
  "whatsapp": {
7705
7463
  command: "node",
7706
- args: [resolve6(PLATFORM_ROOT3, "plugins/whatsapp/mcp/dist/index.js")],
7464
+ args: [resolve5(PLATFORM_ROOT3, "plugins/whatsapp/mcp/dist/index.js")],
7707
7465
  env: { ...baseEnv, PLATFORM_PORT: process.env.PORT ?? "19200" }
7708
7466
  },
7709
7467
  "admin": {
7710
7468
  command: "node",
7711
- args: [resolve6(PLATFORM_ROOT3, "plugins/admin/mcp/dist/index.js")],
7469
+ args: [resolve5(PLATFORM_ROOT3, "plugins/admin/mcp/dist/index.js")],
7712
7470
  env: { ...baseEnv, PLATFORM_PORT: process.env.PORT ?? "19200", ...userId ? { USER_ID: userId } : {} }
7713
7471
  },
7714
7472
  "scheduling": {
7715
7473
  command: "node",
7716
- args: [resolve6(PLATFORM_ROOT3, "plugins/scheduling/mcp/dist/index.js")],
7474
+ args: [resolve5(PLATFORM_ROOT3, "plugins/scheduling/mcp/dist/index.js")],
7717
7475
  env: { ...baseEnv }
7718
7476
  },
7719
7477
  "tasks": {
7720
7478
  command: "node",
7721
- args: [resolve6(PLATFORM_ROOT3, "plugins/tasks/mcp/dist/index.js")],
7479
+ args: [resolve5(PLATFORM_ROOT3, "plugins/tasks/mcp/dist/index.js")],
7722
7480
  env: { ...baseEnv }
7723
7481
  },
7724
7482
  "email": {
7725
7483
  command: "node",
7726
- args: [resolve6(PLATFORM_ROOT3, "plugins/email/mcp/dist/index.js")],
7484
+ args: [resolve5(PLATFORM_ROOT3, "plugins/email/mcp/dist/index.js")],
7727
7485
  env: { ...baseEnv }
7728
7486
  },
7729
7487
  // Workflows MCP — persistent admin-session server for list/get/update/delete/
@@ -7734,7 +7492,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
7734
7492
  // ToolSearches fruitlessly before degrading to a task-create stand-in (Task 571).
7735
7493
  "workflows": {
7736
7494
  command: "node",
7737
- args: [resolve6(PLATFORM_ROOT3, "plugins/workflows/mcp/dist/index.js")],
7495
+ args: [resolve5(PLATFORM_ROOT3, "plugins/workflows/mcp/dist/index.js")],
7738
7496
  env: { ...baseEnv }
7739
7497
  },
7740
7498
  // Playwright MCP server — browser automation for browser-specialist.
@@ -7756,7 +7514,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
7756
7514
  // MAXY-PRD.md:627, not in any application-layer filter).
7757
7515
  "graph": {
7758
7516
  command: "node",
7759
- args: [resolve6(PLATFORM_ROOT3, "lib/graph-mcp/dist/index.js")],
7517
+ args: [resolve5(PLATFORM_ROOT3, "lib/graph-mcp/dist/index.js")],
7760
7518
  env: {
7761
7519
  ...baseEnv,
7762
7520
  BRAND: readBrandHostname(),
@@ -7772,7 +7530,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
7772
7530
  if (tgBotToken) {
7773
7531
  servers["telegram"] = {
7774
7532
  command: "node",
7775
- args: [resolve6(PLATFORM_ROOT3, "plugins/telegram/mcp/dist/index.js")],
7533
+ args: [resolve5(PLATFORM_ROOT3, "plugins/telegram/mcp/dist/index.js")],
7776
7534
  env: { ...baseEnv, TELEGRAM_BOT_TOKEN: tgBotToken }
7777
7535
  };
7778
7536
  } else {
@@ -7780,11 +7538,11 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
7780
7538
  }
7781
7539
  servers["cloudflare"] = {
7782
7540
  command: "node",
7783
- args: [resolve6(PLATFORM_ROOT3, "plugins/cloudflare/mcp/dist/index.js")],
7541
+ args: [resolve5(PLATFORM_ROOT3, "plugins/cloudflare/mcp/dist/index.js")],
7784
7542
  env: { ...baseEnv, PLATFORM_PORT: process.env.PORT ?? "19200" }
7785
7543
  };
7786
7544
  if (Array.isArray(enabledPlugins) && enabledPlugins.length > 0) {
7787
- const pluginsDir = resolve6(PLATFORM_ROOT3, "plugins");
7545
+ const pluginsDir = resolve5(PLATFORM_ROOT3, "plugins");
7788
7546
  let dirs;
7789
7547
  try {
7790
7548
  dirs = readdirSync2(pluginsDir);
@@ -7807,7 +7565,7 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
7807
7565
  continue;
7808
7566
  }
7809
7567
  }
7810
- const mcpEntry = resolve6(PLATFORM_ROOT3, "plugins", dir, "mcp/dist/index.js");
7568
+ const mcpEntry = resolve5(PLATFORM_ROOT3, "plugins", dir, "mcp/dist/index.js");
7811
7569
  if (!existsSync5(mcpEntry)) continue;
7812
7570
  servers[dir] = {
7813
7571
  command: "node",
@@ -7943,7 +7701,7 @@ var ADMIN_CORE_TOOLS = [
7943
7701
  function getAdminAllowedTools(enabledPlugins) {
7944
7702
  const tools = [...ADMIN_CORE_TOOLS];
7945
7703
  if (Array.isArray(enabledPlugins) && enabledPlugins.length > 0) {
7946
- const pluginsDir = resolve6(PLATFORM_ROOT3, "plugins");
7704
+ const pluginsDir = resolve5(PLATFORM_ROOT3, "plugins");
7947
7705
  let dirs;
7948
7706
  try {
7949
7707
  dirs = readdirSync2(pluginsDir);
@@ -8051,13 +7809,13 @@ ${message.slice(0, QUERY_CLASSIFIER_MSG_CAP)}`
8051
7809
  }
8052
7810
  }
8053
7811
  async function fetchMemoryContext(accountId, query, sessionKey, options) {
8054
- const serverPath = resolve6(PLATFORM_ROOT3, "plugins/memory/mcp/dist/index.js");
7812
+ const serverPath = resolve5(PLATFORM_ROOT3, "plugins/memory/mcp/dist/index.js");
8055
7813
  if (!existsSync5(serverPath)) {
8056
7814
  console.error(`[fetchMemoryContext] MCP server not found: ${serverPath}`);
8057
7815
  return null;
8058
7816
  }
8059
7817
  const startMs = Date.now();
8060
- return new Promise((resolve29) => {
7818
+ return new Promise((resolve28) => {
8061
7819
  const proc = spawn2(process.execPath, [serverPath], {
8062
7820
  env: {
8063
7821
  ...process.env,
@@ -8086,7 +7844,7 @@ async function fetchMemoryContext(accountId, query, sessionKey, options) {
8086
7844
  } else {
8087
7845
  console.error(`[fetchMemoryContext] failed: ${reason} (${elapsed}ms)${stderrBuf ? ` stderr: ${stderrBuf.slice(0, 500)}` : ""}`);
8088
7846
  }
8089
- resolve29(value);
7847
+ resolve28(value);
8090
7848
  };
8091
7849
  proc.stdout.on("data", (chunk) => {
8092
7850
  buffer += chunk.toString();
@@ -8149,7 +7907,7 @@ async function fetchMemoryContext(accountId, query, sessionKey, options) {
8149
7907
  }
8150
7908
  async function compactTrimmedMessages(accountId, trimmedMessages) {
8151
7909
  if (trimmedMessages.length === 0) return true;
8152
- const serverPath = resolve6(PLATFORM_ROOT3, "plugins/memory/mcp/dist/index.js");
7910
+ const serverPath = resolve5(PLATFORM_ROOT3, "plugins/memory/mcp/dist/index.js");
8153
7911
  if (!existsSync5(serverPath)) return false;
8154
7912
  const briefing = trimmedMessages.map((m) => `[${m.role.toUpperCase()}] ${m.content}`).join("\n\n");
8155
7913
  return new Promise((resolvePromise) => {
@@ -8467,7 +8225,7 @@ Then respond with only: [COMPACTED]`;
8467
8225
  var COMPACTION_TIMEOUT_MS = 45e3;
8468
8226
  async function* runCompactionTurn(accountDir, accountId, systemPrompt, resumeSessionId, adminModel, conversationId, enabledPlugins) {
8469
8227
  const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, conversationId, void 0, enabledPlugins) });
8470
- const specialistsDir = resolve6(accountDir, "specialists");
8228
+ const specialistsDir = resolve5(accountDir, "specialists");
8471
8229
  if (!existsSync5(specialistsDir)) agentLogStream("claude-agent-compaction-stream", accountDir, conversationId).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
8472
8230
  `);
8473
8231
  const args = [
@@ -8741,7 +8499,7 @@ async function* parseClaudeStream(proc, streamLog, adminModel, conversationId, a
8741
8499
  const { logDir } = streamLogPathFor(accountId, conversationId);
8742
8500
  const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
8743
8501
  for (const s of failed) {
8744
- const stderrPath = resolve6(logDir, `mcp-${s.name}-stderr-${date}.log`);
8502
+ const stderrPath = resolve5(logDir, `mcp-${s.name}-stderr-${date}.log`);
8745
8503
  let tail = "(no stderr file)";
8746
8504
  try {
8747
8505
  const stats = statSync3(stderrPath);
@@ -9371,20 +9129,24 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
9371
9129
  const userTimestamp = clientTimestamp ?? (/* @__PURE__ */ new Date()).toISOString();
9372
9130
  const resumeSessionId = sessionKey ? getAgentSessionId(sessionKey) : void 0;
9373
9131
  const spawnConvId = sessionKey ? getConversationIdForSession(sessionKey) : void 0;
9374
- if (!spawnConvId) {
9375
- throw new Error(`invokeAdminAgent: conversationId missing for sessionKey=${sessionKey?.slice(0, 8) ?? "none"} \u2014 ensureConversation must run before invoking the agent`);
9132
+ if (!spawnConvId && sessionKey && getPendingTurnCount(sessionKey) >= 2) {
9133
+ throw new Error(`invokeAdminAgent: conversationId missing post-flush for sessionKey=${sessionKey.slice(0, 8)} \u2014 maybeFlushConversationBuffer must bind it before invoking the agent`);
9134
+ }
9135
+ const spawnLogKey = spawnConvId ?? (sessionKey ? preflushStreamLogKey(sessionKey) : void 0);
9136
+ if (!spawnLogKey) {
9137
+ throw new Error(`invokeAdminAgent: sessionKey required \u2014 cannot resolve log stream without one`);
9376
9138
  }
9377
9139
  const cdpOk = await ensureCdp();
9378
9140
  if (!cdpOk) {
9379
- const cdpLog = agentLogStream("claude-agent-stream", accountDir, spawnConvId);
9141
+ const cdpLog = agentLogStream("claude-agent-stream", accountDir, spawnLogKey);
9380
9142
  cdpLog.write(`[${isoTs()}] [warn] ensureCdp failed \u2014 browser-specialist degraded
9381
9143
  `);
9382
9144
  cdpLog.end();
9383
9145
  }
9384
9146
  const ccUserId = sessionKey ? getUserIdForSession(sessionKey) : void 0;
9385
- const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, spawnConvId, ccUserId, enabledPlugins) });
9386
- const specialistsDir = resolve6(accountDir, "specialists");
9387
- if (!existsSync5(specialistsDir)) agentLogStream("claude-agent-stream", accountDir, spawnConvId).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
9147
+ const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, spawnLogKey, ccUserId, enabledPlugins) });
9148
+ const specialistsDir = resolve5(accountDir, "specialists");
9149
+ if (!existsSync5(specialistsDir)) agentLogStream("claude-agent-stream", accountDir, spawnLogKey).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
9388
9150
  `);
9389
9151
  const args = [
9390
9152
  "--print",
@@ -9415,19 +9177,19 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
9415
9177
  cwd: accountDir,
9416
9178
  stdio: ["ignore", "pipe", "pipe"],
9417
9179
  // Task 556: STREAM_LOG_PATH inherited by Bash-tool subprocesses.
9418
- env: buildSpawnEnv(accountId, accountDir, spawnConvId)
9180
+ env: buildSpawnEnv(accountId, accountDir, spawnLogKey)
9419
9181
  });
9420
- const stderrLog = agentLogStream("claude-agent-stderr", accountDir, spawnConvId);
9182
+ const stderrLog = agentLogStream("claude-agent-stderr", accountDir, spawnLogKey);
9421
9183
  stderrLog.on("error", () => {
9422
9184
  });
9423
9185
  proc.stderr?.pipe(stderrLog);
9424
- const streamLog = agentLogStream("claude-agent-stream", accountDir, spawnConvId);
9186
+ const streamLog = agentLogStream("claude-agent-stream", accountDir, spawnLogKey);
9425
9187
  streamLog.on("error", () => {
9426
9188
  });
9427
9189
  teeProcStderrToStreamLog(proc, streamLog);
9428
9190
  streamLog.write(`[${isoTs()}] [subproc-debug-unavailable] reason=bundled-bun-binary-ignores-node-debug pid=${proc.pid} cli=claude
9429
9191
  `);
9430
- streamLog.write(`[${isoTs()}] [spawn-env] STREAM_LOG_PATH=set pid=${proc.pid} conversationId=${spawnConvId} site=admin
9192
+ streamLog.write(`[${isoTs()}] [spawn-env] STREAM_LOG_PATH=set pid=${proc.pid} conversationId=${spawnConvId ?? "preflush"} logKey=${spawnLogKey} site=admin
9431
9193
  `);
9432
9194
  if (sessionKey) {
9433
9195
  const prev = activeProcesses.get(sessionKey);
@@ -9437,7 +9199,7 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
9437
9199
  }
9438
9200
  activeProcesses.set(sessionKey, { pid: proc.pid, spawnedAt: Date.now() });
9439
9201
  }
9440
- streamLog.write(`[${isoTs()}] [spawn] pid=${proc.pid} resume=${resumeSessionId ?? "none"} sessionKey=${sessionKey ?? "none"} conversationId=${spawnConvId} pluginDir=${specialistsDir}
9202
+ streamLog.write(`[${isoTs()}] [spawn] pid=${proc.pid} resume=${resumeSessionId ?? "none"} sessionKey=${sessionKey ?? "none"} conversationId=${spawnConvId ?? "preflush"} logKey=${spawnLogKey} pluginDir=${specialistsDir}
9441
9203
  `);
9442
9204
  streamLog.write(`[${isoTs()}] [stdin] len=${fullMessage.length} preview=${JSON.stringify(fullMessage.slice(0, 80))}
9443
9205
  `);
@@ -9509,7 +9271,7 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
9509
9271
  }
9510
9272
  if (event.type === "usage" && sessionKey && currentAgentSessionId) {
9511
9273
  const peakReqPct = event.peak_request_pct ?? 0;
9512
- if (peakReqPct >= COMPACTION_THRESHOLD) {
9274
+ if (peakReqPct >= COMPACTION_THRESHOLD && spawnConvId) {
9513
9275
  const compactionIter = runCompactionTurn(accountDir, accountId, systemPrompt, currentAgentSessionId, adminModel, spawnConvId, enabledPlugins);
9514
9276
  let step = await compactionIter.next();
9515
9277
  while (!step.done) {
@@ -9619,9 +9381,9 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
9619
9381
  } else {
9620
9382
  gotDone = true;
9621
9383
  if (!sessionWasReset) {
9384
+ const assistantTimestamp = (/* @__PURE__ */ new Date()).toISOString();
9622
9385
  const convId = sessionKey ? sessionStore.get(sessionKey)?.conversationId : void 0;
9623
9386
  if (convId) {
9624
- const assistantTimestamp = (/* @__PURE__ */ new Date()).toISOString();
9625
9387
  persistMessage(convId, "user", fullMessage, accountId, void 0, userTimestamp).catch(() => {
9626
9388
  });
9627
9389
  autoLabelSession(convId, fullMessage).catch(() => {
@@ -9629,7 +9391,14 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
9629
9391
  if (responseText) persistMessage(convId, "assistant", responseText, accountId, capturedTokens, assistantTimestamp).catch(() => {
9630
9392
  });
9631
9393
  } else if (sessionKey) {
9632
- console.warn(`[persist] skipped: no conversationId for sessionKey=${sessionKey.slice(0, 12)}\u2026 (session registered=${sessionStore.has(sessionKey)})`);
9394
+ bufferPendingTurn(sessionKey, { role: "user", content: fullMessage, timestamp: userTimestamp });
9395
+ if (responseText) bufferPendingTurn(sessionKey, { role: "assistant", content: responseText, timestamp: assistantTimestamp, tokens: capturedTokens });
9396
+ const flushedId = await maybeFlushConversationBuffer(sessionKey, "admin", accountId);
9397
+ if (flushedId) {
9398
+ autoLabelSession(flushedId, fullMessage).catch(() => {
9399
+ });
9400
+ yield { type: "conversation_attributed", conversationId: flushedId };
9401
+ }
9633
9402
  }
9634
9403
  if (sessionKey) {
9635
9404
  const commitSession = sessionStore.get(sessionKey);
@@ -9692,9 +9461,10 @@ ${summary}`;
9692
9461
  async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accountId, adminModel, sessionKey, maxTurns = 20, attachments = [], retryCount = 0, enabledPlugins, clientTimestamp, adherenceConstraints, agentName) {
9693
9462
  const userTimestamp = clientTimestamp ?? (/* @__PURE__ */ new Date()).toISOString();
9694
9463
  const managedConvId = getConversationIdForSession(sessionKey);
9695
- if (!managedConvId) {
9696
- throw new Error(`invokeManagedAdminAgent: conversationId missing for sessionKey=${sessionKey.slice(0, 8)} \u2014 ensureConversation must run first`);
9464
+ if (!managedConvId && getPendingTurnCount(sessionKey) >= 2) {
9465
+ throw new Error(`invokeManagedAdminAgent: conversationId missing post-flush for sessionKey=${sessionKey.slice(0, 8)} \u2014 maybeFlushConversationBuffer must bind it first`);
9697
9466
  }
9467
+ const managedLogKey = managedConvId ?? preflushStreamLogKey(sessionKey);
9698
9468
  const pendingTrimmed = consumePendingTrimmedMessages(sessionKey);
9699
9469
  if (pendingTrimmed && pendingTrimmed.length > 0) {
9700
9470
  const ok = await compactTrimmedMessages(accountId, pendingTrimmed);
@@ -9702,7 +9472,7 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
9702
9472
  storePendingTrimmedMessages(sessionKey, pendingTrimmed);
9703
9473
  }
9704
9474
  }
9705
- const streamLog = agentLogStream("claude-agent-stream", accountDir, managedConvId);
9475
+ const streamLog = agentLogStream("claude-agent-stream", accountDir, managedLogKey);
9706
9476
  streamLog.on("error", () => {
9707
9477
  });
9708
9478
  const systemPromptTokens = estimateTokens(systemPrompt);
@@ -9733,8 +9503,8 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
9733
9503
  if (!cdpOk) streamLog.write(`[${isoTs()}] [warn] ensureCdp failed \u2014 browser-specialist degraded
9734
9504
  `);
9735
9505
  const managedUserId = getUserIdForSession(sessionKey);
9736
- const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, managedConvId, managedUserId, enabledPlugins) });
9737
- const specialistsDir = resolve6(accountDir, "specialists");
9506
+ const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, managedLogKey, managedUserId, enabledPlugins) });
9507
+ const specialistsDir = resolve5(accountDir, "specialists");
9738
9508
  if (!existsSync5(specialistsDir)) streamLog.write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
9739
9509
  `);
9740
9510
  const fullMessage = attachments.length > 0 ? message + buildAttachmentMetaText(attachments) : message;
@@ -9764,16 +9534,16 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
9764
9534
  cwd: accountDir,
9765
9535
  stdio: ["ignore", "pipe", "pipe"],
9766
9536
  // Task 556: STREAM_LOG_PATH inherited by Bash-tool subprocesses.
9767
- env: buildSpawnEnv(accountId, accountDir, managedConvId)
9537
+ env: buildSpawnEnv(accountId, accountDir, managedLogKey)
9768
9538
  });
9769
- const stderrLog = agentLogStream("claude-agent-stderr", accountDir, managedConvId);
9539
+ const stderrLog = agentLogStream("claude-agent-stderr", accountDir, managedLogKey);
9770
9540
  stderrLog.on("error", () => {
9771
9541
  });
9772
9542
  proc.stderr?.pipe(stderrLog);
9773
9543
  teeProcStderrToStreamLog(proc, streamLog);
9774
9544
  streamLog.write(`[${isoTs()}] [subproc-debug-unavailable] reason=bundled-bun-binary-ignores-node-debug pid=${proc.pid} cli=claude
9775
9545
  `);
9776
- streamLog.write(`[${isoTs()}] [spawn-env] STREAM_LOG_PATH=set pid=${proc.pid} conversationId=${managedConvId} site=managed
9546
+ streamLog.write(`[${isoTs()}] [spawn-env] STREAM_LOG_PATH=set pid=${proc.pid} conversationId=${managedConvId ?? "preflush"} logKey=${managedLogKey} site=managed
9777
9547
  `);
9778
9548
  if (sessionKey) {
9779
9549
  const prev = activeProcesses.get(sessionKey);
@@ -9783,13 +9553,13 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
9783
9553
  }
9784
9554
  activeProcesses.set(sessionKey, { pid: proc.pid, spawnedAt: Date.now() });
9785
9555
  }
9786
- streamLog.write(`[${isoTs()}] [managed-spawn] pid=${proc.pid} sessionKey=${sessionKey} conversationId=${managedConvId} historyMessages=${history.length} pluginDir=${specialistsDir}
9556
+ streamLog.write(`[${isoTs()}] [managed-spawn] pid=${proc.pid} sessionKey=${sessionKey} conversationId=${managedConvId ?? "preflush"} logKey=${managedLogKey} historyMessages=${history.length} pluginDir=${specialistsDir}
9787
9557
  `);
9788
9558
  streamLog.write(`[${isoTs()}] [stdin] len=${fullMessage.length} preview=${JSON.stringify(fullMessage.slice(0, 80))}
9789
9559
  `);
9790
9560
  proc.on("exit", (code, signal) => {
9791
- console.log(`[process-exit] pid=${proc.pid} code=${code} signal=${signal} sessionKey=${sessionKey ?? "none"} conversationId=${managedConvId}`);
9792
- if (!streamLog.destroyed && !streamLog.writableEnded) streamLog.write(`[${isoTs()}] [process-exit] pid=${proc.pid} code=${code} signal=${signal} conversationId=${managedConvId}
9561
+ console.log(`[process-exit] pid=${proc.pid} code=${code} signal=${signal} sessionKey=${sessionKey ?? "none"} conversationId=${managedConvId ?? "preflush"}`);
9562
+ if (!streamLog.destroyed && !streamLog.writableEnded) streamLog.write(`[${isoTs()}] [process-exit] pid=${proc.pid} code=${code} signal=${signal} conversationId=${managedConvId ?? "preflush"}
9793
9563
  `);
9794
9564
  if (sessionKey) activeProcesses.delete(sessionKey);
9795
9565
  });
@@ -9939,17 +9709,24 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
9939
9709
  `);
9940
9710
  const successSession = sessionStore.get(sessionKey);
9941
9711
  if (successSession) successSession.lastPeakContextPct = peakContextPct;
9712
+ const assistantTimestamp = (/* @__PURE__ */ new Date()).toISOString();
9942
9713
  const convId = sessionStore.get(sessionKey)?.conversationId;
9943
9714
  if (convId) {
9944
- const assistantTimestamp = (/* @__PURE__ */ new Date()).toISOString();
9945
9715
  persistMessage(convId, "user", fullMessage, accountId, void 0, userTimestamp).catch(() => {
9946
9716
  });
9947
9717
  autoLabelSession(convId, fullMessage).catch(() => {
9948
9718
  });
9949
9719
  if (responseText) persistMessage(convId, "assistant", responseText, accountId, capturedTokens, assistantTimestamp).catch(() => {
9950
9720
  });
9951
- } else if (sessionKey) {
9952
- console.warn(`[persist] skipped: no conversationId for sessionKey=${sessionKey.slice(0, 12)}\u2026 (session registered=${sessionStore.has(sessionKey)})`);
9721
+ } else {
9722
+ bufferPendingTurn(sessionKey, { role: "user", content: fullMessage, timestamp: userTimestamp });
9723
+ if (responseText) bufferPendingTurn(sessionKey, { role: "assistant", content: responseText, timestamp: assistantTimestamp, tokens: capturedTokens });
9724
+ const flushedId = await maybeFlushConversationBuffer(sessionKey, "admin", accountId);
9725
+ if (flushedId) {
9726
+ autoLabelSession(flushedId, fullMessage).catch(() => {
9727
+ });
9728
+ yield { type: "conversation_attributed", conversationId: flushedId };
9729
+ }
9953
9730
  }
9954
9731
  const commitSession = sessionStore.get(sessionKey);
9955
9732
  if (commitSession?.pendingCommitmentOffers && commitSession.pendingCommitmentOffers.length > 0) {
@@ -10021,10 +9798,14 @@ async function* invokePublicAgent(message, systemPrompt, accountId, accountDir,
10021
9798
  return;
10022
9799
  }
10023
9800
  const publicConvId = sessionKey ? getConversationIdForSession(sessionKey) : void 0;
10024
- if (!publicConvId) {
10025
- throw new Error(`invokePublicAgent: conversationId missing for sessionKey=${sessionKey?.slice(0, 8) ?? "none"} \u2014 ensureConversation must run first`);
9801
+ if (!publicConvId && sessionKey && getPendingTurnCount(sessionKey) >= 2) {
9802
+ throw new Error(`invokePublicAgent: conversationId missing post-flush for sessionKey=${sessionKey.slice(0, 8)} \u2014 maybeFlushConversationBuffer must bind it first`);
10026
9803
  }
10027
- const streamLog = agentLogStream("public-agent-stream", accountDir, publicConvId);
9804
+ const publicLogKey = publicConvId ?? (sessionKey ? preflushStreamLogKey(sessionKey) : void 0);
9805
+ if (!publicLogKey) {
9806
+ throw new Error(`invokePublicAgent: sessionKey required \u2014 cannot resolve log stream without one`);
9807
+ }
9808
+ const streamLog = agentLogStream("public-agent-stream", accountDir, publicLogKey);
10028
9809
  streamLog.write(`[${isoTs()}] [public-user-message] ${JSON.stringify(message)}
10029
9810
  `);
10030
9811
  if (sessionKey) {
@@ -10261,10 +10042,10 @@ User messages are prefixed with the sender's name in brackets. Address participa
10261
10042
  `);
10262
10043
  }
10263
10044
  const conversationId = sessionKey ? sessionStore.get(sessionKey)?.conversationId : void 0;
10045
+ const assistantTimestamp = (/* @__PURE__ */ new Date()).toISOString();
10046
+ const sess = sessionKey ? sessionStore.get(sessionKey) : void 0;
10047
+ const sender = sess?.groupSlug && sess.visitorId && sess.senderDisplayName ? { visitorId: sess.visitorId, displayName: sess.senderDisplayName } : void 0;
10264
10048
  if (conversationId) {
10265
- const assistantTimestamp = (/* @__PURE__ */ new Date()).toISOString();
10266
- const sess = sessionKey ? sessionStore.get(sessionKey) : void 0;
10267
- const sender = sess?.groupSlug && sess.visitorId && sess.senderDisplayName ? { visitorId: sess.visitorId, displayName: sess.senderDisplayName } : void 0;
10268
10049
  persistMessage(conversationId, "user", message, accountId, void 0, userTimestamp, sender).catch(() => {
10269
10050
  });
10270
10051
  autoLabelSession(conversationId, message).catch(() => {
@@ -10272,7 +10053,14 @@ User messages are prefixed with the sender's name in brackets. Address participa
10272
10053
  if (fullText) persistMessage(conversationId, "assistant", fullText, accountId, void 0, assistantTimestamp).catch(() => {
10273
10054
  });
10274
10055
  } else if (sessionKey) {
10275
- console.warn(`[persist] skipped: no conversationId for sessionKey=${sessionKey.slice(0, 12)}\u2026 (session registered=${sessionStore.has(sessionKey)})`);
10056
+ bufferPendingTurn(sessionKey, { role: "user", content: message, timestamp: userTimestamp, sender });
10057
+ if (fullText) bufferPendingTurn(sessionKey, { role: "assistant", content: fullText, timestamp: assistantTimestamp });
10058
+ const flushedId = await maybeFlushConversationBuffer(sessionKey, "public", accountId);
10059
+ if (flushedId) {
10060
+ autoLabelSession(flushedId, message).catch(() => {
10061
+ });
10062
+ yield { type: "conversation_attributed", conversationId: flushedId };
10063
+ }
10276
10064
  }
10277
10065
  streamLog.end();
10278
10066
  }
@@ -10428,7 +10216,7 @@ ${sessionContext}`;
10428
10216
  console.log(`[onboarding-inject] accountId=${accountId.slice(0, 8)}\u2026 error=neo4j-unreachable injected=false`);
10429
10217
  } else if (onboardingStep < 8) {
10430
10218
  const GENERIC_FALLBACK = "At every session start, call `onboarding-get`. If `currentStep` is less than 8, load the onboarding skill via `plugin-read` (find its path in the manifest under `admin`) and follow it \u2014 before any business setup. If `onboarding-get` fails (Neo4j unreachable), tell the user and skip onboarding for this session \u2014 it resumes automatically when the graph is available.";
10431
- const skillPath = resolve6(PLATFORM_ROOT3, "plugins/admin/skills/onboarding/SKILL.md");
10219
+ const skillPath = resolve5(PLATFORM_ROOT3, "plugins/admin/skills/onboarding/SKILL.md");
10432
10220
  let skillContent = "";
10433
10221
  try {
10434
10222
  skillContent = readFileSync6(skillPath, "utf-8");
@@ -10481,7 +10269,7 @@ ${body}`;
10481
10269
 
10482
10270
  ${manifest}`;
10483
10271
  }
10484
- const graphRefPath = resolve6(PLATFORM_ROOT3, "plugins/memory/references/graph-primitives.md");
10272
+ const graphRefPath = resolve5(PLATFORM_ROOT3, "plugins/memory/references/graph-primitives.md");
10485
10273
  try {
10486
10274
  const graphRef = readFileSync6(graphRefPath, "utf-8");
10487
10275
  baseSystemPrompt += `
@@ -10569,7 +10357,7 @@ Current session key: ${sessionKey}` : systemPromptBase;
10569
10357
 
10570
10358
  ${gwParts.join("\n")}`;
10571
10359
  }
10572
- if (sessionKey) {
10360
+ if (sessionKey && isDmChannelSessionKey(sessionKey)) {
10573
10361
  try {
10574
10362
  await ensureConversation(accountId, agentType, sessionKey, void 0, void 0, sessionUserId);
10575
10363
  } catch (err) {
@@ -10699,7 +10487,7 @@ var clientIpMiddleware = async (c, next) => {
10699
10487
 
10700
10488
  // server/routes/health.ts
10701
10489
  import { existsSync as existsSync10, readFileSync as readFileSync11 } from "fs";
10702
- import { createConnection as createConnection3 } from "net";
10490
+ import { createConnection as createConnection2 } from "net";
10703
10491
 
10704
10492
  // app/lib/network.ts
10705
10493
  import { networkInterfaces } from "os";
@@ -10723,8 +10511,8 @@ function getLanIp() {
10723
10511
  import { basename as basename2 } from "path";
10724
10512
 
10725
10513
  // app/lib/review-detector/rules.ts
10726
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync6, statSync as statSync4, mkdirSync as mkdirSync6, renameSync as renameSync2 } from "fs";
10727
- import { resolve as resolve7, dirname as dirname3 } from "path";
10514
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, existsSync as existsSync6, statSync as statSync4, mkdirSync as mkdirSync5, renameSync as renameSync2 } from "fs";
10515
+ import { resolve as resolve6, dirname as dirname3 } from "path";
10728
10516
  var DEFAULT_SCAN_INTERVAL_MS = 5e3;
10729
10517
  var RATE_LIMIT_PATTERN = "rate[- ]?limit(?:ed| reached| hit)|(?:HTTP|status)[^a-z]{0,3}429|too many requests";
10730
10518
  var RATE_LIMIT_PATTERN_V1 = "\\b429\\b|rate.?limit|too.?many.?requests";
@@ -11153,12 +10941,12 @@ function defaultRules() {
11153
10941
  ];
11154
10942
  }
11155
10943
  function rulesFilePath(configDir2) {
11156
- return resolve7(configDir2, "review-rules.json");
10944
+ return resolve6(configDir2, "review-rules.json");
11157
10945
  }
11158
10946
  function ensureRulesFile(configDir2) {
11159
10947
  const path2 = rulesFilePath(configDir2);
11160
10948
  if (existsSync6(path2)) return { created: false, path: path2 };
11161
- mkdirSync6(dirname3(path2), { recursive: true });
10949
+ mkdirSync5(dirname3(path2), { recursive: true });
11162
10950
  const body = {
11163
10951
  scanIntervalMs: DEFAULT_SCAN_INTERVAL_MS,
11164
10952
  rules: defaultRules()
@@ -11336,10 +11124,10 @@ function validateRule(input, label, seenIds) {
11336
11124
  }
11337
11125
 
11338
11126
  // app/lib/review-detector/sources.ts
11339
- import { existsSync as existsSync7, readdirSync as readdirSync3, statSync as statSync5, writeFileSync as writeFileSync7, renameSync as renameSync3, mkdirSync as mkdirSync7, openSync as openSync3, readSync as readSync3, closeSync as closeSync3, readFileSync as readFileSync8 } from "fs";
11340
- import { resolve as resolve8, join as join4, basename, dirname as dirname4 } from "path";
11127
+ import { existsSync as existsSync7, readdirSync as readdirSync3, statSync as statSync5, writeFileSync as writeFileSync7, renameSync as renameSync3, mkdirSync as mkdirSync6, openSync as openSync3, readSync as readSync3, closeSync as closeSync3, readFileSync as readFileSync8 } from "fs";
11128
+ import { resolve as resolve7, join as join4, basename, dirname as dirname4 } from "path";
11341
11129
  function tailStatePath(configDir2) {
11342
- return resolve8(configDir2, "review-state.json");
11130
+ return resolve7(configDir2, "review-state.json");
11343
11131
  }
11344
11132
  function loadTailState(configDir2) {
11345
11133
  const path2 = tailStatePath(configDir2);
@@ -11363,25 +11151,25 @@ function loadTailState(configDir2) {
11363
11151
  }
11364
11152
  function saveTailState(configDir2, state) {
11365
11153
  const path2 = tailStatePath(configDir2);
11366
- mkdirSync7(dirname4(path2), { recursive: true });
11154
+ mkdirSync6(dirname4(path2), { recursive: true });
11367
11155
  const tmp = `${path2}.tmp.${process.pid}.${Date.now()}`;
11368
11156
  writeFileSync7(tmp, JSON.stringify(state, null, 2) + "\n", "utf-8");
11369
11157
  renameSync3(tmp, path2);
11370
11158
  }
11371
11159
  function discoverSourceFiles(configDir2, accountLogDir2, logicalSource) {
11372
11160
  if (logicalSource === "server") {
11373
- const p = resolve8(configDir2, "logs", "server.log");
11161
+ const p = resolve7(configDir2, "logs", "server.log");
11374
11162
  return existsSync7(p) ? [{ logicalSource: "server", filepath: p }] : [];
11375
11163
  }
11376
11164
  if (logicalSource === "vnc") {
11377
- const p = resolve8(configDir2, "logs", "vnc-boot.log");
11165
+ const p = resolve7(configDir2, "logs", "vnc-boot.log");
11378
11166
  return existsSync7(p) ? [{ logicalSource: "vnc", filepath: p }] : [];
11379
11167
  }
11380
11168
  if (logicalSource === "cloudflared") {
11381
11169
  const files2 = [];
11382
- const daemon = resolve8(configDir2, "logs", "cloudflared.log");
11170
+ const daemon = resolve7(configDir2, "logs", "cloudflared.log");
11383
11171
  if (existsSync7(daemon)) files2.push({ logicalSource: "cloudflared", filepath: daemon });
11384
- const login = resolve8(configDir2, "logs", "cloudflared-login.log");
11172
+ const login = resolve7(configDir2, "logs", "cloudflared-login.log");
11385
11173
  if (existsSync7(login)) files2.push({ logicalSource: "cloudflared", filepath: login });
11386
11174
  return files2;
11387
11175
  }
@@ -11508,31 +11296,31 @@ function fileLastWriteMs(path2) {
11508
11296
  }
11509
11297
  }
11510
11298
  function accountLogDir(accountDir) {
11511
- return resolve8(accountDir, "logs");
11299
+ return resolve7(accountDir, "logs");
11512
11300
  }
11513
11301
  function sourceKey(file) {
11514
11302
  return `${file.logicalSource}:${basename(file.filepath)}`;
11515
11303
  }
11516
11304
 
11517
11305
  // app/lib/review-detector/writer.ts
11518
- import { appendFileSync as appendFileSync3, existsSync as existsSync8, mkdirSync as mkdirSync8, readFileSync as readFileSync9, writeFileSync as writeFileSync8, renameSync as renameSync4, statSync as statSync6 } from "fs";
11519
- import { resolve as resolve9, dirname as dirname5 } from "path";
11306
+ import { appendFileSync as appendFileSync2, existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync9, writeFileSync as writeFileSync8, renameSync as renameSync4, statSync as statSync6 } from "fs";
11307
+ import { resolve as resolve8, dirname as dirname5 } from "path";
11520
11308
  import { randomUUID as randomUUID3 } from "crypto";
11521
11309
  function reviewLogPath(configDir2) {
11522
- return resolve9(configDir2, "logs", "review.log");
11310
+ return resolve8(configDir2, "logs", "review.log");
11523
11311
  }
11524
11312
  function pendingAlertsPath(configDir2) {
11525
- return resolve9(configDir2, "review-pending-alerts.jsonl");
11313
+ return resolve8(configDir2, "review-pending-alerts.jsonl");
11526
11314
  }
11527
11315
  function reviewLog(configDir2, event) {
11528
11316
  const path2 = reviewLogPath(configDir2);
11529
11317
  try {
11530
- mkdirSync8(dirname5(path2), { recursive: true });
11318
+ mkdirSync7(dirname5(path2), { recursive: true });
11531
11319
  const line = `${new Date(
11532
11320
  typeof event.ts === "number" ? event.ts : Date.now()
11533
11321
  ).toISOString()} [review] ${JSON.stringify(event)}
11534
11322
  `;
11535
- appendFileSync3(path2, line, "utf-8");
11323
+ appendFileSync2(path2, line, "utf-8");
11536
11324
  } catch (err) {
11537
11325
  console.error(`[review] failed to write review log at ${path2}: ${err instanceof Error ? err.message : String(err)}`);
11538
11326
  }
@@ -11644,9 +11432,9 @@ async function upsertReviewAlert(accountId, match2) {
11644
11432
  function queueAlert(configDir2, accountId, match2) {
11645
11433
  const path2 = pendingAlertsPath(configDir2);
11646
11434
  try {
11647
- mkdirSync8(dirname5(path2), { recursive: true });
11435
+ mkdirSync7(dirname5(path2), { recursive: true });
11648
11436
  const line = JSON.stringify({ accountId, match: match2 }) + "\n";
11649
- appendFileSync3(path2, line, "utf-8");
11437
+ appendFileSync2(path2, line, "utf-8");
11650
11438
  } catch (err) {
11651
11439
  console.error(`[review] failed to queue alert at ${path2}: ${err instanceof Error ? err.message : String(err)}`);
11652
11440
  }
@@ -11777,7 +11565,7 @@ async function bootDetector() {
11777
11565
  }
11778
11566
 
11779
11567
  // app/lib/review-detector/scan-loop.ts
11780
- import { resolve as resolve10 } from "path";
11568
+ import { resolve as resolve9 } from "path";
11781
11569
 
11782
11570
  // app/lib/review-detector/evaluator.ts
11783
11571
  var SAMPLE_MAX_CHARS = 500;
@@ -12071,14 +11859,14 @@ async function runScanCycle(runtime) {
12071
11859
  match2 = result.match;
12072
11860
  }
12073
11861
  } else if (rule.type === "file-write-storm") {
12074
- const dir = resolve10(runtime.configDir, rule.watchPath ?? "");
11862
+ const dir = resolve9(runtime.configDir, rule.watchPath ?? "");
12075
11863
  const sinceMs = cycleStart - rule.thresholdWindowMinutes * 6e4;
12076
11864
  const count = countRecentWrites(dir, sinceMs);
12077
11865
  const result = evaluateFileWriteStormRule(rule, count, state, cycleStart);
12078
11866
  state = result.state;
12079
11867
  match2 = result.match;
12080
11868
  } else if (rule.type === "stale-log") {
12081
- const trackedPath = resolve10(runtime.configDir, rule.watchPath ?? "");
11869
+ const trackedPath = resolve9(runtime.configDir, rule.watchPath ?? "");
12082
11870
  const lastMs = fileLastWriteMs(trackedPath);
12083
11871
  const result = evaluateStaleLogRule(rule, lastMs, state, cycleStart);
12084
11872
  state = result.state;
@@ -12318,10 +12106,10 @@ var WhatsAppConfigSchema = z.object({
12318
12106
 
12319
12107
  // app/lib/whatsapp/config-persist.ts
12320
12108
  import { readFileSync as readFileSync10, writeFileSync as writeFileSync9, existsSync as existsSync9 } from "fs";
12321
- import { resolve as resolve11, join as join5 } from "path";
12109
+ import { resolve as resolve10, join as join5 } from "path";
12322
12110
  var TAG3 = "[whatsapp:config]";
12323
12111
  function configPath(accountDir) {
12324
- return resolve11(accountDir, "account.json");
12112
+ return resolve10(accountDir, "account.json");
12325
12113
  }
12326
12114
  function readConfig(accountDir) {
12327
12115
  const path2 = configPath(accountDir);
@@ -12776,7 +12564,7 @@ var credsSaveQueue = Promise.resolve();
12776
12564
  async function drainCredsSaveQueue(timeoutMs = 5e3) {
12777
12565
  console.error(`${TAG5} draining credential save queue\u2026`);
12778
12566
  const timer = new Promise(
12779
- (resolve29) => setTimeout(() => resolve29("timeout"), timeoutMs)
12567
+ (resolve28) => setTimeout(() => resolve28("timeout"), timeoutMs)
12780
12568
  );
12781
12569
  const result = await Promise.race([
12782
12570
  credsSaveQueue.then(() => "drained"),
@@ -12904,11 +12692,11 @@ async function createWaSocket(opts) {
12904
12692
  return sock;
12905
12693
  }
12906
12694
  async function waitForConnection(sock) {
12907
- return new Promise((resolve29, reject) => {
12695
+ return new Promise((resolve28, reject) => {
12908
12696
  const handler = (update) => {
12909
12697
  if (update.connection === "open") {
12910
12698
  sock.ev.off("connection.update", handler);
12911
- resolve29();
12699
+ resolve28();
12912
12700
  }
12913
12701
  if (update.connection === "close") {
12914
12702
  sock.ev.off("connection.update", handler);
@@ -13022,14 +12810,14 @@ ${inspected}`;
13022
12810
  return inspect2(err, INSPECT_OPTS2);
13023
12811
  }
13024
12812
  function withTimeout(label, promise, timeoutMs) {
13025
- return new Promise((resolve29, reject) => {
12813
+ return new Promise((resolve28, reject) => {
13026
12814
  const timer = setTimeout(() => {
13027
12815
  reject(new Error(`${label} timed out after ${timeoutMs}ms`));
13028
12816
  }, timeoutMs);
13029
12817
  promise.then(
13030
12818
  (value) => {
13031
12819
  clearTimeout(timer);
13032
- resolve29(value);
12820
+ resolve28(value);
13033
12821
  },
13034
12822
  (err) => {
13035
12823
  clearTimeout(timer);
@@ -13971,6 +13759,14 @@ function deriveSessionKey(input) {
13971
13759
  if (input.isOwnerMirror || input.agentType === "admin") {
13972
13760
  return `whatsapp:${input.accountId}`;
13973
13761
  }
13762
+ if (input.isGroup) {
13763
+ if (!input.groupJid) {
13764
+ throw new Error(
13765
+ `deriveSessionKey: isGroup=true requires groupJid (accountId=${input.accountId}, senderPhone=${input.senderPhone})`
13766
+ );
13767
+ }
13768
+ return `whatsapp:${input.accountId}:group:${input.groupJid}`;
13769
+ }
13974
13770
  return `whatsapp:${input.accountId}:${input.senderPhone}`;
13975
13771
  }
13976
13772
  async function init(opts) {
@@ -14235,11 +14031,11 @@ async function connectWithReconnect(conn) {
14235
14031
  console.error(
14236
14032
  `${TAG13} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
14237
14033
  );
14238
- await new Promise((resolve29) => {
14239
- const timer = setTimeout(resolve29, delay);
14034
+ await new Promise((resolve28) => {
14035
+ const timer = setTimeout(resolve28, delay);
14240
14036
  conn.abortController.signal.addEventListener("abort", () => {
14241
14037
  clearTimeout(timer);
14242
- resolve29();
14038
+ resolve28();
14243
14039
  }, { once: true });
14244
14040
  });
14245
14041
  }
@@ -14247,16 +14043,16 @@ async function connectWithReconnect(conn) {
14247
14043
  }
14248
14044
  }
14249
14045
  function waitForDisconnectEvent(conn) {
14250
- return new Promise((resolve29) => {
14046
+ return new Promise((resolve28) => {
14251
14047
  if (!conn.sock) {
14252
- resolve29();
14048
+ resolve28();
14253
14049
  return;
14254
14050
  }
14255
14051
  const sock = conn.sock;
14256
14052
  const handler = (update) => {
14257
14053
  if (update.connection === "close") {
14258
14054
  sock.ev.off("connection.update", handler);
14259
- resolve29();
14055
+ resolve28();
14260
14056
  }
14261
14057
  };
14262
14058
  sock.ev.on("connection.update", handler);
@@ -14442,6 +14238,8 @@ async function handleInboundMessage(conn, msg) {
14442
14238
  agentType: "admin",
14443
14239
  accountId: conn.accountId,
14444
14240
  senderPhone: senderPhone2,
14241
+ isGroup: isGroup2,
14242
+ groupJid: isGroup2 ? remoteJid : void 0,
14445
14243
  isOwnerMirror: true
14446
14244
  }),
14447
14245
  isOwnerMirror: true
@@ -14471,8 +14269,8 @@ async function handleInboundMessage(conn, msg) {
14471
14269
  const conversationKey = isGroup ? remoteJid : senderPhone;
14472
14270
  const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
14473
14271
  let resolvePending;
14474
- const sttPending = new Promise((resolve29) => {
14475
- resolvePending = resolve29;
14272
+ const sttPending = new Promise((resolve28) => {
14273
+ resolvePending = resolve28;
14476
14274
  });
14477
14275
  if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
14478
14276
  try {
@@ -14525,7 +14323,9 @@ async function handleInboundMessage(conn, msg) {
14525
14323
  const sessionKey = deriveSessionKey({
14526
14324
  agentType: accessResult.agentType,
14527
14325
  accountId: conn.accountId,
14528
- senderPhone
14326
+ senderPhone,
14327
+ isGroup,
14328
+ groupJid: isGroup ? remoteJid : void 0
14529
14329
  });
14530
14330
  conn.lastMessageAt = Date.now();
14531
14331
  const payload = {
@@ -14583,37 +14383,23 @@ async function probeApiKey() {
14583
14383
  return result.status;
14584
14384
  }
14585
14385
  function checkPort(port2, timeoutMs = 500) {
14586
- return new Promise((resolve29) => {
14587
- const socket = createConnection3(port2, "127.0.0.1");
14386
+ return new Promise((resolve28) => {
14387
+ const socket = createConnection2(port2, "127.0.0.1");
14588
14388
  socket.setTimeout(timeoutMs);
14589
14389
  socket.once("connect", () => {
14590
14390
  socket.destroy();
14591
- resolve29(true);
14391
+ resolve28(true);
14592
14392
  });
14593
14393
  socket.once("error", () => {
14594
14394
  socket.destroy();
14595
- resolve29(false);
14395
+ resolve28(false);
14596
14396
  });
14597
14397
  socket.once("timeout", () => {
14598
14398
  socket.destroy();
14599
- resolve29(false);
14399
+ resolve28(false);
14600
14400
  });
14601
14401
  });
14602
14402
  }
14603
- var TTYD_PROBE_CACHE_MS = 2e3;
14604
- var TTYD_PROBE_TIMEOUT_MS = 200;
14605
- var cachedTtydReady = null;
14606
- async function probeTerminalReady() {
14607
- if (cachedTtydReady && Date.now() - cachedTtydReady.checkedAt < TTYD_PROBE_CACHE_MS) {
14608
- return cachedTtydReady.ok;
14609
- }
14610
- const ok = await checkPort(7681, TTYD_PROBE_TIMEOUT_MS);
14611
- if (!cachedTtydReady || cachedTtydReady.ok !== ok) {
14612
- console.log(`[health] terminal_ready: ${cachedTtydReady?.ok ?? "unknown"} \u2192 ${ok}`);
14613
- }
14614
- cachedTtydReady = { ok, checkedAt: Date.now() };
14615
- return ok;
14616
- }
14617
14403
  var app = new Hono2();
14618
14404
  app.get("/", async (c) => {
14619
14405
  const browserTransport = resolveBrowserTransport(c.req.raw, c.env?.incoming?.socket?.remoteAddress);
@@ -14637,7 +14423,6 @@ app.get("/", async (c) => {
14637
14423
  }
14638
14424
  const claudeAuthenticated = authHealth.status === "ok" || authHealth.status === "expiring";
14639
14425
  const vncRunning = await checkPort(6080);
14640
- const terminalReady = await probeTerminalReady();
14641
14426
  let apiKeyConfigured = false;
14642
14427
  try {
14643
14428
  apiKeyConfigured = existsSync10(keyFilePath());
@@ -14681,7 +14466,6 @@ app.get("/", async (c) => {
14681
14466
  claude_authenticated: claudeAuthenticated,
14682
14467
  ...onboardingComplete !== void 0 && { onboarding_complete: onboardingComplete },
14683
14468
  vnc_running: vncRunning,
14684
- terminal_ready: terminalReady,
14685
14469
  browser_transport: browserTransport,
14686
14470
  auth_status: authHealth.status,
14687
14471
  auth_expires_at: authHealth.expiresAt ?? null,
@@ -14709,14 +14493,14 @@ app.get("/", async (c) => {
14709
14493
  var health_default = app;
14710
14494
 
14711
14495
  // server/routes/session.ts
14712
- import { resolve as resolve12 } from "path";
14713
- import { existsSync as existsSync11, writeFileSync as writeFileSync10, mkdirSync as mkdirSync9 } from "fs";
14496
+ import { resolve as resolve11 } from "path";
14497
+ import { existsSync as existsSync11, writeFileSync as writeFileSync10, mkdirSync as mkdirSync8 } from "fs";
14714
14498
  var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
14715
14499
  function writeBrandingCache(accountId, agentSlug, branding) {
14716
14500
  try {
14717
- const cacheDir = resolve12(MAXY_DIR, "branding-cache", accountId);
14718
- mkdirSync9(cacheDir, { recursive: true });
14719
- writeFileSync10(resolve12(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
14501
+ const cacheDir = resolve11(MAXY_DIR, "branding-cache", accountId);
14502
+ mkdirSync8(cacheDir, { recursive: true });
14503
+ writeFileSync10(resolve11(cacheDir, `${agentSlug}.json`), JSON.stringify(branding), "utf-8");
14720
14504
  } catch (err) {
14721
14505
  console.error(`[branding] cache write failed: ${err instanceof Error ? err.message : String(err)}`);
14722
14506
  }
@@ -14786,8 +14570,8 @@ app2.post("/", async (c) => {
14786
14570
  }
14787
14571
  let agentConfig = null;
14788
14572
  if (account) {
14789
- const agentDir = resolve12(account.accountDir, "agents", agentSlug);
14790
- const agentConfigPath = resolve12(agentDir, "config.json");
14573
+ const agentDir = resolve11(account.accountDir, "agents", agentSlug);
14574
+ const agentConfigPath = resolve11(agentDir, "config.json");
14791
14575
  if (!existsSync11(agentDir) || !existsSync11(agentConfigPath)) {
14792
14576
  return c.json({ error: "Agent not found" }, 404);
14793
14577
  }
@@ -14978,8 +14762,6 @@ app2.post("/", async (c) => {
14978
14762
  const newVisitorId = visitorId ?? crypto.randomUUID();
14979
14763
  const sessionKey = crypto.randomUUID();
14980
14764
  registerSession(sessionKey, "public", accountId, agentSlug);
14981
- ensureConversation(accountId, "public", sessionKey, newVisitorId, agentSlug).catch(() => {
14982
- });
14983
14765
  const hasImage = agentConfig?.image ? "yes" : "no";
14984
14766
  console.log(`[session] new-session visitor=${newVisitorId.slice(0, 8)}\u2026 session=${sessionKey.slice(0, 8)}\u2026 agent=${agentSlug} image=${hasImage} showAgentName=${agentConfig?.showAgentName ?? false}`);
14985
14767
  return withVisitorCookie(
@@ -15034,9 +14816,9 @@ ${raw2}`;
15034
14816
  import { randomUUID as randomUUID6 } from "crypto";
15035
14817
  import { mkdir as mkdir2, readFile, stat as stat2, writeFile as writeFile2 } from "fs/promises";
15036
14818
  import { realpathSync } from "fs";
15037
- import { resolve as resolve13, extname, basename as basename3 } from "path";
15038
- var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT ?? resolve13(process.cwd(), "../platform");
15039
- var ATTACHMENTS_ROOT = resolve13(PLATFORM_ROOT4, "..", "data/uploads");
14819
+ import { resolve as resolve12, extname, basename as basename3 } from "path";
14820
+ var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT ?? resolve12(process.cwd(), "../platform");
14821
+ var ATTACHMENTS_ROOT = resolve12(PLATFORM_ROOT4, "..", "data/uploads");
15040
14822
  var SUPPORTED_MIME_TYPES = /* @__PURE__ */ new Set([
15041
14823
  "image/jpeg",
15042
14824
  "image/png",
@@ -15060,11 +14842,11 @@ function assertSupportedMime(mimeType) {
15060
14842
  }
15061
14843
  async function writeAttachment(scope, filename, mimeType, sizeBytes, buffer) {
15062
14844
  const attachmentId = randomUUID6();
15063
- const dir = resolve13(ATTACHMENTS_ROOT, scope, attachmentId);
14845
+ const dir = resolve12(ATTACHMENTS_ROOT, scope, attachmentId);
15064
14846
  await mkdir2(dir, { recursive: true });
15065
14847
  const ext = extname(filename) || "";
15066
- const storagePath = resolve13(dir, `${attachmentId}${ext}`);
15067
- const metaPath = resolve13(dir, `${attachmentId}.meta.json`);
14848
+ const storagePath = resolve12(dir, `${attachmentId}${ext}`);
14849
+ const metaPath = resolve12(dir, `${attachmentId}.meta.json`);
15068
14850
  const meta = {
15069
14851
  attachmentId,
15070
14852
  scope,
@@ -15742,13 +15524,13 @@ var group_default = app4;
15742
15524
  // app/lib/access-gate.ts
15743
15525
  import neo4j2 from "neo4j-driver";
15744
15526
  import { readFileSync as readFileSync12 } from "fs";
15745
- import { resolve as resolve14 } from "path";
15527
+ import { resolve as resolve13 } from "path";
15746
15528
  import { randomUUID as randomUUID7, randomInt } from "crypto";
15747
- var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT ?? resolve14(process.cwd(), "..");
15529
+ var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT ?? resolve13(process.cwd(), "..");
15748
15530
  var driver2 = null;
15749
15531
  function readPassword2() {
15750
15532
  if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
15751
- const passwordFile = resolve14(PLATFORM_ROOT5, "config/.neo4j-password");
15533
+ const passwordFile = resolve13(PLATFORM_ROOT5, "config/.neo4j-password");
15752
15534
  try {
15753
15535
  return readFileSync12(passwordFile, "utf-8").trim();
15754
15536
  } catch {
@@ -16061,17 +15843,17 @@ async function findActiveGrantByContact(contactValue, agentSlug, accountId) {
16061
15843
  }
16062
15844
 
16063
15845
  // app/lib/brevo-sms.ts
16064
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync11, mkdirSync as mkdirSync10, existsSync as existsSync12, chmodSync } from "fs";
15846
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync11, mkdirSync as mkdirSync9, existsSync as existsSync12, chmodSync } from "fs";
16065
15847
  import { dirname as dirname6 } from "path";
16066
- import { resolve as resolve15 } from "path";
16067
- var BREVO_API_KEY_FILE = resolve15(MAXY_DIR, ".brevo-api-key");
15848
+ import { resolve as resolve14 } from "path";
15849
+ var BREVO_API_KEY_FILE = resolve14(MAXY_DIR, ".brevo-api-key");
16068
15850
  var BREVO_API_URL = "https://api.brevo.com/v3/transactionalSMS/sms";
16069
15851
  var BREVO_TIMEOUT_MS = 1e4;
16070
15852
  var BREVO_SENDER = "Maxy";
16071
15853
  var platformRoot = process.env.MAXY_PLATFORM_ROOT;
16072
15854
  if (platformRoot) {
16073
15855
  try {
16074
- const brandPath = resolve15(platformRoot, "config", "brand.json");
15856
+ const brandPath = resolve14(platformRoot, "config", "brand.json");
16075
15857
  if (existsSync12(brandPath)) {
16076
15858
  const brand = JSON.parse(readFileSync13(brandPath, "utf-8"));
16077
15859
  if (brand.productName) BREVO_SENDER = brand.productName;
@@ -16708,7 +16490,7 @@ app6.post("/webhook", async (c) => {
16708
16490
  var telegram_default = app6;
16709
16491
 
16710
16492
  // server/routes/whatsapp.ts
16711
- import { join as join8, resolve as resolve16, basename as basename4 } from "path";
16493
+ import { join as join8, resolve as resolve15, basename as basename4 } from "path";
16712
16494
  import { readFile as readFile2, stat as stat3 } from "fs/promises";
16713
16495
  import { realpathSync as realpathSync2, readdirSync as readdirSync4, readFileSync as readFileSync15, existsSync as existsSync14 } from "fs";
16714
16496
 
@@ -16816,8 +16598,8 @@ async function startLogin(opts) {
16816
16598
  resetActiveLogin(accountId);
16817
16599
  let resolveQr = null;
16818
16600
  let rejectQr = null;
16819
- const qrPromise = new Promise((resolve29, reject) => {
16820
- resolveQr = resolve29;
16601
+ const qrPromise = new Promise((resolve28, reject) => {
16602
+ resolveQr = resolve28;
16821
16603
  rejectQr = reject;
16822
16604
  });
16823
16605
  const qrTimer = setTimeout(
@@ -17188,14 +16970,14 @@ app7.post("/config", async (c) => {
17188
16970
  return c.json({ ok: true, slug: currentSlug });
17189
16971
  }
17190
16972
  case "list-public-agents": {
17191
- const agentsDir = resolve16(account.accountDir, "agents");
16973
+ const agentsDir = resolve15(account.accountDir, "agents");
17192
16974
  const agents = [];
17193
16975
  if (existsSync14(agentsDir)) {
17194
16976
  try {
17195
16977
  const entries = readdirSync4(agentsDir, { withFileTypes: true });
17196
16978
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
17197
16979
  if (!entry.isDirectory() || entry.name === "admin") continue;
17198
- const configPath2 = resolve16(agentsDir, entry.name, "config.json");
16980
+ const configPath2 = resolve15(agentsDir, entry.name, "config.json");
17199
16981
  if (!existsSync14(configPath2)) continue;
17200
16982
  try {
17201
16983
  const config = JSON.parse(readFileSync15(configPath2, "utf-8"));
@@ -17273,7 +17055,7 @@ app7.post("/send-document", async (c) => {
17273
17055
  if (!maxyAccountId || !PLATFORM_ROOT6) {
17274
17056
  return c.json({ error: "Cannot validate file path: missing account or platform context" }, 400);
17275
17057
  }
17276
- const accountDir = resolve16(PLATFORM_ROOT6, "..", "data/accounts", maxyAccountId);
17058
+ const accountDir = resolve15(PLATFORM_ROOT6, "..", "data/accounts", maxyAccountId);
17277
17059
  let resolvedPath;
17278
17060
  try {
17279
17061
  resolvedPath = realpathSync2(filePath);
@@ -17410,8 +17192,8 @@ var whatsapp_default = app7;
17410
17192
 
17411
17193
  // server/routes/onboarding.ts
17412
17194
  import { spawn as spawn3, execFileSync as execFileSync2 } from "child_process";
17413
- import { openSync as openSync4, closeSync as closeSync4, writeFileSync as writeFileSync12, writeSync, existsSync as existsSync15, mkdirSync as mkdirSync11, readFileSync as readFileSync16, unlinkSync as unlinkSync4 } from "fs";
17414
- import { resolve as resolve17, dirname as dirname7 } from "path";
17195
+ import { openSync as openSync4, closeSync as closeSync4, writeFileSync as writeFileSync12, writeSync, existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync16, unlinkSync as unlinkSync4 } from "fs";
17196
+ import { resolve as resolve16, dirname as dirname7 } from "path";
17415
17197
  import { createHash, randomUUID as randomUUID9 } from "crypto";
17416
17198
  var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT || "";
17417
17199
  function hashPin(pin) {
@@ -17524,7 +17306,7 @@ app8.post("/set-pin", async (c) => {
17524
17306
  }
17525
17307
  const hash = hashPin(body.pin);
17526
17308
  const userId = randomUUID9();
17527
- mkdirSync11(dirname7(USERS_FILE), { recursive: true });
17309
+ mkdirSync10(dirname7(USERS_FILE), { recursive: true });
17528
17310
  writeFileSync12(USERS_FILE, JSON.stringify([{ userId, name: "Owner", pin: hash }]), { mode: 384 });
17529
17311
  console.log(`[set-pin] created users.json: userId=${userId.slice(0, 8)}\u2026 hash=${hash.slice(0, 8)}\u2026`);
17530
17312
  const account = resolveAccount();
@@ -17585,7 +17367,7 @@ app8.post("/skip", async (c) => {
17585
17367
  }
17586
17368
  const { accountId, accountDir } = account;
17587
17369
  let agentName = "Maxy";
17588
- const brandPath = PLATFORM_ROOT7 ? resolve17(PLATFORM_ROOT7, "config", "brand.json") : "";
17370
+ const brandPath = PLATFORM_ROOT7 ? resolve16(PLATFORM_ROOT7, "config", "brand.json") : "";
17589
17371
  if (brandPath && existsSync15(brandPath)) {
17590
17372
  try {
17591
17373
  const brand = JSON.parse(readFileSync16(brandPath, "utf-8"));
@@ -17594,9 +17376,9 @@ app8.post("/skip", async (c) => {
17594
17376
  console.error(`[onboarding-skip] brand.json read failed: ${err instanceof Error ? err.message : String(err)}`);
17595
17377
  }
17596
17378
  }
17597
- const soulPath = resolve17(accountDir, "agents", "admin", "SOUL.md");
17379
+ const soulPath = resolve16(accountDir, "agents", "admin", "SOUL.md");
17598
17380
  try {
17599
- mkdirSync11(dirname7(soulPath), { recursive: true });
17381
+ mkdirSync10(dirname7(soulPath), { recursive: true });
17600
17382
  writeFileSync12(soulPath, `You are ${agentName}, an AI operations manager.
17601
17383
  `);
17602
17384
  console.log(`[onboarding-skip] wrote SOUL.md: ${soulPath}`);
@@ -17635,7 +17417,7 @@ app8.post("/skip", async (c) => {
17635
17417
  var onboarding_default = app8;
17636
17418
 
17637
17419
  // server/routes/client-error.ts
17638
- import { appendFileSync as appendFileSync4, existsSync as existsSync16, renameSync as renameSync5, statSync as statSync7 } from "fs";
17420
+ import { appendFileSync as appendFileSync3, existsSync as existsSync16, renameSync as renameSync5, statSync as statSync7 } from "fs";
17639
17421
  import { join as join9 } from "path";
17640
17422
  var CLIENT_ERRORS_LOG = join9(LOG_DIR, "client-errors.log");
17641
17423
  var MAX_LOG_SIZE = 10 * 1024 * 1024;
@@ -17772,7 +17554,7 @@ app9.post("/", async (c) => {
17772
17554
  tag: typeof body.tag === "string" ? truncate2(body.tag, 32) : void 0,
17773
17555
  status: typeof body.status === "number" ? body.status : void 0
17774
17556
  };
17775
- appendFileSync4(CLIENT_ERRORS_LOG, JSON.stringify(payload) + "\n", "utf-8");
17557
+ appendFileSync3(CLIENT_ERRORS_LOG, JSON.stringify(payload) + "\n", "utf-8");
17776
17558
  } catch (err) {
17777
17559
  console.error(`[client-error] append failed: ${err instanceof Error ? err.message : String(err)}`);
17778
17560
  }
@@ -17811,11 +17593,7 @@ async function createAdminSession(accountId, thinkingView, userId, userName) {
17811
17593
  businessName = branding?.name || void 0;
17812
17594
  } catch {
17813
17595
  }
17814
- let conversationId = null;
17815
- if (userId) {
17816
- conversationId = await createNewAdminConversation(userId, accountId, sessionKey);
17817
- }
17818
- console.log(`[session] ${(/* @__PURE__ */ new Date()).toISOString()} admin session created: userId=${userId ?? "\u2013"} userName=${userName ?? "\u2013"} accountId=${accountId} conversationId=${conversationId?.slice(0, 8) ?? "\u2013"} sessionKey=${sessionKey.slice(0, 8)}`);
17596
+ console.log(`[session] ${(/* @__PURE__ */ new Date()).toISOString()} admin session created: userId=${userId ?? "\u2013"} userName=${userName ?? "\u2013"} accountId=${accountId} conversationId=deferred sessionKey=${sessionKey.slice(0, 8)}`);
17819
17597
  return {
17820
17598
  session_key: sessionKey,
17821
17599
  agent_id: "admin",
@@ -17824,7 +17602,7 @@ async function createAdminSession(accountId, thinkingView, userId, userName) {
17824
17602
  thinkingView: effectiveThinkingView,
17825
17603
  onboardingComplete,
17826
17604
  businessName,
17827
- conversationId
17605
+ conversationId: null
17828
17606
  };
17829
17607
  }
17830
17608
  var app10 = new Hono2();
@@ -17919,11 +17697,11 @@ app10.post("/", async (c) => {
17919
17697
  var session_default2 = app10;
17920
17698
 
17921
17699
  // server/routes/admin/chat.ts
17922
- import { resolve as resolve18 } from "path";
17700
+ import { resolve as resolve17 } from "path";
17923
17701
 
17924
17702
  // app/lib/script-stream-tailer.ts
17925
17703
  import * as childProcess from "child_process";
17926
- import { appendFileSync as appendFileSync5, createReadStream as createReadStream2, mkdirSync as mkdirSync12, statSync as statSync8 } from "fs";
17704
+ import { appendFileSync as appendFileSync4, createReadStream as createReadStream2, mkdirSync as mkdirSync11, statSync as statSync8 } from "fs";
17927
17705
  import { dirname as dirname8 } from "path";
17928
17706
  import { StringDecoder as StringDecoder2 } from "string_decoder";
17929
17707
  var SCRIPT_STREAM_RE = /^\[([^\]]+)\] \[script:([a-z][a-z0-9-]*)((?::[a-z0-9:_-]+)?)\] (.*)$/;
@@ -18033,8 +17811,8 @@ function writeRouteMilestone(streamLogPath, scope, line) {
18033
17811
  }
18034
17812
  const ts = (/* @__PURE__ */ new Date()).toISOString();
18035
17813
  try {
18036
- mkdirSync12(dirname8(streamLogPath), { recursive: true });
18037
- appendFileSync5(streamLogPath, `[${ts}] [script:${scope}] ${line}
17814
+ mkdirSync11(dirname8(streamLogPath), { recursive: true });
17815
+ appendFileSync4(streamLogPath, `[${ts}] [script:${scope}] ${line}
18038
17816
  `);
18039
17817
  } catch (err) {
18040
17818
  console.error(
@@ -18364,7 +18142,7 @@ app11.post("/", requireAdminSession, async (c) => {
18364
18142
  try {
18365
18143
  registerAdminSSE(sseEntry);
18366
18144
  if (sseConvId) {
18367
- const streamLogPath = resolve18(account.accountDir, "logs", `claude-agent-stream-${sseConvId}.log`);
18145
+ const streamLogPath = resolve17(account.accountDir, "logs", `claude-agent-stream-${sseConvId}.log`);
18368
18146
  tailer = startScriptStreamTailer({
18369
18147
  path: streamLogPath,
18370
18148
  onEvent: (event) => {
@@ -18490,7 +18268,7 @@ var compact_default = app12;
18490
18268
 
18491
18269
  // server/routes/admin/logs.ts
18492
18270
  import { existsSync as existsSync18, readdirSync as readdirSync5, readFileSync as readFileSync18, statSync as statSync9 } from "fs";
18493
- import { resolve as resolve19, basename as basename5 } from "path";
18271
+ import { resolve as resolve18, basename as basename5 } from "path";
18494
18272
  var TAIL_BYTES = 8192;
18495
18273
  var app13 = new Hono2();
18496
18274
  app13.get("/", async (c) => {
@@ -18499,13 +18277,13 @@ app13.get("/", async (c) => {
18499
18277
  const conversationIdParam = c.req.query("conversationId");
18500
18278
  const download = c.req.query("download") === "1";
18501
18279
  const account = resolveAccount();
18502
- const accountLogDir2 = account ? resolve19(account.accountDir, "logs") : null;
18280
+ const accountLogDir2 = account ? resolve18(account.accountDir, "logs") : null;
18503
18281
  if (fileParam) {
18504
18282
  const safe = basename5(fileParam);
18505
18283
  const searched = [];
18506
18284
  for (const dir of [accountLogDir2, LOG_DIR]) {
18507
18285
  if (!dir) continue;
18508
- const filePath = resolve19(dir, safe);
18286
+ const filePath = resolve18(dir, safe);
18509
18287
  searched.push(filePath);
18510
18288
  try {
18511
18289
  const content = readFileSync18(filePath, "utf-8");
@@ -18547,7 +18325,7 @@ app13.get("/", async (c) => {
18547
18325
  const searched = [];
18548
18326
  for (const dir of [accountLogDir2, LOG_DIR]) {
18549
18327
  if (!dir) continue;
18550
- const filePath = resolve19(dir, fileName);
18328
+ const filePath = resolve18(dir, fileName);
18551
18329
  searched.push(filePath);
18552
18330
  try {
18553
18331
  const content = readFileSync18(filePath, "utf-8");
@@ -18574,10 +18352,10 @@ app13.get("/", async (c) => {
18574
18352
  console.warn(`[admin/logs] readdir-fail dir=${dir} reason=${reason}`);
18575
18353
  continue;
18576
18354
  }
18577
- files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync9(resolve19(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
18355
+ files.filter((f) => !seen.has(f)).map((f) => ({ name: f, mtime: statSync9(resolve18(dir, f)).mtimeMs })).sort((a, b) => b.mtime - a.mtime).forEach(({ name }) => {
18578
18356
  seen.add(name);
18579
18357
  try {
18580
- const content = readFileSync18(resolve19(dir, name));
18358
+ const content = readFileSync18(resolve18(dir, name));
18581
18359
  const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
18582
18360
  logs[name] = tail.trim() || "(empty)";
18583
18361
  } catch (err) {
@@ -18618,7 +18396,7 @@ var claude_info_default = app14;
18618
18396
  // server/routes/admin/attachment.ts
18619
18397
  import { readFile as readFile3, readdir } from "fs/promises";
18620
18398
  import { existsSync as existsSync19 } from "fs";
18621
- import { resolve as resolve20 } from "path";
18399
+ import { resolve as resolve19 } from "path";
18622
18400
  var app15 = new Hono2();
18623
18401
  app15.get("/:attachmentId", requireAdminSession, async (c) => {
18624
18402
  const attachmentId = c.req.param("attachmentId");
@@ -18630,11 +18408,11 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
18630
18408
  if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(attachmentId)) {
18631
18409
  return new Response("Not found", { status: 404 });
18632
18410
  }
18633
- const dir = resolve20(ATTACHMENTS_ROOT, accountId, attachmentId);
18411
+ const dir = resolve19(ATTACHMENTS_ROOT, accountId, attachmentId);
18634
18412
  if (!existsSync19(dir)) {
18635
18413
  return new Response("Not found", { status: 404 });
18636
18414
  }
18637
- const metaPath = resolve20(dir, `${attachmentId}.meta.json`);
18415
+ const metaPath = resolve19(dir, `${attachmentId}.meta.json`);
18638
18416
  if (!existsSync19(metaPath)) {
18639
18417
  return new Response("Not found", { status: 404 });
18640
18418
  }
@@ -18649,7 +18427,7 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
18649
18427
  if (!dataFile) {
18650
18428
  return new Response("Not found", { status: 404 });
18651
18429
  }
18652
- const filePath = resolve20(dir, dataFile);
18430
+ const filePath = resolve19(dir, dataFile);
18653
18431
  const buffer = await readFile3(filePath);
18654
18432
  return new Response(new Uint8Array(buffer), {
18655
18433
  headers: {
@@ -18663,7 +18441,7 @@ var attachment_default = app15;
18663
18441
 
18664
18442
  // server/routes/admin/account.ts
18665
18443
  import { readFileSync as readFileSync19, writeFileSync as writeFileSync13 } from "fs";
18666
- import { resolve as resolve21 } from "path";
18444
+ import { resolve as resolve20 } from "path";
18667
18445
  var VALID_CONTEXT_MODES = ["managed", "claude-code"];
18668
18446
  var app16 = new Hono2();
18669
18447
  app16.patch("/", requireAdminSession, async (c) => {
@@ -18679,7 +18457,7 @@ app16.patch("/", requireAdminSession, async (c) => {
18679
18457
  }
18680
18458
  const account = resolveAccount();
18681
18459
  if (!account) return c.json({ error: "No account configured" }, 500);
18682
- const configPath2 = resolve21(account.accountDir, "account.json");
18460
+ const configPath2 = resolve20(account.accountDir, "account.json");
18683
18461
  try {
18684
18462
  const raw2 = readFileSync19(configPath2, "utf-8");
18685
18463
  const config = JSON.parse(raw2);
@@ -18695,13 +18473,13 @@ app16.patch("/", requireAdminSession, async (c) => {
18695
18473
  var account_default = app16;
18696
18474
 
18697
18475
  // server/routes/admin/agents.ts
18698
- import { resolve as resolve22 } from "path";
18476
+ import { resolve as resolve21 } from "path";
18699
18477
  import { readdirSync as readdirSync6, readFileSync as readFileSync20, existsSync as existsSync20, rmSync as rmSync3 } from "fs";
18700
18478
  var app17 = new Hono2();
18701
18479
  app17.get("/", (c) => {
18702
18480
  const account = resolveAccount();
18703
18481
  if (!account) return c.json({ agents: [] });
18704
- const agentsDir = resolve22(account.accountDir, "agents");
18482
+ const agentsDir = resolve21(account.accountDir, "agents");
18705
18483
  if (!existsSync20(agentsDir)) return c.json({ agents: [] });
18706
18484
  const agents = [];
18707
18485
  try {
@@ -18709,7 +18487,7 @@ app17.get("/", (c) => {
18709
18487
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
18710
18488
  if (!entry.isDirectory()) continue;
18711
18489
  if (entry.name === "admin") continue;
18712
- const configPath2 = resolve22(agentsDir, entry.name, "config.json");
18490
+ const configPath2 = resolve21(agentsDir, entry.name, "config.json");
18713
18491
  if (!existsSync20(configPath2)) continue;
18714
18492
  try {
18715
18493
  const config = JSON.parse(readFileSync20(configPath2, "utf-8"));
@@ -18738,7 +18516,7 @@ app17.delete("/:slug", (c) => {
18738
18516
  if (slug.includes("/") || slug.includes("..") || slug.includes("\\")) {
18739
18517
  return c.json({ error: "Invalid agent slug" }, 400);
18740
18518
  }
18741
- const agentDir = resolve22(account.accountDir, "agents", slug);
18519
+ const agentDir = resolve21(account.accountDir, "agents", slug);
18742
18520
  if (!existsSync20(agentDir)) {
18743
18521
  return c.json({ error: "Agent not found" }, 404);
18744
18522
  }
@@ -18755,8 +18533,8 @@ var agents_default = app17;
18755
18533
 
18756
18534
  // server/routes/admin/version.ts
18757
18535
  import { existsSync as existsSync21, readFileSync as readFileSync21 } from "fs";
18758
- import { resolve as resolve23, join as join10 } from "path";
18759
- var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ?? resolve23(process.cwd(), "..");
18536
+ import { resolve as resolve22, join as join10 } from "path";
18537
+ var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ?? resolve22(process.cwd(), "..");
18760
18538
  var brandHostname = "maxy";
18761
18539
  var brandNpmPackage = "@rubytech/create-maxy";
18762
18540
  var brandJsonPath = join10(PLATFORM_ROOT8, "config", "brand.json");
@@ -18768,7 +18546,7 @@ if (existsSync21(brandJsonPath)) {
18768
18546
  } catch {
18769
18547
  }
18770
18548
  }
18771
- var VERSION_FILE = resolve23(PLATFORM_ROOT8, `config/.${brandHostname}-version`);
18549
+ var VERSION_FILE = resolve22(PLATFORM_ROOT8, `config/.${brandHostname}-version`);
18772
18550
  var NPM_PACKAGE = brandNpmPackage;
18773
18551
  var REGISTRY_URL = `https://registry.npmjs.org/${NPM_PACKAGE}/latest`;
18774
18552
  var FETCH_TIMEOUT_MS = 5e3;
@@ -18884,16 +18662,10 @@ app19.post("/new", requireAdminSession, async (c) => {
18884
18662
  const newSessionKey = crypto3.randomUUID();
18885
18663
  const userName = getUserNameForSession(oldSessionKey);
18886
18664
  registerSession(newSessionKey, "admin", accountId, void 0, userId, userName);
18887
- const conversationId = await createNewAdminConversation(userId, accountId, newSessionKey);
18888
- if (!conversationId) {
18889
- unregisterSession(newSessionKey);
18890
- console.error(`[session] ${(/* @__PURE__ */ new Date()).toISOString()} new-conversation-failed: userId=${userId} accountId=${accountId.slice(0, 8)}\u2026 oldSessionKey=${oldSessionKey.slice(0, 8)}\u2026 newSessionKey=${newSessionKey.slice(0, 8)}\u2026`);
18891
- return c.json({ error: "Failed to create conversation" }, 500);
18892
- }
18893
18665
  const previousConversationId = clearSessionHistory(oldSessionKey);
18894
18666
  unregisterSession(oldSessionKey);
18895
- console.log(`[session] ${(/* @__PURE__ */ new Date()).toISOString()} session reset for new conversation: oldSessionKey=${oldSessionKey.slice(0, 8)}\u2026 newSessionKey=${newSessionKey.slice(0, 8)}\u2026 previousConversationId=${previousConversationId?.slice(0, 8) ?? "none"}\u2026 newConversationId=${conversationId.slice(0, 8)}\u2026`);
18896
- return c.json({ session_key: newSessionKey, conversationId });
18667
+ console.log(`[session] ${(/* @__PURE__ */ new Date()).toISOString()} session reset for new conversation: oldSessionKey=${oldSessionKey.slice(0, 8)}\u2026 newSessionKey=${newSessionKey.slice(0, 8)}\u2026 previousConversationId=${previousConversationId?.slice(0, 8) ?? "none"}\u2026 newConversationId=deferred`);
18668
+ return c.json({ session_key: newSessionKey, conversationId: null });
18897
18669
  });
18898
18670
  app19.delete("/:id", requireAdminSession, async (c) => {
18899
18671
  const conversationId = c.req.param("id");
@@ -19268,7 +19040,7 @@ var events_default = app23;
19268
19040
 
19269
19041
  // server/routes/admin/cloudflare.ts
19270
19042
  import { homedir as homedir3 } from "os";
19271
- import { resolve as resolve25 } from "path";
19043
+ import { resolve as resolve24 } from "path";
19272
19044
  import { readFileSync as readFileSync23 } from "fs";
19273
19045
 
19274
19046
  // app/lib/dns-label.ts
@@ -19285,10 +19057,10 @@ function isValidDomain(value) {
19285
19057
  }
19286
19058
 
19287
19059
  // app/lib/alias-domains.ts
19288
- import { existsSync as existsSync22, mkdirSync as mkdirSync13, readFileSync as readFileSync22, writeFileSync as writeFileSync14 } from "fs";
19060
+ import { existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync22, writeFileSync as writeFileSync14 } from "fs";
19289
19061
  import { dirname as dirname9 } from "path";
19290
- import { resolve as resolve24 } from "path";
19291
- var ALIAS_DOMAINS_PATH = resolve24(MAXY_DIR, "alias-domains.json");
19062
+ import { resolve as resolve23 } from "path";
19063
+ var ALIAS_DOMAINS_PATH = resolve23(MAXY_DIR, "alias-domains.json");
19292
19064
  function readExisting() {
19293
19065
  if (!existsSync22(ALIAS_DOMAINS_PATH)) return /* @__PURE__ */ new Set();
19294
19066
  try {
@@ -19303,7 +19075,7 @@ function addAliasDomain(hostname2) {
19303
19075
  const existing = readExisting();
19304
19076
  if (existing.has(hostname2)) return;
19305
19077
  existing.add(hostname2);
19306
- mkdirSync13(dirname9(ALIAS_DOMAINS_PATH), { recursive: true });
19078
+ mkdirSync12(dirname9(ALIAS_DOMAINS_PATH), { recursive: true });
19307
19079
  writeFileSync14(ALIAS_DOMAINS_PATH, JSON.stringify([...existing], null, 2) + "\n", "utf-8");
19308
19080
  }
19309
19081
 
@@ -19311,8 +19083,8 @@ function addAliasDomain(hostname2) {
19311
19083
  var SETUP_TIMEOUT_MS = 10 * 60 * 1e3;
19312
19084
  var DOMAINS_TIMEOUT_MS = 40 * 1e3;
19313
19085
  function loadBrandInfo() {
19314
- const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve25(process.cwd(), "..");
19315
- const brandPath = resolve25(platformRoot2, "config", "brand.json");
19086
+ const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve24(process.cwd(), "..");
19087
+ const brandPath = resolve24(platformRoot2, "config", "brand.json");
19316
19088
  try {
19317
19089
  const parsed = JSON.parse(readFileSync23(brandPath, "utf-8"));
19318
19090
  const hostname2 = typeof parsed.hostname === "string" && parsed.hostname ? parsed.hostname : "maxy";
@@ -19417,7 +19189,7 @@ app24.get("/domains", requireAdminSession, async (c) => {
19417
19189
  streamLogPath = streamLogPathFor(accountId, correlationId).streamLogPath;
19418
19190
  log2(`phase=stream-log-resolved path=${streamLogPath}`);
19419
19191
  const brand = loadBrandInfo();
19420
- const scriptPath = resolve25(homedir3(), "list-cf-domains.sh");
19192
+ const scriptPath = resolve24(homedir3(), "list-cf-domains.sh");
19421
19193
  const result = await runFormSpawn({
19422
19194
  scriptPath,
19423
19195
  args: [brand.hostname],
@@ -19542,7 +19314,7 @@ app24.post("/setup", requireAdminSession, async (c) => {
19542
19314
  }
19543
19315
  streamLogPath = streamLogPathFor(accountId, correlationId).streamLogPath;
19544
19316
  log2(`phase=stream-log-resolved path=${streamLogPath}`);
19545
- const scriptPath = resolve25(homedir3(), "setup-tunnel.sh");
19317
+ const scriptPath = resolve24(homedir3(), "setup-tunnel.sh");
19546
19318
  const args = [brand.hostname, String(port2), adminFqdn];
19547
19319
  if (publicFqdn) args.push(publicFqdn);
19548
19320
  if (apex) args.push(apex);
@@ -19611,17 +19383,17 @@ var cloudflare_default = app24;
19611
19383
  import { createReadStream as createReadStream3 } from "fs";
19612
19384
  import { readdir as readdir2, readFile as readFile4, stat as stat4, mkdir as mkdir3, writeFile as writeFile4, unlink as unlink2 } from "fs/promises";
19613
19385
  import { realpathSync as realpathSync4 } from "fs";
19614
- import { basename as basename6, dirname as dirname10, join as join11, resolve as resolve27, sep as sep2 } from "path";
19386
+ import { basename as basename6, dirname as dirname10, join as join11, resolve as resolve26, sep as sep2 } from "path";
19615
19387
  import { Readable as Readable3 } from "stream";
19616
19388
 
19617
19389
  // app/lib/data-path.ts
19618
19390
  import { realpathSync as realpathSync3 } from "fs";
19619
- import { resolve as resolve26, normalize, sep, relative } from "path";
19620
- var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT ?? resolve26(process.cwd(), "../platform");
19621
- var DATA_ROOT = resolve26(PLATFORM_ROOT9, "..", "data");
19391
+ import { resolve as resolve25, normalize, sep, relative } from "path";
19392
+ var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT ?? resolve25(process.cwd(), "../platform");
19393
+ var DATA_ROOT = resolve25(PLATFORM_ROOT9, "..", "data");
19622
19394
  function resolveDataPath(raw2) {
19623
19395
  const cleaned = normalize("/" + (raw2 ?? "").replace(/\\/g, "/")).replace(/^\/+/, "");
19624
- const absolute = resolve26(DATA_ROOT, cleaned);
19396
+ const absolute = resolve25(DATA_ROOT, cleaned);
19625
19397
  let dataRootReal;
19626
19398
  try {
19627
19399
  dataRootReal = realpathSync3(DATA_ROOT);
@@ -19899,7 +19671,7 @@ async function readMeta(absDir, baseName) {
19899
19671
  }
19900
19672
  async function readAccountNames() {
19901
19673
  const map = /* @__PURE__ */ new Map();
19902
- const accountsDir = resolve27(DATA_ROOT, "accounts");
19674
+ const accountsDir = resolve26(DATA_ROOT, "accounts");
19903
19675
  let names;
19904
19676
  try {
19905
19677
  names = await readdir2(accountsDir);
@@ -19908,7 +19680,7 @@ async function readAccountNames() {
19908
19680
  }
19909
19681
  for (const name of names) {
19910
19682
  if (!UUID_RE3.test(name)) continue;
19911
- const configPath2 = resolve27(accountsDir, name, "account.json");
19683
+ const configPath2 = resolve26(accountsDir, name, "account.json");
19912
19684
  try {
19913
19685
  const raw2 = await readFile4(configPath2, "utf8");
19914
19686
  const parsed = JSON.parse(raw2);
@@ -20099,8 +19871,8 @@ app25.post("/upload", requireAdminSession, async (c) => {
20099
19871
  }
20100
19872
  const safeName = basename6(file.name).replace(/[\0/\\]/g, "_");
20101
19873
  const finalName = `${Date.now()}-${safeName}`;
20102
- const destDir = resolve27(DATA_ROOT, "uploads", accountId);
20103
- const destPath = resolve27(destDir, finalName);
19874
+ const destDir = resolve26(DATA_ROOT, "uploads", accountId);
19875
+ const destPath = resolve26(destDir, finalName);
20104
19876
  try {
20105
19877
  await mkdir3(destDir, { recursive: true });
20106
19878
  const dataRootReal = realpathSync4(DATA_ROOT);
@@ -20253,24 +20025,35 @@ var GRAPH_LABEL_COLOURS = {
20253
20025
  Review: "#059669",
20254
20026
  ImageObject: "#6EE7B7",
20255
20027
  // Conversational
20028
+ //
20029
+ // Task 649 palette — the four sublabelled conversation/message labels
20030
+ // (AdminConversation, PublicConversation, UserMessage, AssistantMessage)
20031
+ // are assigned four pairwise-distinguishable hues: indigo / purple /
20032
+ // orange / yellow. The Task 633 violet-shade split (darker for admin,
20033
+ // lighter for public) was indistinguishable on canvas and in the legend;
20034
+ // hue-shift replaces lightness-shift.
20035
+ //
20036
+ // Constraints honoured:
20037
+ // - Pairwise distinct among the four in-family labels.
20038
+ // - UserMessage=#F97316 reserved (Person/Preference orange cue) —
20039
+ // the other three are non-orange.
20040
+ // - No collision with Task/Project/Event pinks (#DB2777/#BE185D/#EC4899),
20041
+ // Knowledge greens, Workflow cyans, LocalBusiness blues.
20042
+ // - Conversation base (#7C3AED) kept as pre-backfill fallback for
20043
+ // nodes that pre-date the AdminConversation/PublicConversation split.
20044
+ // - Message base reassigned away from old #A78BFA to avoid near-
20045
+ // collision with the new PublicConversation=#A855F7; slate signals
20046
+ // "legacy/system/tool" — nodes carrying only :Message without a
20047
+ // role-sublabel.
20048
+ // - Out of family (intentionally not touched): OnboardingState=#8B5CF6
20049
+ // sits in the violet neighbourhood; palette reassignment outside
20050
+ // the conversation/message family is out of scope for Task 649.
20256
20051
  Conversation: "#7C3AED",
20257
- // Task 633 — admin vs public disambiguation on /graph. Darker violet for
20258
- // admin conversations (the operator-facing side), lighter for public
20259
- // (customer-facing) so the operator can read chat topology by colour
20260
- // alone. Both within the Conversation family — the hue shift is subtle
20261
- // enough to stay legible on adjacent nodes yet distinct enough to be
20262
- // unmistakable in the popover legend.
20263
- AdminConversation: "#5B21B6",
20264
- PublicConversation: "#A78BFA",
20265
- Message: "#A78BFA",
20266
- // Task 633 — user vs assistant disambiguation on /graph. UserMessage
20267
- // picks up the Person/Preference orange family cue (the user is a
20268
- // person); AssistantMessage stays violet-adjacent to reinforce the
20269
- // AdminConversation cue (both are admin-side). system/tool Messages
20270
- // (not written by the persist path today; reserved shape) stay on the
20271
- // base `:Message` colour.
20052
+ AdminConversation: "#4338CA",
20053
+ PublicConversation: "#A855F7",
20054
+ Message: "#64748B",
20272
20055
  UserMessage: "#F97316",
20273
- AssistantMessage: "#8B5CF6",
20056
+ AssistantMessage: "#FACC15",
20274
20057
  ToolCall: "#C4B5FD",
20275
20058
  // Tasks / projects / events
20276
20059
  Task: "#DB2777",
@@ -21405,8 +21188,8 @@ app35.get("/agent-assets/:slug/:filename", (c) => {
21405
21188
  console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
21406
21189
  return c.text("Not found", 404);
21407
21190
  }
21408
- const filePath = resolve28(account.accountDir, "agents", slug, "assets", filename);
21409
- const expectedDir = resolve28(account.accountDir, "agents", slug, "assets");
21191
+ const filePath = resolve27(account.accountDir, "agents", slug, "assets", filename);
21192
+ const expectedDir = resolve27(account.accountDir, "agents", slug, "assets");
21410
21193
  if (!filePath.startsWith(expectedDir + "/")) {
21411
21194
  console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
21412
21195
  return c.text("Forbidden", 403);
@@ -21435,8 +21218,8 @@ app35.get("/generated/:filename", (c) => {
21435
21218
  console.error(`[generated] serve file=${filename} status=404`);
21436
21219
  return c.text("Not found", 404);
21437
21220
  }
21438
- const filePath = resolve28(account.accountDir, "generated", filename);
21439
- const expectedDir = resolve28(account.accountDir, "generated");
21221
+ const filePath = resolve27(account.accountDir, "generated", filename);
21222
+ const expectedDir = resolve27(account.accountDir, "generated");
21440
21223
  if (!filePath.startsWith(expectedDir + "/")) {
21441
21224
  console.error(`[generated] serve file=${filename} status=403`);
21442
21225
  return c.text("Forbidden", 403);
@@ -21519,7 +21302,7 @@ var clientErrorReporterScript = `<script>
21519
21302
  function cachedHtml(file) {
21520
21303
  let html = htmlCache.get(file);
21521
21304
  if (!html) {
21522
- html = readFileSync24(resolve28(process.cwd(), "public", file), "utf-8");
21305
+ html = readFileSync24(resolve27(process.cwd(), "public", file), "utf-8");
21523
21306
  html = html.replace("<title>Maxy</title>", `<title>${escapeHtml(BRAND.productName)}</title>`);
21524
21307
  html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
21525
21308
  const headInjection = file === "index.html" ? `${brandScript}
@@ -21626,7 +21409,7 @@ app35.use("/vnc-popout.html", logViewerFetch);
21626
21409
  app35.get("/vnc-popout.html", (c) => {
21627
21410
  let html = htmlCache.get("vnc-popout.html");
21628
21411
  if (!html) {
21629
- html = readFileSync24(resolve28(process.cwd(), "public", "vnc-popout.html"), "utf-8");
21412
+ html = readFileSync24(resolve27(process.cwd(), "public", "vnc-popout.html"), "utf-8");
21630
21413
  const name = escapeHtml(BRAND.productName);
21631
21414
  html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
21632
21415
  html = html.replace("</head>", ` ${brandScript}
@@ -21683,11 +21466,6 @@ app35.use("/*", serveStatic({ root: "./public" }));
21683
21466
  var port = parseInt(process.env.PORT ?? "19199", 10);
21684
21467
  var hostname = process.env.HOSTNAME ?? "127.0.0.1";
21685
21468
  var httpServer = serve({ fetch: app35.fetch, port, hostname });
21686
- attachTerminalWsProxy(httpServer, {
21687
- isPublicHost,
21688
- upstreamHost: "127.0.0.1",
21689
- upstreamPort: 7681
21690
- });
21691
21469
  console.log(`${BRAND.productName} listening on http://${hostname}:${port}`);
21692
21470
  var SUBAPP_MANIFEST = [
21693
21471
  { prefix: "/api/health", file: "server/routes/health.ts", subapp: health_default },
@@ -21748,7 +21526,7 @@ if (bootAccountConfig?.whatsapp) {
21748
21526
  }
21749
21527
  init({
21750
21528
  configDir: configDirForWhatsApp,
21751
- platformRoot: resolve28(process.env.MAXY_PLATFORM_ROOT ?? join12(__dirname, "..")),
21529
+ platformRoot: resolve27(process.env.MAXY_PLATFORM_ROOT ?? join12(__dirname, "..")),
21752
21530
  accountConfig: bootAccountConfig,
21753
21531
  onMessage: async (msg) => {
21754
21532
  try {