@rubytech/create-realagent 1.0.819 → 1.0.821

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/package.json +1 -1
  2. package/payload/platform/plugins/admin/skills/onboarding/SKILL.md +8 -9
  3. package/payload/platform/plugins/cloudflare/PLUGIN.md +1 -1
  4. package/payload/platform/plugins/cloudflare/scripts/setup-tunnel.sh +70 -20
  5. package/payload/platform/plugins/docs/references/cloudflare.md +1 -1
  6. package/payload/platform/plugins/docs/references/graph.md +20 -0
  7. package/payload/platform/plugins/email/PLUGIN.md +2 -0
  8. package/payload/platform/plugins/memory/mcp/dist/index.js +8 -2
  9. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  10. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts +20 -0
  11. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts.map +1 -1
  12. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js +40 -1
  13. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js.map +1 -1
  14. package/payload/server/chunk-5OG7TUQL.js +315 -0
  15. package/payload/server/chunk-CZGOB575.js +593 -0
  16. package/payload/server/chunk-NUXYHO6N.js +10079 -0
  17. package/payload/server/chunk-SALVIGXH.js +1116 -0
  18. package/payload/server/chunk-ZT6LKDTP.js +2238 -0
  19. package/payload/server/client-pool-IQU6H43X.js +32 -0
  20. package/payload/server/cloudflare-task-tracker-Q4X5BYR7.js +17 -0
  21. package/payload/server/maxy-edge.js +4 -3
  22. package/payload/server/neo4j-migrations-CYIKMSEO.js +366 -0
  23. package/payload/server/public/assets/admin-okt0ygrZ.js +352 -0
  24. package/payload/server/public/assets/{graph-DeH6ulGh.js → graph-LLMJa4Ch.js} +1 -1
  25. package/payload/server/public/assets/{page-WIAWD2Oi.js → page-DoaF3DB0.js} +1 -1
  26. package/payload/server/public/graph.html +2 -2
  27. package/payload/server/public/index.html +2 -2
  28. package/payload/server/server.js +269 -488
  29. package/payload/server/public/assets/admin-CdVYoqKD.js +0 -352
