@rubytech/create-realagent 1.0.818 → 1.0.820
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.
- package/package.json +1 -1
- package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.d.ts +2 -0
- package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.d.ts.map +1 -0
- package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.js +168 -0
- package/payload/platform/lib/graph-write/dist/__tests__/action-provenance-gate.test.js.map +1 -0
- package/payload/platform/lib/graph-write/dist/index.d.ts +28 -5
- package/payload/platform/lib/graph-write/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-write/dist/index.js +97 -3
- package/payload/platform/lib/graph-write/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-write/src/__tests__/action-provenance-gate.test.ts +191 -0
- package/payload/platform/lib/graph-write/src/index.ts +90 -9
- package/payload/platform/neo4j/schema.cypher +24 -0
- package/payload/platform/plugins/admin/skills/onboarding/SKILL.md +25 -8
- package/payload/platform/plugins/cloudflare/PLUGIN.md +1 -1
- package/payload/platform/plugins/cloudflare/scripts/setup-tunnel.sh +70 -20
- package/payload/platform/plugins/docs/references/cloudflare.md +1 -1
- package/payload/platform/plugins/docs/references/graph.md +20 -0
- package/payload/platform/plugins/docs/references/internals.md +16 -0
- package/payload/platform/plugins/email/PLUGIN.md +2 -0
- package/payload/platform/plugins/memory/PLUGIN.md +6 -0
- package/payload/platform/plugins/memory/mcp/dist/index.js +15 -4
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts +8 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js +26 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-write.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts +20 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js +40 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js.map +1 -1
- package/payload/platform/plugins/memory/references/schema-base.md +8 -0
- package/payload/platform/plugins/tasks/PLUGIN.md +2 -2
- package/payload/platform/plugins/tasks/mcp/dist/index.js +10 -5
- package/payload/platform/plugins/tasks/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-create.d.ts +27 -1
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-create.d.ts.map +1 -1
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-create.js +45 -2
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-create.js.map +1 -1
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-update.d.ts +20 -1
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-update.d.ts.map +1 -1
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-update.js +46 -6
- package/payload/platform/plugins/tasks/mcp/dist/tools/task-update.js.map +1 -1
- package/payload/server/chunk-5OG7TUQL.js +315 -0
- package/payload/server/chunk-CZGOB575.js +593 -0
- package/payload/server/chunk-NUXYHO6N.js +10079 -0
- package/payload/server/chunk-SALVIGXH.js +1116 -0
- package/payload/server/chunk-ZT6LKDTP.js +2238 -0
- package/payload/server/client-pool-IQU6H43X.js +32 -0
- package/payload/server/cloudflare-task-tracker-Q4X5BYR7.js +17 -0
- package/payload/server/maxy-edge.js +4 -3
- package/payload/server/neo4j-migrations-CYIKMSEO.js +366 -0
- package/payload/server/public/assets/admin-BoGPEBe_.js +352 -0
- package/payload/server/public/assets/{graph-DeH6ulGh.js → graph-LLMJa4Ch.js} +1 -1
- package/payload/server/public/assets/{page-WIAWD2Oi.js → page-DoaF3DB0.js} +1 -1
- package/payload/server/public/graph.html +2 -2
- package/payload/server/public/index.html +2 -2
- package/payload/server/server.js +276 -16
- package/payload/server/public/assets/admin-CdVYoqKD.js +0 -352
package/payload/server/server.js
CHANGED
|
@@ -51,7 +51,7 @@ import {
|
|
|
51
51
|
vncLog,
|
|
52
52
|
waitForExit,
|
|
53
53
|
writeChromiumWrapper
|
|
54
|
-
} from "./chunk-
|
|
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-
|
|
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,16 +124,11 @@ 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-
|
|
131
|
+
} from "./chunk-ZT6LKDTP.js";
|
|
123
132
|
|
|
124
133
|
// ../lib/graph-trash/dist/index.js
|
|
125
134
|
var require_dist = __commonJS({
|
|
@@ -7275,7 +7284,7 @@ var app11 = new Hono();
|
|
|
7275
7284
|
app11.post("/cancel", requireAdminSession, async (c) => {
|
|
7276
7285
|
const session_key = c.var.sessionKey;
|
|
7277
7286
|
try {
|
|
7278
|
-
const { interruptClient: interruptClient2 } = await import("./client-pool-
|
|
7287
|
+
const { interruptClient: interruptClient2 } = await import("./client-pool-IQU6H43X.js");
|
|
7279
7288
|
await interruptClient2(session_key);
|
|
7280
7289
|
return c.json({ ok: true });
|
|
7281
7290
|
} catch (err) {
|
|
@@ -8642,6 +8651,12 @@ function resolvePort() {
|
|
|
8642
8651
|
}
|
|
8643
8652
|
return n;
|
|
8644
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
|
+
}
|
|
8645
8660
|
function validateBody(body) {
|
|
8646
8661
|
if (!body || typeof body !== "object") return { field: "request", message: "Invalid request body" };
|
|
8647
8662
|
if (typeof body.adminLabel !== "string" || !isValidLabel(body.adminLabel)) {
|
|
@@ -8666,6 +8681,20 @@ function validateBody(body) {
|
|
|
8666
8681
|
return { field: "request", message: "Apex must be a valid domain or omitted." };
|
|
8667
8682
|
}
|
|
8668
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
|
+
}
|
|
8669
8698
|
return null;
|
|
8670
8699
|
}
|
|
8671
8700
|
var app21 = new Hono();
|
|
@@ -8778,6 +8807,91 @@ ${result.stderr}` : ""}`;
|
|
|
8778
8807
|
);
|
|
8779
8808
|
return c.json(success, 200);
|
|
8780
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
|
+
});
|
|
8781
8895
|
app21.post("/setup", requireAdminSession, async (c) => {
|
|
8782
8896
|
const started = Date.now();
|
|
8783
8897
|
const sessionKey = c.var.sessionKey;
|
|
@@ -8867,10 +8981,35 @@ app21.post("/setup", requireAdminSession, async (c) => {
|
|
|
8867
8981
|
if (await isActionActive("cloudflare-setup")) {
|
|
8868
8982
|
return err("request", "Another Cloudflare setup is already running. Wait for it to finish before starting a new one.");
|
|
8869
8983
|
}
|
|
8984
|
+
let cloudflareTask;
|
|
8985
|
+
try {
|
|
8986
|
+
cloudflareTask = await openCloudflareTask({
|
|
8987
|
+
accountId,
|
|
8988
|
+
conversationKey: sessionKey,
|
|
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
|
|
8998
|
+
});
|
|
8999
|
+
log(`phase=task-opened taskId=${cloudflareTask.taskId}`);
|
|
9000
|
+
} catch (e) {
|
|
9001
|
+
return err("script", `Failed to open process-provenance Task record: ${e instanceof Error ? e.message : String(e)}`);
|
|
9002
|
+
}
|
|
8870
9003
|
let actionId;
|
|
8871
9004
|
let unit;
|
|
8872
9005
|
try {
|
|
8873
|
-
const launched = await launchAction("cloudflare-setup", {
|
|
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
|
+
});
|
|
8874
9013
|
actionId = launched.actionId;
|
|
8875
9014
|
unit = launched.unit;
|
|
8876
9015
|
} catch (e) {
|
|
@@ -8879,8 +9018,27 @@ app21.post("/setup", requireAdminSession, async (c) => {
|
|
|
8879
9018
|
log(`phase=action-launched id=${actionId} unit=${unit}`);
|
|
8880
9019
|
void (async () => {
|
|
8881
9020
|
const status = await waitForExit(unit, SETUP_TIMEOUT_MS);
|
|
9021
|
+
try {
|
|
9022
|
+
const appendedSteps = await appendCloudflareSteps(cloudflareTask.taskId, accountId, streamLogPath);
|
|
9023
|
+
log(`phase=task-steps-appended count=${appendedSteps.length}`);
|
|
9024
|
+
} catch (e) {
|
|
9025
|
+
logErr(`phase=task-steps-append-failed reason="${e instanceof Error ? e.message : String(e)}"`);
|
|
9026
|
+
}
|
|
8882
9027
|
if (!status || status.execMainStatus !== 0) {
|
|
8883
9028
|
logErr(`phase=post-exit-skipped reason=${status ? `exit=${status.execMainStatus}` : "unit-gone"} id=${actionId}`);
|
|
9029
|
+
try {
|
|
9030
|
+
const diagnostic = status ? `${CLOUDFLARE_TASK_DIAGNOSTICS.scriptExitedNonzero} exit=${status.execMainStatus}` : CLOUDFLARE_TASK_DIAGNOSTICS.endpointDiedPreReconcile;
|
|
9031
|
+
await completeCloudflareTask({
|
|
9032
|
+
taskId: cloudflareTask.taskId,
|
|
9033
|
+
taskElementId: cloudflareTask.taskElementId,
|
|
9034
|
+
accountId,
|
|
9035
|
+
conversationKey: sessionKey,
|
|
9036
|
+
status: "failed",
|
|
9037
|
+
errorMessage: diagnostic
|
|
9038
|
+
});
|
|
9039
|
+
} catch (e) {
|
|
9040
|
+
logErr(`phase=task-close-failed status=failed reason="${e instanceof Error ? e.message : String(e)}"`);
|
|
9041
|
+
}
|
|
8884
9042
|
return;
|
|
8885
9043
|
}
|
|
8886
9044
|
const candidates = [publicFqdn, apex].filter((h) => typeof h === "string" && h.length > 0);
|
|
@@ -8900,6 +9058,30 @@ app21.post("/setup", requireAdminSession, async (c) => {
|
|
|
8900
9058
|
logErr(`phase=alias-domain-write host=${host} result=error reason="${e instanceof Error ? e.message : String(e)}"`);
|
|
8901
9059
|
}
|
|
8902
9060
|
}
|
|
9061
|
+
try {
|
|
9062
|
+
const tunnelState = readTunnelState(brand.configDir);
|
|
9063
|
+
const allHostnames = [adminFqdn, publicFqdn, apex].filter(
|
|
9064
|
+
(h) => typeof h === "string" && h.length > 0
|
|
9065
|
+
);
|
|
9066
|
+
const hostnameRecords = allHostnames.map((value) => ({
|
|
9067
|
+
hostnameValue: value,
|
|
9068
|
+
// Apex heuristic: exactly one dot in the FQDN. Mirrors setup-tunnel.sh:332.
|
|
9069
|
+
isApex: (value.match(/\./g)?.length ?? 0) === 1
|
|
9070
|
+
}));
|
|
9071
|
+
await completeCloudflareTask({
|
|
9072
|
+
taskId: cloudflareTask.taskId,
|
|
9073
|
+
taskElementId: cloudflareTask.taskElementId,
|
|
9074
|
+
accountId,
|
|
9075
|
+
conversationKey: sessionKey,
|
|
9076
|
+
tunnelId: tunnelState?.tunnelId,
|
|
9077
|
+
tunnelName: tunnelState?.tunnelName,
|
|
9078
|
+
hostnames: tunnelState ? hostnameRecords : void 0,
|
|
9079
|
+
status: "completed"
|
|
9080
|
+
});
|
|
9081
|
+
log(`phase=task-closed status=completed tunnelId=${tunnelState?.tunnelId ?? "absent"} hostnames=${allHostnames.length}`);
|
|
9082
|
+
} catch (e) {
|
|
9083
|
+
logErr(`phase=task-close-failed status=completed reason="${e instanceof Error ? e.message : String(e)}"`);
|
|
9084
|
+
}
|
|
8903
9085
|
log(`phase=done action=${actionId}`);
|
|
8904
9086
|
})().catch((e) => logErr(`post-exit handler threw: ${e}`));
|
|
8905
9087
|
const total = Date.now() - started;
|
|
@@ -10300,6 +10482,12 @@ async function handleDefault(c, accountId) {
|
|
|
10300
10482
|
var NEIGHBOURHOOD_SEARCH_DEFAULT_LIMIT = 100;
|
|
10301
10483
|
var NEIGHBOURHOOD_SEARCH_MAX_LIMIT = 2e3;
|
|
10302
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;
|
|
10303
10491
|
async function handleNeighbourhood(c, accountId) {
|
|
10304
10492
|
const elementId = c.req.query("elementId");
|
|
10305
10493
|
if (!elementId) {
|
|
@@ -10337,7 +10525,12 @@ async function handleNeighbourhood(c, accountId) {
|
|
|
10337
10525
|
// `neo4j.int` keeps the LIMIT value as a 64-bit Integer on the wire —
|
|
10338
10526
|
// bare JS `number` works on Pi 4 today but errors on driver versions
|
|
10339
10527
|
// that strictly type-check LIMIT clauses against Long.
|
|
10340
|
-
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)
|
|
10341
10534
|
};
|
|
10342
10535
|
if (allowedIds !== null) cypherParams.allowedIds = allowedIds;
|
|
10343
10536
|
const result = await session.executeRead(async (tx) => {
|
|
@@ -10360,6 +10553,15 @@ async function handleNeighbourhood(c, accountId) {
|
|
|
10360
10553
|
console.error(
|
|
10361
10554
|
`[graph-page] load mode=neighbourhood account=${accountId} agentActions=${includeAgentActions} elementId=${elementId} nodes=${nodes.length} edges=${edges.length} ms=${elapsed}`
|
|
10362
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
|
+
}
|
|
10363
10565
|
if (allowedIds !== null) {
|
|
10364
10566
|
console.error(
|
|
10365
10567
|
`[graph-page] neighbourhood-search-intersect q="${q}" allowed=${allowedIds.length} root=${elementId} rendered=${nodes.length}`
|
|
@@ -10460,15 +10662,50 @@ var NEIGHBOURHOOD_CYPHER = `
|
|
|
10460
10662
|
AND n.accountId = $accountId
|
|
10461
10663
|
AND NOT n:Trashed
|
|
10462
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
|
+
|
|
10463
10690
|
OPTIONAL MATCH (n)-[]-(m)
|
|
10464
10691
|
WHERE m.accountId = $accountId
|
|
10465
10692
|
AND NOT m:Trashed
|
|
10466
10693
|
AND m.deletedAt IS NULL
|
|
10467
10694
|
AND NOT any(lbl IN labels(m) WHERE lbl IN $agentActionLabels)
|
|
10468
|
-
WITH n, m
|
|
10695
|
+
WITH n, conv, clusterMessages, m
|
|
10469
10696
|
LIMIT $neighbourhoodLimit
|
|
10470
|
-
WITH n, collect(DISTINCT m) AS neighbours
|
|
10471
|
-
|
|
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
|
|
10472
10709
|
WITH windowNodes, [x IN windowNodes | elementId(x)] AS windowIds
|
|
10473
10710
|
UNWIND windowNodes AS w
|
|
10474
10711
|
OPTIONAL MATCH (w)-[r]-(o)
|
|
@@ -12356,6 +12593,29 @@ var bootEntitlement = bootAccountConfig ? resolveEntitlement(
|
|
|
12356
12593
|
}
|
|
12357
12594
|
) : null;
|
|
12358
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
|
+
})();
|
|
12359
12619
|
var bootEnabled = Array.isArray(bootAccountConfig?.enabledPlugins) ? bootAccountConfig.enabledPlugins : [];
|
|
12360
12620
|
var bootDelivered = [];
|
|
12361
12621
|
var bootDistMissing = [];
|