@@ -51,7 +51,7 @@ import {
51
51
  vncLog,
52
52
  waitForExit,
53
53
  writeChromiumWrapper
54
- } from "./chunk-AJLGI7Y3.js";
54
+ } from "./chunk-NUXYHO6N.js";
55
55
  import {
56
56
  agentLogStream,
57
57
  clearSessionHistory,
@@ -79,12 +79,27 @@ import {
79
79
  sigtermFlushStreamLogs,
80
80
  unregisterSession,
81
81
  validateSession
82
- } from "./chunk-ON3LBL2Y.js";
82
+ } from "./chunk-SALVIGXH.js";
83
83
  import {
84
84
  ACCOUNTS_DIR,
85
+ PLATFORM_ROOT,
86
+ getDefaultAccountId,
87
+ resolveAccount,
88
+ resolveAgentConfig,
89
+ resolveDefaultAgentSlug,
90
+ resolveUserAccounts,
91
+ validateAgentSlug
92
+ } from "./chunk-5OG7TUQL.js";
93
+ import {
94
+ CLOUDFLARE_TASK_DIAGNOSTICS,
95
+ appendCloudflareSteps,
96
+ completeCloudflareTask,
97
+ openCloudflareTask,
98
+ readTunnelState
99
+ } from "./chunk-CZGOB575.js";
100
+ import {
85
101
  GREETING_DIRECTIVE,
86
102
  HAIKU_MODEL,
87
- PLATFORM_ROOT,
88
103
  __commonJS,
89
104
  __toESM,
90
105
  backfillNullUserIdConversations,
@@ -99,7 +114,6 @@ import {
99
114
  findRecentConversation,
100
115
  generateSessionLabel,
101
116
  getAgentSessionIdForConversation,
102
- getDefaultAccountId,
103
117
  getGroupParticipants,
104
118
  getMessagesSince,
105
119
  getRecentMessages,
@@ -110,236 +124,14 @@ import {
110
124
  loadOnboardingStep,
111
125
  projectAgent,
112
126
  renameConversation,
113
- resolveAccount,
114
- resolveAgentConfig,
115
- resolveDefaultAgentSlug,
116
- resolveUserAccounts,
117
127
  runBootMigrations,
118
- validateAgentSlug,
119
128
  verifyAndGetConversationUpdatedAt,
120
129
  verifyConversationOwnership,
121
130
  writeAdminUserAndPerson
122
- } from "./chunk-PXQA2MA3.js";
123
-
124
- // ../lib/graph-write/dist/audit.js
125
- var require_audit = __commonJS({
126
- "../lib/graph-write/dist/audit.js"(exports) {
127
- "use strict";
128
- Object.defineProperty(exports, "__esModule", { value: true });
129
- exports.auditCypherWrite = auditCypherWrite;
130
- exports.formatAuditLine = formatAuditLine;
131
- var EDGE_PATTERN = /\[[^\]]*?:([A-Z_][A-Za-z0-9_]*(?:\|[A-Z_][A-Za-z0-9_]*)*)[^\]]*?\]/g;
132
- var CREATE_OR_MERGE_NODE = /\b(?:CREATE|MERGE)\s*\(\s*[A-Za-z_][A-Za-z0-9_]*\s*:\s*[A-Z]/g;
133
- var PROVENANCE_TOKEN = /\bcreatedBy(?:Agent|Tool|Session|Source)\b/g;
134
- function stripStringLiterals(cypher) {
135
- return cypher.replace(/'[^']*'|"[^"]*"/g, '""');
136
- }
137
- function extractEdgeTypes(cleaned) {
138
- const out = /* @__PURE__ */ new Set();
139
- for (const m of cleaned.matchAll(EDGE_PATTERN)) {
140
- for (const t of m[1].split("|")) {
141
- const clean = t.trim();
142
- if (clean)
143
- out.add(clean);
144
- }
145
- }
146
- return out;
147
- }
148
- function countCreateOrMergeNodes(cleaned) {
149
- const matches = cleaned.match(CREATE_OR_MERGE_NODE);
150
- return matches ? matches.length : 0;
151
- }
152
- function countProvenanceStamps(cleaned) {
153
- const matches = cleaned.match(PROVENANCE_TOKEN);
154
- return matches ? matches.length : 0;
155
- }
156
- function auditCypherWrite(input) {
157
- const warnings = [];
158
- const cleaned = stripStringLiterals(input.cypher);
159
- const referencedTypes = extractEdgeTypes(cleaned);
160
- for (const t of referencedTypes) {
161
- if (!input.schema.relationshipTypes.has(t)) {
162
- warnings.push({ kind: "unknown-type-warning", type: t });
163
- }
164
- }
165
- if (input.nodesCreated > 0) {
166
- const createOrMergeNodes = countCreateOrMergeNodes(cleaned);
167
- if (createOrMergeNodes > 0) {
168
- const stamps = countProvenanceStamps(cleaned);
169
- if (stamps < createOrMergeNodes) {
170
- warnings.push({
171
- kind: "missing-provenance-warning",
172
- created: createOrMergeNodes,
173
- stamped: stamps
174
- });
175
- }
176
- }
177
- }
178
- if (input.orphanIds.length > 0) {
179
- warnings.push({ kind: "orphan-warning", orphanIds: input.orphanIds });
180
- }
181
- return warnings;
182
- }
183
- function formatAuditLine(line) {
184
- const prefixField = `query="${line.cypherPrefix.replace(/"/g, "'")}"`;
185
- switch (line.kind) {
186
- case "accepted":
187
- return `[graph-cypher-write] accepted ${prefixField} nodesCreated=${line.nodesCreated} relsCreated=${line.relsCreated} agentName=${line.agentName} sessionId=${line.sessionId}`;
188
- case "orphan-warning":
189
- return `[graph-cypher-write] orphan-warning ${prefixField} orphanIds=${line.orphanIds.join(",")}`;
190
- case "unknown-type-warning":
191
- return `[graph-cypher-write] unknown-type-warning ${prefixField} type=${line.type}`;
192
- case "missing-provenance-warning":
193
- return `[graph-cypher-write] missing-provenance-warning ${prefixField} created=${line.created} stamped=${line.stamped}`;
194
- }
195
- }
196
- }
197
- });
198
-
199
- // ../lib/graph-write/dist/index.js
200
- var require_dist = __commonJS({
201
- "../lib/graph-write/dist/index.js"(exports) {
202
- "use strict";
203
- var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
204
- if (k2 === void 0) k2 = k;
205
- var desc = Object.getOwnPropertyDescriptor(m, k);
206
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
207
- desc = { enumerable: true, get: function() {
208
- return m[k];
209
- } };
210
- }
211
- Object.defineProperty(o, k2, desc);
212
- }) : (function(o, m, k, k2) {
213
- if (k2 === void 0) k2 = k;
214
- o[k2] = m[k];
215
- }));
216
- var __exportStar = exports && exports.__exportStar || function(m, exports2) {
217
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p)) __createBinding(exports2, m, p);
218
- };
219
- Object.defineProperty(exports, "__esModule", { value: true });
220
- exports.ACTION_PROVENANCE_LABELS = void 0;
221
- exports.stampCreatedBy = stampCreatedBy;
222
- exports.writeNodeWithEdges = writeNodeWithEdges2;
223
- __exportStar(require_audit(), exports);
224
- exports.ACTION_PROVENANCE_LABELS = /* @__PURE__ */ new Set([
225
- "Person",
226
- "UserProfile",
227
- "AdminUser",
228
- "Organization",
229
- "LocalBusiness",
230
- "CloudflareTunnel",
231
- "CloudflareHostname"
232
- ]);
233
- function requiresActionProvenance(labels) {
234
- for (const label of labels) {
235
- if (exports.ACTION_PROVENANCE_LABELS.has(label))
236
- return true;
237
- }
238
- return false;
239
- }
240
- function findProducedFromTaskCandidates(relationships) {
241
- return relationships.filter((r) => r.type === "PRODUCED" && r.direction === "incoming");
242
- }
243
- function stampCreatedBy(props, createdBy) {
244
- return {
245
- ...props,
246
- createdByAgent: createdBy.agent ?? "unknown",
247
- createdBySession: createdBy.session ?? "unknown",
248
- createdByTool: createdBy.tool ?? null,
249
- createdBySource: createdBy.source ?? null
250
- };
251
- }
252
- async function writeNodeWithEdges2(params) {
253
- const { session, labels, props, relationships, createdBy } = params;
254
- const agentLabel = createdBy.agent ?? createdBy.source ?? "unknown";
255
- const labelCsv = labels.join(",");
256
- const reviewDigestActionTool = typeof props.actionTool === "string" && props.actionTool === "review-digest-compose";
257
- if (labels.includes("ReviewAlert") || reviewDigestActionTool) {
258
- const actionToolField = reviewDigestActionTool ? "review-digest-compose" : "n/a";
259
- process.stderr.write(`[graph-write] reject reason=removed-feature labels=${labelCsv} actionTool=${actionToolField} agent=${agentLabel}
260
- `);
261
- throw new Error("Write doctrine violated: review-detector feature removed (Task 884) \u2014 `:ReviewAlert` and `:Event {actionTool:'review-digest-compose'}` writes are not allowed.");
262
- }
263
- if (!relationships || relationships.length < 1) {
264
- process.stderr.write(`[graph-write] reject reason=zero-relationships labels=${labelCsv} agent=${agentLabel}
265
- `);
266
- throw new Error("Write doctrine violated: a node must be created with at least one relationship. See .docs/neo4j.md (Write doctrine).");
267
- }
268
- const labelStr = labels.map((l) => `\`${l.replace(/`/g, "")}\``).join(":");
269
- const nodeProps = stampCreatedBy(props, createdBy);
270
- return await session.executeWrite(async (tx) => {
271
- const targetIds = relationships.map((r) => r.targetNodeId);
272
- const check = await tx.run(`UNWIND $ids AS id MATCH (t) WHERE elementId(t) = id RETURN elementId(t) AS id, labels(t) AS labels`, { ids: targetIds });
273
- const labelsByTarget = /* @__PURE__ */ new Map();
274
- for (const rec of check.records) {
275
- labelsByTarget.set(rec.get("id"), rec.get("labels"));
276
- }
277
- const found = labelsByTarget.size;
278
- const uniqueRequested = new Set(targetIds).size;
279
- if (found !== uniqueRequested) {
280
- process.stderr.write(`[graph-write] reject reason=unresolved-target labels=${labelCsv} agent=${agentLabel} requested=${uniqueRequested} found=${found}
281
- `);
282
- throw new Error(`Write doctrine violated: ${uniqueRequested - found} of ${uniqueRequested} relationship target(s) did not resolve (elementId mismatch). No node created.`);
283
- }
284
- let producedByTaskId = null;
285
- if (requiresActionProvenance(labels) && (createdBy.agent ?? "") !== "system") {
286
- const candidates = findProducedFromTaskCandidates(relationships);
287
- const taskCandidates = candidates.filter((r) => {
288
- const lbls = labelsByTarget.get(r.targetNodeId);
289
- return Array.isArray(lbls) && lbls.includes("Task");
290
- });
291
- if (taskCandidates.length === 0) {
292
- process.stderr.write(`[graph-write] reject reason=missing-action-provenance labels=${labelCsv} agent=${agentLabel}
293
- `);
294
- throw new Error(`Process provenance doctrine violated: write to ${labelCsv} requires an inbound :PRODUCED edge from a :Task (createdBy.agent='${agentLabel}'). See .docs/neo4j.md (Process provenance doctrine).`);
295
- }
296
- producedByTaskId = taskCandidates[0].targetNodeId;
297
- }
298
- let nodeRes;
299
- try {
300
- nodeRes = await tx.run(`CREATE (n:${labelStr} $props) RETURN elementId(n) AS nodeId, labels(n) AS nodeLabels`, { props: nodeProps });
301
- } catch (err) {
302
- const code = err?.code ?? "";
303
- if (code === "Neo.ClientError.Schema.ConstraintValidationFailed" && labels.includes("UserProfile")) {
304
- const accountIdProp = nodeProps.accountId;
305
- const userIdProp = nodeProps.userId;
306
- const acctSlice = typeof accountIdProp === "string" ? accountIdProp.slice(0, 8) : "unknown";
307
- const userSlice = typeof userIdProp === "string" ? userIdProp.slice(0, 8) : "unknown";
308
- process.stderr.write(`[graph-write] reject reason=user-profile-uniqueness-violation accountId=${acctSlice} userId=${userSlice} writer=${agentLabel}
309
- `);
310
- }
311
- throw err;
312
- }
313
- const nodeId = nodeRes.records[0].get("nodeId");
314
- const nodeLabels = nodeRes.records[0].get("nodeLabels");
315
- let edgesCreated = 0;
316
- for (const rel of relationships) {
317
- const type = rel.type.replace(/`/g, "");
318
- const q = rel.direction === "outgoing" ? `MATCH (a), (b) WHERE elementId(a) = $from AND elementId(b) = $to CREATE (a)-[:\`${type}\`]->(b)` : `MATCH (a), (b) WHERE elementId(a) = $from AND elementId(b) = $to CREATE (b)-[:\`${type}\`]->(a)`;
319
- const r = await tx.run(q, { from: nodeId, to: rel.targetNodeId });
320
- const created = r.summary.counters.updates().relationshipsCreated;
321
- if (created === 0) {
322
- process.stderr.write(`[graph-write] reject reason=unresolved-target-on-create labels=${labelCsv} agent=${agentLabel} relType=${rel.type} targetId=${rel.targetNodeId}
323
- `);
324
- throw new Error(`Write doctrine violated: relationship CREATE to target ${rel.targetNodeId} produced 0 edges (target likely deleted concurrently after pre-check). Transaction rolled back.`);
325
- }
326
- edgesCreated += created;
327
- }
328
- if (edgesCreated !== relationships.length) {
329
- process.stderr.write(`[graph-write] reject reason=edge-count-mismatch labels=${labelCsv} agent=${agentLabel} requested=${relationships.length} created=${edgesCreated}
330
- `);
331
- throw new Error(`Write doctrine violated: expected ${relationships.length} edges, created ${edgesCreated}. Transaction rolled back.`);
332
- }
333
- process.stderr.write(`[graph-write] accepted labels=${labelCsv} edges=${edgesCreated} createdByAgent=${createdBy.agent ?? "unknown"} createdByTool=${createdBy.tool ?? createdBy.source ?? "unknown"} producedByTask=${producedByTaskId ?? "none"}
334
- `);
335
- return { nodeId, labels: nodeLabels, edgesCreated };
336
- });
337
- }
338
- }
339
- });
131
+ } from "./chunk-ZT6LKDTP.js";
340
132
 
341
133
  // ../lib/graph-trash/dist/index.js
342
- var require_dist2 = __commonJS({
134
+ var require_dist = __commonJS({
343
135
  "../lib/graph-trash/dist/index.js"(exports) {
344
136
  "use strict";
345
137
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -835,7 +627,7 @@ var serveStatic = (options = { root: "" }) => {
835
627
  };
836
628
 
837
629
  // server/index.ts
838
- import { readFileSync as readFileSync16, existsSync as existsSync22, watchFile } from "fs";
630
+ import { readFileSync as readFileSync15, existsSync as existsSync21, watchFile } from "fs";
839
631
  import { resolve as resolve21, join as join9, basename as basename5 } from "path";
840
632
  import { homedir as homedir2 } from "os";
841
633
 
@@ -7492,7 +7284,7 @@ var app11 = new Hono();
7492
7284
  app11.post("/cancel", requireAdminSession, async (c) => {
7493
7285
  const session_key = c.var.sessionKey;
7494
7286
  try {
7495
- const { interruptClient: interruptClient2 } = await import("./client-pool-GBY5I2KQ.js");
7287
+ const { interruptClient: interruptClient2 } = await import("./client-pool-IQU6H43X.js");
7496
7288
  await interruptClient2(session_key);
7497
7289
  return c.json({ ok: true });
7498
7290
  } catch (err) {
@@ -8797,7 +8589,7 @@ var events_default = app20;
8797
8589
  // server/routes/admin/cloudflare.ts
8798
8590
  import { homedir } from "os";
8799
8591
  import { resolve as resolve14 } from "path";
8800
- import { readFileSync as readFileSync14 } from "fs";
8592
+ import { readFileSync as readFileSync13 } from "fs";
8801
8593
 
8802
8594
  // app/lib/dns-label.ts
8803
8595
  var VALID_LABEL = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
@@ -8835,214 +8627,6 @@ function addAliasDomain(hostname2) {
8835
8627
  writeFileSync6(ALIAS_DOMAINS_PATH, JSON.stringify([...existing], null, 2) + "\n", "utf-8");
8836
8628
  }
8837
8629
 
8838
- // app/lib/cloudflare-task-tracker.ts
8839
- import { readFileSync as readFileSync13, existsSync as existsSync17 } from "fs";
8840
- import { randomUUID as randomUUID7 } from "crypto";
8841
- var import_dist = __toESM(require_dist(), 1);
8842
- var CREATED_BY_AGENT = "cloudflare-setup-endpoint";
8843
- var TASK_KIND = "cloudflare-tunnel-login";
8844
- async function openCloudflareTask(params) {
8845
- const { accountId, conversationKey, inputsProvided } = params;
8846
- const taskId = randomUUID7();
8847
- const now = (/* @__PURE__ */ new Date()).toISOString();
8848
- const session = getSession();
8849
- try {
8850
- const conv = await session.run(
8851
- `MATCH (c:Conversation {sessionKey: $conversationKey, accountId: $accountId}) RETURN elementId(c) AS id LIMIT 1`,
8852
- { conversationKey, accountId }
8853
- );
8854
- if (conv.records.length === 0) {
8855
- throw new Error(
8856
- `cloudflare-task-tracker: no Conversation with sessionKey=${conversationKey.slice(-8)} for accountId \u2014 refusing to create an orphan Task`
8857
- );
8858
- }
8859
- const conversationElementId = conv.records[0].get("id");
8860
- const props = {
8861
- taskId,
8862
- accountId,
8863
- name: "Cloudflare tunnel login + setup",
8864
- description: `Deterministic cloudflare setup invoked by /api/admin/cloudflare/setup. inputsProvided=[${inputsProvided.join(", ")}]`,
8865
- status: "running",
8866
- priority: "normal",
8867
- kind: TASK_KIND,
8868
- inputsProvided,
8869
- startedAt: now,
8870
- createdAt: now,
8871
- updatedAt: now
8872
- };
8873
- const relationships = [
8874
- {
8875
- type: "RAISED_DURING",
8876
- direction: "outgoing",
8877
- targetNodeId: conversationElementId
8878
- }
8879
- ];
8880
- const result = await (0, import_dist.writeNodeWithEdges)({
8881
- session,
8882
- labels: ["Task"],
8883
- props,
8884
- relationships,
8885
- createdBy: {
8886
- agent: CREATED_BY_AGENT,
8887
- session: conversationKey,
8888
- tool: "cloudflare-setup-endpoint"
8889
- }
8890
- });
8891
- process.stderr.write(
8892
- `[task] action-start kind=${TASK_KIND} taskId=${taskId} raisedDuring=${conversationKey.slice(-8)}
8893
- `
8894
- );
8895
- return { taskId, taskElementId: result.nodeId };
8896
- } finally {
8897
- await session.close();
8898
- }
8899
- }
8900
- async function appendCloudflareSteps(taskId, accountId, streamLogPath) {
8901
- if (!existsSync17(streamLogPath)) return [];
8902
- let content;
8903
- try {
8904
- content = readFileSync13(streamLogPath, "utf-8");
8905
- } catch {
8906
- return [];
8907
- }
8908
- const steps = [];
8909
- for (const line of content.split(/\r?\n/)) {
8910
- const m = line.match(/\bphase_line\s+setup-tunnel\s+step=(\S+)/);
8911
- if (m) steps.push(m[1]);
8912
- }
8913
- if (steps.length === 0) return [];
8914
- const session = getSession();
8915
- try {
8916
- await session.run(
8917
- `MATCH (t:Task {taskId: $taskId, accountId: $accountId})
8918
- SET t.steps = coalesce(t.steps, []) + $steps,
8919
- t.updatedAt = $updatedAt`,
8920
- { taskId, accountId, steps, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }
8921
- );
8922
- for (const step of steps) {
8923
- process.stderr.write(
8924
- `[task] action-step kind=${TASK_KIND} taskId=${taskId} step=${step}
8925
- `
8926
- );
8927
- }
8928
- return steps;
8929
- } finally {
8930
- await session.close();
8931
- }
8932
- }
8933
- async function completeCloudflareTask(params) {
8934
- const { taskId, taskElementId, accountId, conversationKey, tunnelId, tunnelName, hostnames, status, errorMessage } = params;
8935
- const now = (/* @__PURE__ */ new Date()).toISOString();
8936
- if (status === "failed" && (!errorMessage || errorMessage.trim().length === 0)) {
8937
- throw new Error(
8938
- "cloudflare-task-tracker: errorMessage is required when status='failed' (Task 885 process-provenance contract)."
8939
- );
8940
- }
8941
- const session = getSession();
8942
- try {
8943
- if (status === "completed" && tunnelId && tunnelName) {
8944
- const conv = await session.run(
8945
- `MATCH (c:Conversation {sessionKey: $conversationKey, accountId: $accountId}) RETURN elementId(c) AS id LIMIT 1`,
8946
- { conversationKey, accountId }
8947
- );
8948
- const conversationElementId = conv.records.length > 0 ? conv.records[0].get("id") : null;
8949
- const tunnelProps = {
8950
- accountId,
8951
- tunnelId,
8952
- tunnelName,
8953
- createdAt: now,
8954
- updatedAt: now
8955
- };
8956
- const tunnelRels = [
8957
- { type: "PRODUCED", direction: "incoming", targetNodeId: taskElementId }
8958
- ];
8959
- if (conversationElementId) {
8960
- tunnelRels.push({
8961
- type: "RAISED_DURING",
8962
- direction: "outgoing",
8963
- targetNodeId: conversationElementId
8964
- });
8965
- }
8966
- const tunnelWrite = await (0, import_dist.writeNodeWithEdges)({
8967
- session,
8968
- labels: ["CloudflareTunnel"],
8969
- props: tunnelProps,
8970
- relationships: tunnelRels,
8971
- createdBy: {
8972
- agent: CREATED_BY_AGENT,
8973
- session: conversationKey,
8974
- tool: "cloudflare-setup-endpoint"
8975
- }
8976
- });
8977
- for (const h of hostnames ?? []) {
8978
- const hostRels = [
8979
- { type: "PRODUCED", direction: "incoming", targetNodeId: taskElementId },
8980
- {
8981
- type: "ROUTES_TO",
8982
- direction: "outgoing",
8983
- targetNodeId: tunnelWrite.nodeId
8984
- }
8985
- ];
8986
- await (0, import_dist.writeNodeWithEdges)({
8987
- session,
8988
- labels: ["CloudflareHostname"],
8989
- props: {
8990
- accountId,
8991
- hostnameValue: h.hostnameValue,
8992
- tunnelId,
8993
- isApex: h.isApex,
8994
- createdAt: now,
8995
- updatedAt: now
8996
- },
8997
- relationships: hostRels,
8998
- createdBy: {
8999
- agent: CREATED_BY_AGENT,
9000
- session: conversationKey,
9001
- tool: "cloudflare-setup-endpoint"
9002
- }
9003
- });
9004
- }
9005
- }
9006
- const setClauses = [
9007
- "t.status = $status",
9008
- "t.completedAt = $now",
9009
- "t.updatedAt = $now"
9010
- ];
9011
- const queryParams = { taskId, accountId, status, now };
9012
- if (status === "failed" && errorMessage) {
9013
- setClauses.push("t.errorMessage = $errorMessage");
9014
- queryParams.errorMessage = errorMessage;
9015
- }
9016
- const updateRes = await session.run(
9017
- `MATCH (t:Task {taskId: $taskId, accountId: $accountId})
9018
- SET ${setClauses.join(", ")}
9019
- RETURN size(coalesce(t.steps, [])) AS stepsCount`,
9020
- queryParams
9021
- );
9022
- const stepsCount = updateRes.records[0]?.get("stepsCount")?.toNumber?.() ?? 0;
9023
- process.stderr.write(
9024
- `[task] action-done kind=${TASK_KIND} taskId=${taskId} status=${status} stepsCount=${stepsCount}
9025
- `
9026
- );
9027
- } finally {
9028
- await session.close();
9029
- }
9030
- }
9031
- function readTunnelState(brandConfigDir) {
9032
- const statePath = `${process.env.HOME ?? ""}/${brandConfigDir}/cloudflared/tunnel.state`;
9033
- if (!existsSync17(statePath)) return null;
9034
- try {
9035
- const parsed = JSON.parse(readFileSync13(statePath, "utf-8"));
9036
- const tunnelId = typeof parsed.tunnelId === "string" ? parsed.tunnelId : null;
9037
- const tunnelName = typeof parsed.tunnelName === "string" ? parsed.tunnelName : null;
9038
- const domain = typeof parsed.domain === "string" ? parsed.domain : null;
9039
- if (!tunnelId || !tunnelName || !domain) return null;
9040
- return { tunnelId, tunnelName, domain };
9041
- } catch {
9042
- return null;
9043
- }
9044
- }
9045
-
9046
8630
  // server/routes/admin/cloudflare.ts
9047
8631
  var SETUP_TIMEOUT_MS = 10 * 60 * 1e3;
9048
8632
  var DOMAINS_TIMEOUT_MS = 40 * 1e3;
@@ -9050,7 +8634,7 @@ function loadBrandInfo() {
9050
8634
  const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve14(process.cwd(), "..");
9051
8635
  const brandPath = resolve14(platformRoot2, "config", "brand.json");
9052
8636
  try {
9053
- const parsed = JSON.parse(readFileSync14(brandPath, "utf-8"));
8637
+ const parsed = JSON.parse(readFileSync13(brandPath, "utf-8"));
9054
8638
  const hostname2 = typeof parsed.hostname === "string" && parsed.hostname ? parsed.hostname : "maxy";
9055
8639
  const configDir2 = typeof parsed.configDir === "string" && parsed.configDir ? parsed.configDir : ".maxy";
9056
8640
  return { hostname: hostname2, configDir: configDir2 };
@@ -9067,6 +8651,12 @@ function resolvePort() {
9067
8651
  }
9068
8652
  return n;
9069
8653
  }
8654
+ function isValidTunnelId(s) {
8655
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(s);
8656
+ }
8657
+ function isValidTunnelName(s) {
8658
+ return /^[A-Za-z0-9_.-]{1,64}$/.test(s);
8659
+ }
9070
8660
  function validateBody(body) {
9071
8661
  if (!body || typeof body !== "object") return { field: "request", message: "Invalid request body" };
9072
8662
  if (typeof body.adminLabel !== "string" || !isValidLabel(body.adminLabel)) {
@@ -9091,6 +8681,20 @@ function validateBody(body) {
9091
8681
  return { field: "request", message: "Apex must be a valid domain or omitted." };
9092
8682
  }
9093
8683
  }
8684
+ const idSet = typeof body.tunnelId === "string" && body.tunnelId.length > 0;
8685
+ const nameSet = typeof body.tunnelName === "string" && body.tunnelName.length > 0;
8686
+ if (!idSet && !nameSet) {
8687
+ return { field: "tunnel", message: "Pick an existing tunnel from the list, or type a name to create a new one." };
8688
+ }
8689
+ if (idSet && nameSet) {
8690
+ return { field: "tunnel", message: "Pick OR create \u2014 not both. Clear one of the tunnel inputs." };
8691
+ }
8692
+ if (idSet && !isValidTunnelId(body.tunnelId)) {
8693
+ return { field: "tunnel", message: "Selected tunnel UUID is malformed." };
8694
+ }
8695
+ if (nameSet && !isValidTunnelName(body.tunnelName)) {
8696
+ return { field: "tunnel", message: "New-tunnel name must be 1\u201364 characters of letters, digits, dot, dash, underscore." };
8697
+ }
9094
8698
  return null;
9095
8699
  }
9096
8700
  var app21 = new Hono();
@@ -9203,6 +8807,91 @@ ${result.stderr}` : ""}`;
9203
8807
  );
9204
8808
  return c.json(success, 200);
9205
8809
  });
8810
+ var TUNNELS_TIMEOUT_MS = 30 * 1e3;
8811
+ app21.get("/tunnels", requireAdminSession, async (c) => {
8812
+ const started = Date.now();
8813
+ const sessionKey = c.var.sessionKey;
8814
+ let correlationId;
8815
+ let sessionKeyTail;
8816
+ let streamLogPath;
8817
+ function tag() {
8818
+ if (!correlationId) return "";
8819
+ return ` conversationId=${correlationId} session_key=${sessionKeyTail ?? "unknown"}`;
8820
+ }
8821
+ function log(line) {
8822
+ console.log(`[cloudflare-tunnels] ${line}${tag()}`);
8823
+ }
8824
+ function logErr(line) {
8825
+ console.error(`[cloudflare-tunnels] ${line}${tag()}`);
8826
+ }
8827
+ function err(field, message, output) {
8828
+ logErr(`phase=error field=${field} reason="${message.slice(0, 160).replace(/"/g, "'")}"`);
8829
+ if (streamLogPath) {
8830
+ writeRouteMilestone(
8831
+ streamLogPath,
8832
+ "cloudflare-tunnels",
8833
+ `phase=error field=${field} reason="${message.slice(0, 160).replace(/"/g, "'")}"`
8834
+ );
8835
+ }
8836
+ const body = { ok: false, field, message, output, correlationId, streamLogPath };
8837
+ return c.json(body, 200);
8838
+ }
8839
+ const accountId = getAccountIdForSession(sessionKey);
8840
+ correlationId = getConversationIdForSession(sessionKey);
8841
+ sessionKeyTail = sessionKey.slice(-8);
8842
+ if (!accountId) return err("session", "No account bound to session \u2014 refresh chat.");
8843
+ if (!correlationId) return err("session", "No active conversation for session \u2014 refresh chat.");
8844
+ streamLogPath = streamLogPathFor(accountId, correlationId).streamLogPath;
8845
+ const brand = loadBrandInfo();
8846
+ const certPath = resolve14(homedir(), brand.configDir, "cloudflared", "cert.pem");
8847
+ const { existsSync: existsSync22 } = await import("fs");
8848
+ if (!existsSync22(certPath)) {
8849
+ return err("cert", `Cloudflare origin certificate is not on disk yet (${certPath}). Complete the Cloudflare login first by submitting the form once \u2014 the OAuth flow writes cert.pem.`);
8850
+ }
8851
+ const result = await runFormSpawn({
8852
+ scriptPath: "cloudflared",
8853
+ args: ["--origincert", certPath, "tunnel", "list", "--output", "json"],
8854
+ streamLogPath,
8855
+ correlationId,
8856
+ timeoutMs: TUNNELS_TIMEOUT_MS,
8857
+ log,
8858
+ logErr,
8859
+ broadcast: broadcastToConversation
8860
+ });
8861
+ const combined = `${result.stdout}${result.stderr ? `
8862
+ ---
8863
+ ${result.stderr}` : ""}`;
8864
+ if (result.code !== 0) {
8865
+ const message = result.timedOut ? `cloudflared tunnel list timed out after ${TUNNELS_TIMEOUT_MS / 1e3}s` : `cloudflared tunnel list exited with code ${result.code ?? "null"}${result.signal ? ` (signal ${result.signal})` : ""}`;
8866
+ return err("script", message, combined);
8867
+ }
8868
+ let tunnels;
8869
+ try {
8870
+ const parsed = JSON.parse(result.stdout.trim());
8871
+ if (!Array.isArray(parsed)) throw new Error("cloudflared response is not an array");
8872
+ tunnels = parsed.filter((t) => t.deleted_at == null).map((t) => {
8873
+ if (typeof t.id !== "string" || typeof t.name !== "string") {
8874
+ throw new Error("tunnel entry missing id or name");
8875
+ }
8876
+ return { id: t.id, name: t.name };
8877
+ });
8878
+ } catch (e) {
8879
+ return err(
8880
+ "script",
8881
+ `cloudflared returned malformed JSON: ${e instanceof Error ? e.message : String(e)}`,
8882
+ combined
8883
+ );
8884
+ }
8885
+ const total = Date.now() - started;
8886
+ log(`phase=response-sent total_ms=${total} count=${tunnels.length}`);
8887
+ writeRouteMilestone(
8888
+ streamLogPath,
8889
+ "cloudflare-tunnels",
8890
+ `phase=response-sent total_ms=${total} count=${tunnels.length} source=cloudflared`
8891
+ );
8892
+ const success = { ok: true, tunnels, correlationId, streamLogPath };
8893
+ return c.json(success, 200);
8894
+ });
9206
8895
  app21.post("/setup", requireAdminSession, async (c) => {
9207
8896
  const started = Date.now();
9208
8897
  const sessionKey = c.var.sessionKey;
@@ -9297,7 +8986,15 @@ app21.post("/setup", requireAdminSession, async (c) => {
9297
8986
  cloudflareTask = await openCloudflareTask({
9298
8987
  accountId,
9299
8988
  conversationKey: sessionKey,
9300
- inputsProvided: ["adminLabel", "adminDomain", publicFqdn ? "publicLabel" : null, apex ? "apex" : null, "password"].filter((s) => typeof s === "string")
8989
+ inputsProvided: ["adminLabel", "adminDomain", publicFqdn ? "publicLabel" : null, apex ? "apex" : null, "password"].filter((s) => typeof s === "string"),
8990
+ inputs: {
8991
+ adminLabel: body.adminLabel,
8992
+ adminDomain: body.adminDomain,
8993
+ publicLabel: body.publicLabel,
8994
+ publicDomain: body.publicDomain,
8995
+ apex: body.apex
8996
+ },
8997
+ messageId: typeof body.messageId === "string" && body.messageId.length > 0 ? body.messageId : void 0
9301
8998
  });
9302
8999
  log(`phase=task-opened taskId=${cloudflareTask.taskId}`);
9303
9000
  } catch (e) {
@@ -9306,7 +9003,13 @@ app21.post("/setup", requireAdminSession, async (c) => {
9306
9003
  let actionId;
9307
9004
  let unit;
9308
9005
  try {
9309
- const launched = await launchAction("cloudflare-setup", { positional: args, streamLogPath, accountDir });
9006
+ const launched = await launchAction("cloudflare-setup", {
9007
+ positional: args,
9008
+ streamLogPath,
9009
+ accountDir,
9010
+ tunnelId: typeof body.tunnelId === "string" && body.tunnelId.length > 0 ? body.tunnelId : void 0,
9011
+ tunnelName: typeof body.tunnelName === "string" && body.tunnelName.length > 0 ? body.tunnelName : void 0
9012
+ });
9310
9013
  actionId = launched.actionId;
9311
9014
  unit = launched.unit;
9312
9015
  } catch (e) {
@@ -9324,14 +9027,14 @@ app21.post("/setup", requireAdminSession, async (c) => {
9324
9027
  if (!status || status.execMainStatus !== 0) {
9325
9028
  logErr(`phase=post-exit-skipped reason=${status ? `exit=${status.execMainStatus}` : "unit-gone"} id=${actionId}`);
9326
9029
  try {
9327
- const exitReason = status ? `script exited with status ${status.execMainStatus}` : "systemd unit vanished before exit recorded";
9030
+ const diagnostic = status ? `${CLOUDFLARE_TASK_DIAGNOSTICS.scriptExitedNonzero} exit=${status.execMainStatus}` : CLOUDFLARE_TASK_DIAGNOSTICS.endpointDiedPreReconcile;
9328
9031
  await completeCloudflareTask({
9329
9032
  taskId: cloudflareTask.taskId,
9330
9033
  taskElementId: cloudflareTask.taskElementId,
9331
9034
  accountId,
9332
9035
  conversationKey: sessionKey,
9333
9036
  status: "failed",
9334
- errorMessage: exitReason
9037
+ errorMessage: diagnostic
9335
9038
  });
9336
9039
  } catch (e) {
9337
9040
  logErr(`phase=task-close-failed status=failed reason="${e instanceof Error ? e.message : String(e)}"`);
@@ -10071,7 +9774,7 @@ app22.delete("/", requireAdminSession, async (c) => {
10071
9774
  var files_default = app22;
10072
9775
 
10073
9776
  // ../lib/graph-search/src/index.ts
10074
- var import_dist2 = __toESM(require_dist2());
9777
+ var import_dist = __toESM(require_dist());
10075
9778
  import { int } from "neo4j-driver";
10076
9779
  var VECTOR_WEIGHT = 0.7;
10077
9780
  var BM25_WEIGHT = 0.3;
@@ -10126,7 +9829,7 @@ async function bm25Only(session, params) {
10126
9829
  ${scopeClause}
10127
9830
  ${agentClause}
10128
9831
  ${labelClause}
10129
- AND ${(0, import_dist2.notTrashed)("node")}
9832
+ AND ${(0, import_dist.notTrashed)("node")}
10130
9833
  ${kwClause}
10131
9834
  RETURN node, score, labels(node) AS nodeLabels, elementId(node) AS nodeId
10132
9835
  ORDER BY score DESC
@@ -10216,7 +9919,7 @@ async function hybrid(session, embed2, params) {
10216
9919
  WHERE node.accountId = $accountId
10217
9920
  ${scopeClause}
10218
9921
  ${agentClause}
10219
- AND ${(0, import_dist2.notTrashed)("node")}
9922
+ AND ${(0, import_dist.notTrashed)("node")}
10220
9923
  ${keywordClause}
10221
9924
  RETURN node, score, labels(node) AS nodeLabels, elementId(node) AS nodeId
10222
9925
  ORDER BY score DESC
@@ -10277,7 +9980,7 @@ async function hybrid(session, embed2, params) {
10277
9980
  const propResult = await session.run(
10278
9981
  `MATCH (node)
10279
9982
  WHERE node.accountId = $accountId
10280
- AND ${(0, import_dist2.notTrashed)("node")}
9983
+ AND ${(0, import_dist.notTrashed)("node")}
10281
9984
  AND node.keywords IS NOT NULL
10282
9985
  AND ANY(kw IN $kwSubs WHERE ANY(nk IN node.keywords WHERE toLower(nk) = kw))
10283
9986
  ${propScopeClause}
@@ -10334,7 +10037,7 @@ async function hybrid(session, embed2, params) {
10334
10037
  `UNWIND $nodeIds AS nid
10335
10038
  MATCH (n)-[r]-(related)
10336
10039
  WHERE elementId(n) = nid
10337
- AND ${(0, import_dist2.notTrashed)("related")}
10040
+ AND ${(0, import_dist.notTrashed)("related")}
10338
10041
  ${expandScopeClause}
10339
10042
  ${expandAgentClause}
10340
10043
  WITH nid, n, r, related
@@ -10779,6 +10482,12 @@ async function handleDefault(c, accountId) {
10779
10482
  var NEIGHBOURHOOD_SEARCH_DEFAULT_LIMIT = 100;
10780
10483
  var NEIGHBOURHOOD_SEARCH_MAX_LIMIT = 2e3;
10781
10484
  var NEIGHBOURHOOD_LIMIT = 2e3;
10485
+ var CLUSTER_CONVERSATION_LABELS = /* @__PURE__ */ new Set([
10486
+ "Conversation",
10487
+ "AdminConversation",
10488
+ "PublicConversation"
10489
+ ]);
10490
+ var MAX_CLUSTER_MESSAGES = 200;
10782
10491
  async function handleNeighbourhood(c, accountId) {
10783
10492
  const elementId = c.req.query("elementId");
10784
10493
  if (!elementId) {
@@ -10816,7 +10525,12 @@ async function handleNeighbourhood(c, accountId) {
10816
10525
  // `neo4j.int` keeps the LIMIT value as a 64-bit Integer on the wire —
10817
10526
  // bare JS `number` works on Pi 4 today but errors on driver versions
10818
10527
  // that strictly type-check LIMIT clauses against Long.
10819
- neighbourhoodLimit: neo4j2.int(NEIGHBOURHOOD_LIMIT)
10528
+ neighbourhoodLimit: neo4j2.int(NEIGHBOURHOOD_LIMIT),
10529
+ // Task 886 §C — same Long-int discipline for the cluster-expand cap.
10530
+ // Used by NEIGHBOURHOOD_CYPHER's `siblingMessages[0..$clusterMessagesCap]`
10531
+ // slice. The search-intersect variant ignores it (cluster-expand
10532
+ // does not apply when search is narrowing the canvas).
10533
+ clusterMessagesCap: neo4j2.int(MAX_CLUSTER_MESSAGES)
10820
10534
  };
10821
10535
  if (allowedIds !== null) cypherParams.allowedIds = allowedIds;
10822
10536
  const result = await session.executeRead(async (tx) => {
@@ -10839,6 +10553,15 @@ async function handleNeighbourhood(c, accountId) {
10839
10553
  console.error(
10840
10554
  `[graph-page] load mode=neighbourhood account=${accountId} agentActions=${includeAgentActions} elementId=${elementId} nodes=${nodes.length} edges=${edges.length} ms=${elapsed}`
10841
10555
  );
10556
+ if (allowedIds === null) {
10557
+ const conversationNode = nodes.find((n) => n.labels.some((lbl) => CLUSTER_CONVERSATION_LABELS.has(lbl)));
10558
+ const messageCount = nodes.filter((n) => n.labels.includes("Message")).length;
10559
+ if (conversationNode && messageCount >= 2) {
10560
+ console.error(
10561
+ `[graph] cluster-expand kind=conversation conversationId=${conversationNode.id} messageCount=${messageCount} reason=click-target-is-${nodes[0]?.labels.includes("Message") ? "message" : "conversation"}`
10562
+ );
10563
+ }
10564
+ }
10842
10565
  if (allowedIds !== null) {
10843
10566
  console.error(
10844
10567
  `[graph-page] neighbourhood-search-intersect q="${q}" allowed=${allowedIds.length} root=${elementId} rendered=${nodes.length}`
@@ -10939,15 +10662,50 @@ var NEIGHBOURHOOD_CYPHER = `
10939
10662
  AND n.accountId = $accountId
10940
10663
  AND NOT n:Trashed
10941
10664
  AND n.deletedAt IS NULL
10665
+
10666
+ // Resolve the owning Conversation: either n itself if it carries a
10667
+ // Conversation sublabel, or the Conversation reached by one PART_OF
10668
+ // hop (Messages \u2192 Conversation). For non-cluster roots both branches
10669
+ // null out and \`conv\` stays null.
10670
+ OPTIONAL MATCH (n)-[:PART_OF]->(convFromMsg:Conversation)
10671
+ WHERE convFromMsg.accountId = $accountId
10672
+ AND NOT convFromMsg:Trashed
10673
+ AND convFromMsg.deletedAt IS NULL
10674
+ WITH n, CASE
10675
+ WHEN any(lbl IN labels(n) WHERE lbl IN ['Conversation','AdminConversation','PublicConversation']) THEN n
10676
+ ELSE convFromMsg
10677
+ END AS conv
10678
+
10679
+ // Pull all sibling messages of that conversation (or all messages of
10680
+ // n itself when n is a Conversation). EXCLUDE n from the collection
10681
+ // so the later \`[n] + \u2026\` doesn't double-count.
10682
+ OPTIONAL MATCH (conv)<-[:PART_OF]-(siblingMsg:Message)
10683
+ WHERE siblingMsg.accountId = $accountId
10684
+ AND NOT siblingMsg:Trashed
10685
+ AND siblingMsg.deletedAt IS NULL
10686
+ AND elementId(siblingMsg) <> elementId(n)
10687
+ WITH n, conv, collect(DISTINCT siblingMsg) AS siblingMessages
10688
+ WITH n, conv, siblingMessages[0..$clusterMessagesCap] AS clusterMessages
10689
+
10942
10690
  OPTIONAL MATCH (n)-[]-(m)
10943
10691
  WHERE m.accountId = $accountId
10944
10692
  AND NOT m:Trashed
10945
10693
  AND m.deletedAt IS NULL
10946
10694
  AND NOT any(lbl IN labels(m) WHERE lbl IN $agentActionLabels)
10947
- WITH n, m
10695
+ WITH n, conv, clusterMessages, m
10948
10696
  LIMIT $neighbourhoodLimit
10949
- WITH n, collect(DISTINCT m) AS neighbours
10950
- WITH [n] + [x IN neighbours WHERE x IS NOT NULL] AS windowNodes
10697
+ WITH n, conv, clusterMessages, collect(DISTINCT m) AS neighbours
10698
+
10699
+ // Compose the window: root + (conv if non-null and not already n) +
10700
+ // cluster messages + 1-hop neighbours not already in the cluster.
10701
+ WITH [n]
10702
+ + (CASE WHEN conv IS NOT NULL AND elementId(conv) <> elementId(n) THEN [conv] ELSE [] END)
10703
+ + clusterMessages
10704
+ + [x IN neighbours WHERE x IS NOT NULL
10705
+ AND elementId(x) <> elementId(n)
10706
+ AND (conv IS NULL OR elementId(x) <> elementId(conv))
10707
+ AND none(c IN clusterMessages WHERE elementId(c) = elementId(x))]
10708
+ AS windowNodes
10951
10709
  WITH windowNodes, [x IN windowNodes | elementId(x)] AS windowIds
10952
10710
  UNWIND windowNodes AS w
10953
10711
  OPTIONAL MATCH (w)-[r]-(o)
@@ -11510,7 +11268,7 @@ var adherence_default = app30;
11510
11268
  import neo4j3 from "neo4j-driver";
11511
11269
  import { readFile as readFile5, readdir as readdir3, stat as stat5 } from "fs/promises";
11512
11270
  import { resolve as resolve17, relative as relative2, isAbsolute } from "path";
11513
- import { existsSync as existsSync18 } from "fs";
11271
+ import { existsSync as existsSync17 } from "fs";
11514
11272
  var LIMIT = 50;
11515
11273
  var TEXT_MIME_PREFIXES = ["text/", "application/json", "application/markdown"];
11516
11274
  var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
@@ -11658,7 +11416,7 @@ async function fetchAgentTemplateRows(accountDir) {
11658
11416
  async function unionSpecialistFilenames(overrideDir, bundledDir) {
11659
11417
  const names = /* @__PURE__ */ new Set();
11660
11418
  for (const dir of [overrideDir, bundledDir]) {
11661
- if (!existsSync18(dir)) continue;
11419
+ if (!existsSync17(dir)) continue;
11662
11420
  try {
11663
11421
  const entries = await readdir3(dir);
11664
11422
  for (const entry of entries) {
@@ -11673,7 +11431,7 @@ async function unionSpecialistFilenames(overrideDir, bundledDir) {
11673
11431
  }
11674
11432
  async function readAgentTemplateRow(inp) {
11675
11433
  let chosenPath = null;
11676
- if (existsSync18(inp.overridePath)) {
11434
+ if (existsSync17(inp.overridePath)) {
11677
11435
  try {
11678
11436
  validateFilePathInAccount(inp.overridePath, inp.overrideRoot);
11679
11437
  chosenPath = inp.overridePath;
@@ -11684,7 +11442,7 @@ async function readAgentTemplateRow(inp) {
11684
11442
  );
11685
11443
  return null;
11686
11444
  }
11687
- } else if (existsSync18(inp.bundledPath)) {
11445
+ } else if (existsSync17(inp.bundledPath)) {
11688
11446
  if (!isWithin(inp.bundledPath, inp.bundledRoot)) {
11689
11447
  console.error(
11690
11448
  `[admin/sidebar-artefacts] agent-template-read-failed agent=${inp.displayName} kind=${inp.logName} error="bundled path outside PLATFORM_ROOT"`
@@ -11726,7 +11484,7 @@ var sidebar_artefacts_default = app31;
11726
11484
  // server/routes/admin/sidebar-artefact-save.ts
11727
11485
  import { mkdir as mkdir4, readdir as readdir4, stat as stat6, writeFile as writeFile5 } from "fs/promises";
11728
11486
  import { resolve as resolve18 } from "path";
11729
- import { existsSync as existsSync19 } from "fs";
11487
+ import { existsSync as existsSync18 } from "fs";
11730
11488
  var ADMIN_AGENT_FILES2 = /* @__PURE__ */ new Set(["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"]);
11731
11489
  var UUID_RE5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
11732
11490
  var app32 = new Hono();
@@ -11790,7 +11548,7 @@ async function resolveSavePath(id, accountId, accountDir) {
11790
11548
  }
11791
11549
  if (UUID_RE5.test(id)) {
11792
11550
  const dir = resolve18(ATTACHMENTS_ROOT, accountId, id);
11793
- if (!existsSync19(dir)) {
11551
+ if (!existsSync18(dir)) {
11794
11552
  return { kind: "reject", status: 400, reason: "not-found" };
11795
11553
  }
11796
11554
  try {
@@ -11814,7 +11572,7 @@ var sidebar_artefact_save_default = app32;
11814
11572
 
11815
11573
  // server/routes/admin/sidebar-artefact-content.ts
11816
11574
  import { readFile as readFile6, readdir as readdir5 } from "fs/promises";
11817
- import { existsSync as existsSync20 } from "fs";
11575
+ import { existsSync as existsSync19 } from "fs";
11818
11576
  import { resolve as resolve19 } from "path";
11819
11577
  var UUID_RE6 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
11820
11578
  var app33 = new Hono();
@@ -11828,7 +11586,7 @@ app33.get("/", requireAdminSession, async (c) => {
11828
11586
  return new Response("Not found", { status: 404 });
11829
11587
  }
11830
11588
  const dir = resolve19(ATTACHMENTS_ROOT, accountId, id);
11831
- if (!existsSync20(dir)) {
11589
+ if (!existsSync19(dir)) {
11832
11590
  console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
11833
11591
  return new Response("Not found", { status: 404 });
11834
11592
  }
@@ -11890,7 +11648,7 @@ app34.route("/sidebar-artefact-content", sidebar_artefact_content_default);
11890
11648
  var admin_default = app34;
11891
11649
 
11892
11650
  // server/routes/sites.ts
11893
- import { existsSync as existsSync21, readFileSync as readFileSync15, realpathSync as realpathSync5, statSync as statSync5 } from "fs";
11651
+ import { existsSync as existsSync20, readFileSync as readFileSync14, realpathSync as realpathSync5, statSync as statSync5 } from "fs";
11894
11652
  import { resolve as resolve20 } from "path";
11895
11653
  var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
11896
11654
  var MIME = {
@@ -11957,7 +11715,7 @@ app35.get("/:rel{.*}", (c) => {
11957
11715
  }
11958
11716
  let stat7;
11959
11717
  try {
11960
- stat7 = existsSync21(filePath) ? statSync5(filePath) : null;
11718
+ stat7 = existsSync20(filePath) ? statSync5(filePath) : null;
11961
11719
  } catch {
11962
11720
  stat7 = null;
11963
11721
  }
@@ -11970,7 +11728,7 @@ app35.get("/:rel{.*}", (c) => {
11970
11728
  console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
11971
11729
  return c.text("Forbidden", 403);
11972
11730
  }
11973
- if (!existsSync21(filePath)) {
11731
+ if (!existsSync20(filePath)) {
11974
11732
  console.error(`[sites] not-found path=${reqPath} status=404`);
11975
11733
  return c.text("Not found", 404);
11976
11734
  }
@@ -11989,7 +11747,7 @@ app35.get("/:rel{.*}", (c) => {
11989
11747
  }
11990
11748
  let body;
11991
11749
  try {
11992
- body = readFileSync15(realPath);
11750
+ body = readFileSync14(realPath);
11993
11751
  } catch (err) {
11994
11752
  const code = err?.code;
11995
11753
  if (code === "EISDIR") {
@@ -12123,12 +11881,12 @@ function clientFrom(c) {
12123
11881
  var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT || "";
12124
11882
  var BRAND_JSON_PATH = PLATFORM_ROOT7 ? join9(PLATFORM_ROOT7, "config", "brand.json") : "";
12125
11883
  var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", domain: "getmaxy.com" };
12126
- if (BRAND_JSON_PATH && !existsSync22(BRAND_JSON_PATH)) {
11884
+ if (BRAND_JSON_PATH && !existsSync21(BRAND_JSON_PATH)) {
12127
11885
  console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
12128
11886
  }
12129
- if (BRAND_JSON_PATH && existsSync22(BRAND_JSON_PATH)) {
11887
+ if (BRAND_JSON_PATH && existsSync21(BRAND_JSON_PATH)) {
12130
11888
  try {
12131
- const parsed = JSON.parse(readFileSync16(BRAND_JSON_PATH, "utf-8"));
11889
+ const parsed = JSON.parse(readFileSync15(BRAND_JSON_PATH, "utf-8"));
12132
11890
  BRAND = { ...BRAND, ...parsed };
12133
11891
  } catch (err) {
12134
11892
  console.error(`[brand] Failed to parse brand.json: ${err.message}`);
@@ -12150,8 +11908,8 @@ var brandLoginOpts = {
12150
11908
  var ALIAS_DOMAINS_PATH2 = join9(homedir2(), BRAND.configDir, "alias-domains.json");
12151
11909
  function loadAliasDomains() {
12152
11910
  try {
12153
- if (!existsSync22(ALIAS_DOMAINS_PATH2)) return null;
12154
- const parsed = JSON.parse(readFileSync16(ALIAS_DOMAINS_PATH2, "utf-8"));
11911
+ if (!existsSync21(ALIAS_DOMAINS_PATH2)) return null;
11912
+ const parsed = JSON.parse(readFileSync15(ALIAS_DOMAINS_PATH2, "utf-8"));
12155
11913
  if (!Array.isArray(parsed)) {
12156
11914
  console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
12157
11915
  return null;
@@ -12497,14 +12255,14 @@ app36.get("/agent-assets/:slug/:filename", (c) => {
12497
12255
  console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
12498
12256
  return c.text("Forbidden", 403);
12499
12257
  }
12500
- if (!existsSync22(filePath)) {
12258
+ if (!existsSync21(filePath)) {
12501
12259
  console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
12502
12260
  return c.text("Not found", 404);
12503
12261
  }
12504
12262
  const ext = "." + filename.split(".").pop()?.toLowerCase();
12505
12263
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
12506
12264
  console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
12507
- const body = readFileSync16(filePath);
12265
+ const body = readFileSync15(filePath);
12508
12266
  return c.body(body, 200, {
12509
12267
  "Content-Type": contentType,
12510
12268
  "Cache-Control": "public, max-age=3600"
@@ -12527,14 +12285,14 @@ app36.get("/generated/:filename", (c) => {
12527
12285
  console.error(`[generated] serve file=${filename} status=403`);
12528
12286
  return c.text("Forbidden", 403);
12529
12287
  }
12530
- if (!existsSync22(filePath)) {
12288
+ if (!existsSync21(filePath)) {
12531
12289
  console.error(`[generated] serve file=${filename} status=404`);
12532
12290
  return c.text("Not found", 404);
12533
12291
  }
12534
12292
  const ext = "." + filename.split(".").pop()?.toLowerCase();
12535
12293
  const contentType = IMAGE_MIME[ext] || "application/octet-stream";
12536
12294
  console.log(`[generated] serve file=${filename} status=200`);
12537
- const body = readFileSync16(filePath);
12295
+ const body = readFileSync15(filePath);
12538
12296
  return c.body(body, 200, {
12539
12297
  "Content-Type": contentType,
12540
12298
  "Cache-Control": "public, max-age=86400"
@@ -12544,9 +12302,9 @@ app36.route("/sites", sites_default);
12544
12302
  var htmlCache = /* @__PURE__ */ new Map();
12545
12303
  var brandLogoPath = "/brand/maxy-monochrome.png";
12546
12304
  var brandIconPath = "/brand/maxy-monochrome.png";
12547
- if (BRAND_JSON_PATH && existsSync22(BRAND_JSON_PATH)) {
12305
+ if (BRAND_JSON_PATH && existsSync21(BRAND_JSON_PATH)) {
12548
12306
  try {
12549
- const fullBrand = JSON.parse(readFileSync16(BRAND_JSON_PATH, "utf-8"));
12307
+ const fullBrand = JSON.parse(readFileSync15(BRAND_JSON_PATH, "utf-8"));
12550
12308
  if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
12551
12309
  brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
12552
12310
  } catch {
@@ -12564,8 +12322,8 @@ function readInstalledVersion() {
12564
12322
  try {
12565
12323
  if (!PLATFORM_ROOT7) return "unknown";
12566
12324
  const versionFile = join9(PLATFORM_ROOT7, "config", `.${BRAND.hostname}-version`);
12567
- if (!existsSync22(versionFile)) return "unknown";
12568
- const content = readFileSync16(versionFile, "utf-8").trim();
12325
+ if (!existsSync21(versionFile)) return "unknown";
12326
+ const content = readFileSync15(versionFile, "utf-8").trim();
12569
12327
  return content || "unknown";
12570
12328
  } catch {
12571
12329
  return "unknown";
@@ -12606,7 +12364,7 @@ var clientErrorReporterScript = `<script>
12606
12364
  function cachedHtml(file) {
12607
12365
  let html = htmlCache.get(file);
12608
12366
  if (!html) {
12609
- html = readFileSync16(resolve21(process.cwd(), "public", file), "utf-8");
12367
+ html = readFileSync15(resolve21(process.cwd(), "public", file), "utf-8");
12610
12368
  const productNameEsc = escapeHtml(BRAND.productName);
12611
12369
  html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
12612
12370
  html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
@@ -12625,13 +12383,13 @@ function loadBrandingCache(agentSlug) {
12625
12383
  const configDir2 = join9(homedir2(), BRAND.configDir);
12626
12384
  try {
12627
12385
  const accountJsonPath = join9(configDir2, "account.json");
12628
- if (!existsSync22(accountJsonPath)) return null;
12629
- const account = JSON.parse(readFileSync16(accountJsonPath, "utf-8"));
12386
+ if (!existsSync21(accountJsonPath)) return null;
12387
+ const account = JSON.parse(readFileSync15(accountJsonPath, "utf-8"));
12630
12388
  const accountId = account.accountId;
12631
12389
  if (!accountId) return null;
12632
12390
  const cachePath = join9(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
12633
- if (!existsSync22(cachePath)) return null;
12634
- return JSON.parse(readFileSync16(cachePath, "utf-8"));
12391
+ if (!existsSync21(cachePath)) return null;
12392
+ return JSON.parse(readFileSync15(cachePath, "utf-8"));
12635
12393
  } catch {
12636
12394
  return null;
12637
12395
  }
@@ -12640,8 +12398,8 @@ function resolveDefaultSlug() {
12640
12398
  try {
12641
12399
  const configDir2 = join9(homedir2(), BRAND.configDir);
12642
12400
  const accountJsonPath = join9(configDir2, "account.json");
12643
- if (!existsSync22(accountJsonPath)) return null;
12644
- const account = JSON.parse(readFileSync16(accountJsonPath, "utf-8"));
12401
+ if (!existsSync21(accountJsonPath)) return null;
12402
+ const account = JSON.parse(readFileSync15(accountJsonPath, "utf-8"));
12645
12403
  return account.defaultAgent || null;
12646
12404
  } catch {
12647
12405
  return null;
@@ -12714,7 +12472,7 @@ app36.use("/vnc-popout.html", logViewerFetch);
12714
12472
  app36.get("/vnc-popout.html", (c) => {
12715
12473
  let html = htmlCache.get("vnc-popout.html");
12716
12474
  if (!html) {
12717
- html = readFileSync16(resolve21(process.cwd(), "public", "vnc-popout.html"), "utf-8");
12475
+ html = readFileSync15(resolve21(process.cwd(), "public", "vnc-popout.html"), "utf-8");
12718
12476
  const name = escapeHtml(BRAND.productName);
12719
12477
  html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
12720
12478
  html = html.replace("</head>", ` ${brandScript}
@@ -12804,8 +12562,8 @@ try {
12804
12562
  (async () => {
12805
12563
  try {
12806
12564
  let userId = "";
12807
- if (existsSync22(USERS_FILE)) {
12808
- const users = JSON.parse(readFileSync16(USERS_FILE, "utf-8").trim() || "[]");
12565
+ if (existsSync21(USERS_FILE)) {
12566
+ const users = JSON.parse(readFileSync15(USERS_FILE, "utf-8").trim() || "[]");
12809
12567
  userId = users[0]?.userId ?? "";
12810
12568
  }
12811
12569
  await backfillNullUserIdConversations(userId);
@@ -12835,6 +12593,29 @@ var bootEntitlement = bootAccountConfig ? resolveEntitlement(
12835
12593
  }
12836
12594
  ) : null;
12837
12595
  autoDeliverPremiumPlugins(bootEntitlement?.purchasedPlugins ?? void 0);
12596
+ (async () => {
12597
+ if (!bootAccount) return;
12598
+ try {
12599
+ const { recoverRunningCloudflareTasks } = await import("./cloudflare-task-tracker-Q4X5BYR7.js");
12600
+ const result = await recoverRunningCloudflareTasks(
12601
+ bootAccount.accountId,
12602
+ configDirForWhatsApp,
12603
+ // No conversationKey at boot — the tracker's writeNodeWithEdges path
12604
+ // requires it for the Conversation join, but the recovery path is
12605
+ // operating on Tasks whose Conversation is already linked. Pass null
12606
+ // and let the tracker pick up the linked Conversation via the Task's
12607
+ // existing edge.
12608
+ null
12609
+ );
12610
+ if (result.scanned > 0) {
12611
+ console.error(
12612
+ `[task] reconciler-startup kind=cloudflare-tunnel-login scanned=${result.scanned} resolved=${result.resolved.length}`
12613
+ );
12614
+ }
12615
+ } catch (err) {
12616
+ console.error(`[task] reconciler-startup failed: ${err instanceof Error ? err.message : String(err)}`);
12617
+ }
12618
+ })();
12838
12619
  var bootEnabled = Array.isArray(bootAccountConfig?.enabledPlugins) ? bootAccountConfig.enabledPlugins : [];
12839
12620
  var bootDelivered = [];
12840
12621
  var bootDistMissing = [];