@rubytech/create-realagent 1.0.677 → 1.0.680
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/dist/index.js +23 -0
- package/package.json +1 -1
- package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate.test.d.ts +2 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate.test.d.ts.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate.test.js +112 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate.test.js.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cache.test.d.ts +2 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cache.test.d.ts.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cache.test.js +163 -0
- package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cache.test.js.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/cypher-validate.d.ts +38 -0
- package/payload/platform/lib/graph-mcp/dist/cypher-validate.d.ts.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/cypher-validate.js +130 -0
- package/payload/platform/lib/graph-mcp/dist/cypher-validate.js.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/index.js +201 -45
- package/payload/platform/lib/graph-mcp/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-mcp/dist/schema-cache.d.ts +78 -0
- package/payload/platform/lib/graph-mcp/dist/schema-cache.d.ts.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/schema-cache.js +194 -0
- package/payload/platform/lib/graph-mcp/dist/schema-cache.js.map +1 -0
- package/payload/platform/lib/graph-mcp/src/__tests__/cypher-validate.test.ts +141 -0
- package/payload/platform/lib/graph-mcp/src/__tests__/schema-cache.test.ts +169 -0
- package/payload/platform/lib/graph-mcp/src/cypher-validate.ts +157 -0
- package/payload/platform/lib/graph-mcp/src/index.ts +247 -47
- package/payload/platform/lib/graph-mcp/src/schema-cache.ts +212 -0
- package/payload/platform/lib/graph-trash/dist/index.d.ts +8 -0
- package/payload/platform/lib/graph-trash/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/graph-trash/dist/index.js +109 -14
- package/payload/platform/lib/graph-trash/dist/index.js.map +1 -1
- package/payload/platform/lib/graph-trash/src/index.ts +136 -21
- package/payload/platform/plugins/docs/references/internals.md +4 -0
- package/payload/platform/plugins/docs/references/memory-guide.md +5 -1
- package/payload/platform/plugins/docs/references/platform.md +1 -1
- package/payload/platform/plugins/docs/references/troubleshooting.md +18 -0
- package/payload/platform/plugins/memory/PLUGIN.md +1 -0
- package/payload/platform/plugins/memory/mcp/dist/index.js +54 -6
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/lib/filter-token.d.ts +36 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/filter-token.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/filter-token.js +86 -0
- package/payload/platform/plugins/memory/mcp/dist/lib/filter-token.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.d.ts +23 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.js +47 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.d.ts +58 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.js +125 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.js.map +1 -0
- package/payload/platform/templates/agents/admin/IDENTITY.md +16 -0
- package/payload/server/chunk-3RBKKDHC.js +783 -0
- package/payload/server/maxy-edge.js +11 -3
- package/payload/server/public/assets/{admin-DQmUdTBa.js → admin-BBL1no_g.js} +1 -1
- package/payload/server/public/assets/{data-DVlvxbTt.js → data-DUSyrydY.js} +1 -1
- package/payload/server/public/assets/{file-OY_hX2wu.js → file-CDJ6dUV3.js} +1 -1
- package/payload/server/public/assets/graph-CWcYp5bE.js +50 -0
- package/payload/server/public/assets/{house-CgENfOCP.js → house-CNP_bwvT.js} +1 -1
- package/payload/server/public/assets/{jsx-runtime-Bu4vXoe7.css → jsx-runtime-BFFQvkdQ.css} +1 -1
- package/payload/server/public/assets/{public-Clp4VPwo.js → public-sHoAccvb.js} +1 -1
- package/payload/server/public/assets/{share-2-RSIR3MmX.js → share-2-DBcb9j6E.js} +1 -1
- package/payload/server/public/assets/{useVoiceRecorder-B0FI_hts.js → useVoiceRecorder-CtSgpc95.js} +1 -1
- package/payload/server/public/assets/{x-DKZ5NR3n.js → x-CTVJaC_u.js} +1 -1
- package/payload/server/public/data.html +6 -6
- package/payload/server/public/graph.html +6 -6
- package/payload/server/public/index.html +7 -7
- package/payload/server/public/public.html +4 -4
- package/payload/server/server.js +446 -178
- package/payload/server/public/assets/graph-BDaM4Qer.js +0 -49
- /package/payload/server/public/assets/{jsx-runtime-C_VUlXvu.js → jsx-runtime-BVKWELH6.js} +0 -0
package/payload/server/server.js
CHANGED
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
clearRateLimit,
|
|
14
14
|
createRemoteSession,
|
|
15
15
|
hashPassword,
|
|
16
|
-
invalidateRemoteSession,
|
|
17
16
|
isPasswordValid,
|
|
18
17
|
isRemoteAuthConfigured,
|
|
19
18
|
recordFailedAttempt,
|
|
@@ -25,7 +24,7 @@ import {
|
|
|
25
24
|
verifyPassword,
|
|
26
25
|
verifyRemotePassword,
|
|
27
26
|
vncLog
|
|
28
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-3RBKKDHC.js";
|
|
29
28
|
|
|
30
29
|
// ../lib/models/dist/index.js
|
|
31
30
|
var require_dist = __commonJS({
|
|
@@ -2895,7 +2894,7 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
2895
2894
|
};
|
|
2896
2895
|
|
|
2897
2896
|
// server/index.ts
|
|
2898
|
-
import { readFileSync as
|
|
2897
|
+
import { readFileSync as readFileSync25, existsSync as existsSync23, watchFile } from "fs";
|
|
2899
2898
|
import { resolve as resolve27, join as join12, basename as basename7 } from "path";
|
|
2900
2899
|
import { homedir as homedir4 } from "os";
|
|
2901
2900
|
|
|
@@ -2908,7 +2907,7 @@ import { spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
|
|
|
2908
2907
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
2909
2908
|
import { resolve as resolve5, join as join3 } from "path";
|
|
2910
2909
|
import { platform as osPlatform } from "os";
|
|
2911
|
-
import { readFileSync as
|
|
2910
|
+
import { readFileSync as readFileSync7, 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";
|
|
2912
2911
|
import { lookup as dnsLookup } from "dns/promises";
|
|
2913
2912
|
import { createConnection as netConnect } from "net";
|
|
2914
2913
|
import { StringDecoder } from "string_decoder";
|
|
@@ -5803,6 +5802,101 @@ async function criticAndRecord(opts) {
|
|
|
5803
5802
|
}
|
|
5804
5803
|
}
|
|
5805
5804
|
|
|
5805
|
+
// app/lib/admin-schema-block.ts
|
|
5806
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
5807
|
+
var EDGE_TAXONOMY = [
|
|
5808
|
+
{ type: "PART_OF", direction: "(Message)-[:PART_OF]->(Conversation)", note: "Messages belong to exactly one Conversation. Do NOT use :HAS_MESSAGE \u2014 that edge does not exist." },
|
|
5809
|
+
{ type: "NEXT", direction: "(Message)-[:NEXT]->(Message)", note: "Chain order within a Conversation. Each Message has at most one NEXT successor." },
|
|
5810
|
+
{ type: "BELONGS_TO", direction: "(Conversation|Task|...)-[:BELONGS_TO]->(LocalBusiness)", note: "Account scope. Universal \u2014 almost every tenant-owned node has one." },
|
|
5811
|
+
{ type: "ADMIN_OF", direction: "(AdminUser)-[:ADMIN_OF]->(LocalBusiness)", note: "Device-level admin membership (see admin-add / admin-remove)." },
|
|
5812
|
+
{ type: "AUTHORED_BY", direction: "(Message)-[:AUTHORED_BY]->(Person)", note: "Message authorship (when the author is an identified Person)." },
|
|
5813
|
+
{ type: "HAS_TOOL_CALL", direction: "(Message)-[:HAS_TOOL_CALL]->(ToolCall)", note: "Tool calls attached to an assistant message." },
|
|
5814
|
+
{ type: "HAS_RESULT", direction: "(WorkflowStep|ToolCall)-[:HAS_RESULT]->(StepResult)", note: "Workflow step / tool-call outputs." },
|
|
5815
|
+
{ type: "HAS_STEP", direction: "(Workflow)-[:HAS_STEP]->(WorkflowStep)", note: "Workflow composition." },
|
|
5816
|
+
{ type: "RUN_OF", direction: "(WorkflowRun)-[:RUN_OF]->(Workflow)", note: "Workflow execution instance." },
|
|
5817
|
+
{ type: "HAS_SECTION", direction: "(KnowledgeDocument)-[:HAS_SECTION]->(Section)", note: "Document hierarchy." },
|
|
5818
|
+
{ type: "HAS_CHUNK", direction: "(Section)-[:HAS_CHUNK]->(Chunk)", note: "Section-to-embeddable-chunk." },
|
|
5819
|
+
{ type: "HAS_PREFERENCE", direction: "(Person)-[:HAS_PREFERENCE]->(Preference)", note: "Person-scoped preferences (profile memory)." },
|
|
5820
|
+
{ type: "HAS_ACCESS", direction: "(Person)-[:HAS_ACCESS]->(AccessGrant)", note: "Public agent access grants." },
|
|
5821
|
+
{ type: "HAS_TASK", direction: "(Person|LocalBusiness)-[:HAS_TASK]->(Task)", note: "Task ownership / assignment." },
|
|
5822
|
+
{ type: "HAS_PRICING", direction: "(Service)-[:HAS_PRICING]->(PriceSpecification)", note: "Service pricing." },
|
|
5823
|
+
{ type: "HAS_HOURS", direction: "(LocalBusiness)-[:HAS_HOURS]->(OpeningHoursSpecification)", note: "Opening hours." },
|
|
5824
|
+
{ type: "HAS_FAQ", direction: "(LocalBusiness)-[:HAS_FAQ]->(Question)", note: "FAQ attached to business." },
|
|
5825
|
+
{ type: "HAS_BRAND_ASSET", direction: "(LocalBusiness)-[:HAS_BRAND_ASSET]->(DigitalDocument|ImageObject)", note: "Branding assets." },
|
|
5826
|
+
{ type: "OFFERS", direction: "(LocalBusiness)-[:OFFERS]->(Service)", note: "Service catalogue." },
|
|
5827
|
+
{ type: "CONTAINS", direction: "(KnowledgeDocument)-[:CONTAINS]->(Chunk)", note: "Flat document-to-chunk (alternative to HAS_SECTION then HAS_CHUNK)." },
|
|
5828
|
+
{ type: "REFERENCES", direction: "(Message|KnowledgeDocument)-[:REFERENCES]->(*)", note: "Soft reference link." },
|
|
5829
|
+
{ type: "ABOUT", direction: "(Review|Message)-[:ABOUT]->(*)", note: "Subject pointer." },
|
|
5830
|
+
{ type: "REPLY_TO", direction: "(Email)-[:REPLY_TO]->(Email)", note: "Email threading." },
|
|
5831
|
+
{ type: "RECEIVED_BY", direction: "(Email)-[:RECEIVED_BY]->(Person)", note: "Email recipient." },
|
|
5832
|
+
{ type: "RAISED_BY", direction: "(ReviewAlert)-[:RAISED_BY]->(*)", note: "Alert provenance." },
|
|
5833
|
+
{ type: "AFFECTS", direction: "(ReviewAlert)-[:AFFECTS]->(*)", note: "Alert impact surface." },
|
|
5834
|
+
{ type: "BLOCKS", direction: "(Task)-[:BLOCKS]->(Task)", note: "Task dependency." },
|
|
5835
|
+
{ type: "OBSERVED_IN", direction: "(*)-[:OBSERVED_IN]->(Conversation)", note: "Observation provenance." }
|
|
5836
|
+
];
|
|
5837
|
+
var SUBLABELS = [
|
|
5838
|
+
{ base: "Conversation", sub: "AdminConversation", note: "Admin-only conversation (admin chat, not public)." },
|
|
5839
|
+
{ base: "Conversation", sub: "PublicConversation", note: "Public agent conversation (visitor-facing)." },
|
|
5840
|
+
{ base: "Message", sub: "UserMessage", note: "Message authored by the human user." },
|
|
5841
|
+
{ base: "Message", sub: "AssistantMessage", note: "Message authored by the assistant." }
|
|
5842
|
+
];
|
|
5843
|
+
var LABEL_PATTERN = /FOR\s*\(\s*\w+\s*:\s*(\w+)\s*\)/g;
|
|
5844
|
+
function parseLabelsFromSchemaCypher(schemaCypher) {
|
|
5845
|
+
const found = /* @__PURE__ */ new Set();
|
|
5846
|
+
for (const match2 of schemaCypher.matchAll(LABEL_PATTERN)) {
|
|
5847
|
+
found.add(match2[1]);
|
|
5848
|
+
}
|
|
5849
|
+
return [...found].sort();
|
|
5850
|
+
}
|
|
5851
|
+
function buildSchemaBlock(schemaCypherText) {
|
|
5852
|
+
const baseLabels = parseLabelsFromSchemaCypher(schemaCypherText);
|
|
5853
|
+
const sublabels = SUBLABELS.map((s) => s.sub);
|
|
5854
|
+
const lines = [];
|
|
5855
|
+
lines.push("# SCHEMA (Neo4j graph, canonical reference)");
|
|
5856
|
+
lines.push("");
|
|
5857
|
+
lines.push("The Neo4j instance backing this admin session contains exactly the labels and relationship types below. Never invent names outside this list. If you need cypher against a token not listed here, first invoke `maxy-graph-get_neo4j_schema` to confirm it exists \u2014 do not guess.");
|
|
5858
|
+
lines.push("");
|
|
5859
|
+
lines.push("## Labels (from platform/neo4j/schema.cypher)");
|
|
5860
|
+
lines.push("");
|
|
5861
|
+
for (const label of baseLabels) {
|
|
5862
|
+
lines.push(`- :${label}`);
|
|
5863
|
+
}
|
|
5864
|
+
lines.push("");
|
|
5865
|
+
lines.push("### Sublabels (Task 633 role-cue variants)");
|
|
5866
|
+
lines.push("");
|
|
5867
|
+
for (const s of SUBLABELS) {
|
|
5868
|
+
lines.push(`- :${s.sub} (on :${s.base}) \u2014 ${s.note}`);
|
|
5869
|
+
}
|
|
5870
|
+
lines.push("");
|
|
5871
|
+
lines.push("## Relationship types (from .docs/neo4j.md)");
|
|
5872
|
+
lines.push("");
|
|
5873
|
+
for (const e of EDGE_TAXONOMY) {
|
|
5874
|
+
lines.push(`- :${e.type} \u2014 ${e.direction} \u2014 ${e.note}`);
|
|
5875
|
+
}
|
|
5876
|
+
lines.push("");
|
|
5877
|
+
lines.push("The cypher MCP validator (graph-mcp proxy, Task 654) rejects write cypher that references unknown labels or edges and warns on read cypher. Stick to the taxonomy above; if you believe a token is missing, invoke `maxy-graph-get_neo4j_schema` for the live snapshot.");
|
|
5878
|
+
const text = lines.join("\n");
|
|
5879
|
+
return {
|
|
5880
|
+
text,
|
|
5881
|
+
labelCount: baseLabels.length + sublabels.length,
|
|
5882
|
+
relationshipTypeCount: EDGE_TAXONOMY.length,
|
|
5883
|
+
bytes: Buffer.byteLength(text, "utf-8")
|
|
5884
|
+
};
|
|
5885
|
+
}
|
|
5886
|
+
function loadAdminSchemaBlock(schemaCypherPath) {
|
|
5887
|
+
let text;
|
|
5888
|
+
try {
|
|
5889
|
+
text = readFileSync6(schemaCypherPath, "utf-8");
|
|
5890
|
+
} catch (err) {
|
|
5891
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5892
|
+
console.error(
|
|
5893
|
+
`[admin-identity] schema.cypher unreadable path=${schemaCypherPath} error="${msg.replace(/"/g, "'")}" block=omitted`
|
|
5894
|
+
);
|
|
5895
|
+
return null;
|
|
5896
|
+
}
|
|
5897
|
+
return buildSchemaBlock(text);
|
|
5898
|
+
}
|
|
5899
|
+
|
|
5806
5900
|
// app/lib/claude-agent.ts
|
|
5807
5901
|
var LOG_RETENTION_DAYS = 7;
|
|
5808
5902
|
var BROWSER_TOOL_PREFIXES = [
|
|
@@ -6063,7 +6157,7 @@ function sampleProcState(pid) {
|
|
|
6063
6157
|
let sockets2 = 0;
|
|
6064
6158
|
for (const tcpFile of ["/proc/" + pid + "/net/tcp", "/proc/" + pid + "/net/tcp6"]) {
|
|
6065
6159
|
try {
|
|
6066
|
-
const raw2 =
|
|
6160
|
+
const raw2 = readFileSync7(tcpFile, "utf-8");
|
|
6067
6161
|
const lines2 = raw2.split("\n");
|
|
6068
6162
|
for (let i = 1; i < lines2.length; i++) {
|
|
6069
6163
|
const line = lines2[i].trim();
|
|
@@ -6079,7 +6173,7 @@ function sampleProcState(pid) {
|
|
|
6079
6173
|
}
|
|
6080
6174
|
let rssMb = 0;
|
|
6081
6175
|
try {
|
|
6082
|
-
const statm =
|
|
6176
|
+
const statm = readFileSync7(`/proc/${pid}/statm`, "utf-8").trim().split(/\s+/);
|
|
6083
6177
|
const rssPages = parseInt(statm[1] ?? "0", 10);
|
|
6084
6178
|
if (Number.isFinite(rssPages)) rssMb = Math.round(rssPages * 4096 / (1024 * 1024));
|
|
6085
6179
|
} catch {
|
|
@@ -6126,7 +6220,7 @@ function resolveAccount() {
|
|
|
6126
6220
|
let usersJsonUserId = null;
|
|
6127
6221
|
if (existsSync5(usersFilePath)) {
|
|
6128
6222
|
try {
|
|
6129
|
-
const raw2 =
|
|
6223
|
+
const raw2 = readFileSync7(usersFilePath, "utf-8").trim();
|
|
6130
6224
|
if (raw2) {
|
|
6131
6225
|
const users = JSON.parse(raw2);
|
|
6132
6226
|
if (users.length > 0) {
|
|
@@ -6142,7 +6236,7 @@ function resolveAccount() {
|
|
|
6142
6236
|
if (!entry.isDirectory()) continue;
|
|
6143
6237
|
const configPath2 = resolve5(ACCOUNTS_DIR, entry.name, "account.json");
|
|
6144
6238
|
if (!existsSync5(configPath2)) continue;
|
|
6145
|
-
const raw2 =
|
|
6239
|
+
const raw2 = readFileSync7(configPath2, "utf-8");
|
|
6146
6240
|
let config;
|
|
6147
6241
|
try {
|
|
6148
6242
|
config = JSON.parse(raw2);
|
|
@@ -6177,7 +6271,7 @@ function resolveAccount() {
|
|
|
6177
6271
|
function readAgentFile(accountDir, agentName, filename) {
|
|
6178
6272
|
const filePath = resolve5(accountDir, "agents", agentName, filename);
|
|
6179
6273
|
if (!existsSync5(filePath)) return null;
|
|
6180
|
-
return
|
|
6274
|
+
return readFileSync7(filePath, "utf-8");
|
|
6181
6275
|
}
|
|
6182
6276
|
function readIdentity(accountDir, agentName) {
|
|
6183
6277
|
return readAgentFile(accountDir, agentName, "IDENTITY.md");
|
|
@@ -6219,7 +6313,7 @@ function resolveDefaultAgentSlug(accountDir) {
|
|
|
6219
6313
|
}
|
|
6220
6314
|
let config;
|
|
6221
6315
|
try {
|
|
6222
|
-
config = JSON.parse(
|
|
6316
|
+
config = JSON.parse(readFileSync7(configPath2, "utf-8"));
|
|
6223
6317
|
} catch (err) {
|
|
6224
6318
|
console.error("[agent-resolve] failed to read account.json:", err);
|
|
6225
6319
|
return null;
|
|
@@ -6310,14 +6404,14 @@ function resolveAgentConfig(accountDir, agentName) {
|
|
|
6310
6404
|
const knowledgeMtime = statSync3(knowledgePath).mtimeMs;
|
|
6311
6405
|
const summaryMtime = statSync3(summaryPath).mtimeMs;
|
|
6312
6406
|
if (summaryMtime >= knowledgeMtime) {
|
|
6313
|
-
knowledge =
|
|
6407
|
+
knowledge = readFileSync7(summaryPath, "utf-8");
|
|
6314
6408
|
} else {
|
|
6315
6409
|
console.warn(`[agent-config] ${agentName}: KNOWLEDGE-SUMMARY.md is stale (KNOWLEDGE.md is newer) \u2014 using full knowledge`);
|
|
6316
|
-
knowledge =
|
|
6410
|
+
knowledge = readFileSync7(knowledgePath, "utf-8");
|
|
6317
6411
|
}
|
|
6318
6412
|
knowledgeBaked = true;
|
|
6319
6413
|
} else if (hasKnowledge) {
|
|
6320
|
-
knowledge =
|
|
6414
|
+
knowledge = readFileSync7(knowledgePath, "utf-8");
|
|
6321
6415
|
knowledgeBaked = true;
|
|
6322
6416
|
}
|
|
6323
6417
|
let budget = null;
|
|
@@ -6343,7 +6437,7 @@ function parsePluginFrontmatter(pluginDir) {
|
|
|
6343
6437
|
if (!existsSync5(pluginPath)) return null;
|
|
6344
6438
|
let raw2;
|
|
6345
6439
|
try {
|
|
6346
|
-
raw2 =
|
|
6440
|
+
raw2 = readFileSync7(pluginPath, "utf-8");
|
|
6347
6441
|
} catch {
|
|
6348
6442
|
console.warn(`[plugins] cannot read ${pluginPath}`);
|
|
6349
6443
|
return null;
|
|
@@ -6419,7 +6513,7 @@ function autoDeliverPremiumPlugins(purchasedPlugins) {
|
|
|
6419
6513
|
if (isBundle) {
|
|
6420
6514
|
let bundleRaw;
|
|
6421
6515
|
try {
|
|
6422
|
-
bundleRaw =
|
|
6516
|
+
bundleRaw = readFileSync7(bundlePath, "utf-8");
|
|
6423
6517
|
} catch (err) {
|
|
6424
6518
|
console.log(`${TAG19} ${pluginName}: cannot read BUNDLE.md \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
6425
6519
|
continue;
|
|
@@ -6512,7 +6606,7 @@ function migratePluginRenames(accountDir, config) {
|
|
|
6512
6606
|
if (!changed) return;
|
|
6513
6607
|
const configPath2 = resolve5(accountDir, "account.json");
|
|
6514
6608
|
try {
|
|
6515
|
-
const raw2 =
|
|
6609
|
+
const raw2 = readFileSync7(configPath2, "utf-8");
|
|
6516
6610
|
const parsed = JSON.parse(raw2);
|
|
6517
6611
|
parsed.enabledPlugins = migrated;
|
|
6518
6612
|
writeFileSync5(configPath2, JSON.stringify(parsed, null, 2) + "\n");
|
|
@@ -6546,7 +6640,7 @@ function autoDeliverBundleAgents(accountDir, purchasedPlugins) {
|
|
|
6546
6640
|
const agentsmdPath = resolve5(accountDir, "agents", "admin", "AGENTS.md");
|
|
6547
6641
|
let agentsmd = "";
|
|
6548
6642
|
try {
|
|
6549
|
-
agentsmd = existsSync5(agentsmdPath) ?
|
|
6643
|
+
agentsmd = existsSync5(agentsmdPath) ? readFileSync7(agentsmdPath, "utf-8") : "";
|
|
6550
6644
|
} catch {
|
|
6551
6645
|
}
|
|
6552
6646
|
let delivered = 0;
|
|
@@ -6570,7 +6664,7 @@ function autoDeliverBundleAgents(accountDir, purchasedPlugins) {
|
|
|
6570
6664
|
continue;
|
|
6571
6665
|
}
|
|
6572
6666
|
try {
|
|
6573
|
-
const content =
|
|
6667
|
+
const content = readFileSync7(target, "utf-8");
|
|
6574
6668
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
6575
6669
|
if (fmMatch) {
|
|
6576
6670
|
const nameMatch = fmMatch[1].match(/^name:\s*(.+)/m);
|
|
@@ -6606,7 +6700,7 @@ function assemblePublicPluginContent(pluginDir) {
|
|
|
6606
6700
|
const pluginPath = resolve5(pluginRoot, "PLUGIN.md");
|
|
6607
6701
|
let raw2;
|
|
6608
6702
|
try {
|
|
6609
|
-
raw2 =
|
|
6703
|
+
raw2 = readFileSync7(pluginPath, "utf-8");
|
|
6610
6704
|
} catch {
|
|
6611
6705
|
return null;
|
|
6612
6706
|
}
|
|
@@ -6627,7 +6721,7 @@ function assemblePublicPluginContent(pluginDir) {
|
|
|
6627
6721
|
const skillMdPath = resolve5(skillDir, "SKILL.md");
|
|
6628
6722
|
let skillRaw;
|
|
6629
6723
|
try {
|
|
6630
|
-
skillRaw =
|
|
6724
|
+
skillRaw = readFileSync7(skillMdPath, "utf-8");
|
|
6631
6725
|
} catch {
|
|
6632
6726
|
continue;
|
|
6633
6727
|
}
|
|
@@ -6678,7 +6772,7 @@ function assemblePublicPluginContent(pluginDir) {
|
|
|
6678
6772
|
}
|
|
6679
6773
|
for (const refFile of refFiles) {
|
|
6680
6774
|
try {
|
|
6681
|
-
const refContent =
|
|
6775
|
+
const refContent = readFileSync7(resolve5(refsDir, refFile), "utf-8").trim();
|
|
6682
6776
|
if (refContent) {
|
|
6683
6777
|
parts.push(`
|
|
6684
6778
|
<!-- reference: ${refFile} -->`);
|
|
@@ -6752,7 +6846,7 @@ function loadEmbeddedPlugins(agentType, selectedPlugins, enabledPlugins) {
|
|
|
6752
6846
|
const pluginPath = resolve5(pluginsDir, dir, "PLUGIN.md");
|
|
6753
6847
|
let raw2;
|
|
6754
6848
|
try {
|
|
6755
|
-
raw2 =
|
|
6849
|
+
raw2 = readFileSync7(pluginPath, "utf-8");
|
|
6756
6850
|
} catch (err) {
|
|
6757
6851
|
console.warn(`[plugins] ${dir}: failed to read PLUGIN.md for ${agentType} embed: ${String(err)}`);
|
|
6758
6852
|
continue;
|
|
@@ -7098,7 +7192,7 @@ function resolveUserAccounts(userId) {
|
|
|
7098
7192
|
if (!existsSync5(configPath2)) continue;
|
|
7099
7193
|
let config;
|
|
7100
7194
|
try {
|
|
7101
|
-
config = JSON.parse(
|
|
7195
|
+
config = JSON.parse(readFileSync7(configPath2, "utf-8"));
|
|
7102
7196
|
} catch {
|
|
7103
7197
|
console.error(`[session] account.json corrupt at ${configPath2} \u2014 skipping`);
|
|
7104
7198
|
continue;
|
|
@@ -7168,8 +7262,75 @@ function clearSessionHistory(sessionKey) {
|
|
|
7168
7262
|
session.stalledSubagents = void 0;
|
|
7169
7263
|
session.pendingTrimmedMessages = void 0;
|
|
7170
7264
|
session.pendingCommitmentOffers = void 0;
|
|
7265
|
+
session.pendingTurns = void 0;
|
|
7171
7266
|
return previousConversationId;
|
|
7172
7267
|
}
|
|
7268
|
+
function bufferPendingTurn(sessionKey, turn) {
|
|
7269
|
+
const session = sessionStore.get(sessionKey);
|
|
7270
|
+
if (!session) {
|
|
7271
|
+
console.error(`[conversation-gate] bufferPendingTurn: session not found sessionKey=${sessionKey.slice(0, 8)}\u2026`);
|
|
7272
|
+
return;
|
|
7273
|
+
}
|
|
7274
|
+
if (!session.pendingTurns) session.pendingTurns = [];
|
|
7275
|
+
session.pendingTurns.push(turn);
|
|
7276
|
+
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}`);
|
|
7277
|
+
}
|
|
7278
|
+
function getPendingTurnCount(sessionKey) {
|
|
7279
|
+
const buf = sessionStore.get(sessionKey)?.pendingTurns;
|
|
7280
|
+
if (!buf) return 0;
|
|
7281
|
+
let n = 0;
|
|
7282
|
+
for (const t of buf) if (t.role === "user") n++;
|
|
7283
|
+
return n;
|
|
7284
|
+
}
|
|
7285
|
+
function drainPendingTurns(sessionKey) {
|
|
7286
|
+
const session = sessionStore.get(sessionKey);
|
|
7287
|
+
if (!session?.pendingTurns || session.pendingTurns.length === 0) return void 0;
|
|
7288
|
+
const drained = session.pendingTurns;
|
|
7289
|
+
session.pendingTurns = void 0;
|
|
7290
|
+
return drained;
|
|
7291
|
+
}
|
|
7292
|
+
async function maybeFlushConversationBuffer(sessionKey, agentType, accountId) {
|
|
7293
|
+
const session = sessionStore.get(sessionKey);
|
|
7294
|
+
if (!session) return null;
|
|
7295
|
+
if (session.conversationId) return session.conversationId;
|
|
7296
|
+
if (getPendingTurnCount(sessionKey) < 2) return null;
|
|
7297
|
+
if (session.flushInFlight) return session.flushInFlight;
|
|
7298
|
+
const attempt = (async () => {
|
|
7299
|
+
let conversationId = null;
|
|
7300
|
+
if (agentType === "admin") {
|
|
7301
|
+
const userId = session.userId;
|
|
7302
|
+
if (!userId) {
|
|
7303
|
+
console.error(`[conversation-gate] flush aborted: admin session missing userId sessionKey=${sessionKey.slice(0, 8)}\u2026`);
|
|
7304
|
+
return null;
|
|
7305
|
+
}
|
|
7306
|
+
conversationId = await createNewAdminConversation(userId, accountId, sessionKey);
|
|
7307
|
+
} else {
|
|
7308
|
+
conversationId = await ensureConversation(accountId, "public", sessionKey, void 0, void 0, void 0);
|
|
7309
|
+
}
|
|
7310
|
+
if (!conversationId) return null;
|
|
7311
|
+
session.conversationId = conversationId;
|
|
7312
|
+
const buffered = drainPendingTurns(sessionKey) ?? [];
|
|
7313
|
+
for (const turn of buffered) {
|
|
7314
|
+
persistMessage(conversationId, turn.role, turn.content, accountId, turn.tokens, turn.timestamp, turn.sender).catch((err) => {
|
|
7315
|
+
console.error(`[conversation-gate] replay persistMessage failed role=${turn.role}: ${err instanceof Error ? err.message : String(err)}`);
|
|
7316
|
+
});
|
|
7317
|
+
}
|
|
7318
|
+
console.log(`[conversation-gate] ${(/* @__PURE__ */ new Date()).toISOString()} flushed sessionKey=${sessionKey.slice(0, 8)} conversationId=${conversationId.slice(0, 8)} bufferedMessages=${buffered.length} agentType=${agentType}`);
|
|
7319
|
+
return conversationId;
|
|
7320
|
+
})();
|
|
7321
|
+
session.flushInFlight = attempt;
|
|
7322
|
+
try {
|
|
7323
|
+
return await attempt;
|
|
7324
|
+
} finally {
|
|
7325
|
+
if (session.flushInFlight === attempt) session.flushInFlight = void 0;
|
|
7326
|
+
}
|
|
7327
|
+
}
|
|
7328
|
+
function isDmChannelSessionKey(sessionKey) {
|
|
7329
|
+
return sessionKey.startsWith("whatsapp:") || sessionKey.startsWith("telegram:");
|
|
7330
|
+
}
|
|
7331
|
+
function preflushStreamLogKey(sessionKey) {
|
|
7332
|
+
return `preflush-${sessionKey.slice(0, 12)}`;
|
|
7333
|
+
}
|
|
7173
7334
|
function getAgentNameForSession(sessionKey) {
|
|
7174
7335
|
return sessionStore.get(sessionKey)?.agentName;
|
|
7175
7336
|
}
|
|
@@ -7347,7 +7508,7 @@ function readBrandHostname() {
|
|
|
7347
7508
|
if (cachedBrandHostname !== null) return cachedBrandHostname;
|
|
7348
7509
|
try {
|
|
7349
7510
|
const brandPath = resolve5(PLATFORM_ROOT3, "config", "brand.json");
|
|
7350
|
-
const parsed = JSON.parse(
|
|
7511
|
+
const parsed = JSON.parse(readFileSync7(brandPath, "utf-8"));
|
|
7351
7512
|
cachedBrandHostname = typeof parsed.hostname === "string" && parsed.hostname.length > 0 ? parsed.hostname : "maxy";
|
|
7352
7513
|
} catch {
|
|
7353
7514
|
cachedBrandHostname = "maxy";
|
|
@@ -9062,20 +9223,24 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
9062
9223
|
const userTimestamp = clientTimestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
9063
9224
|
const resumeSessionId = sessionKey ? getAgentSessionId(sessionKey) : void 0;
|
|
9064
9225
|
const spawnConvId = sessionKey ? getConversationIdForSession(sessionKey) : void 0;
|
|
9065
|
-
if (!spawnConvId) {
|
|
9066
|
-
throw new Error(`invokeAdminAgent: conversationId missing for sessionKey=${sessionKey
|
|
9226
|
+
if (!spawnConvId && sessionKey && getPendingTurnCount(sessionKey) >= 2) {
|
|
9227
|
+
throw new Error(`invokeAdminAgent: conversationId missing post-flush for sessionKey=${sessionKey.slice(0, 8)} \u2014 maybeFlushConversationBuffer must bind it before invoking the agent`);
|
|
9228
|
+
}
|
|
9229
|
+
const spawnLogKey = spawnConvId ?? (sessionKey ? preflushStreamLogKey(sessionKey) : void 0);
|
|
9230
|
+
if (!spawnLogKey) {
|
|
9231
|
+
throw new Error(`invokeAdminAgent: sessionKey required \u2014 cannot resolve log stream without one`);
|
|
9067
9232
|
}
|
|
9068
9233
|
const cdpOk = await ensureCdp();
|
|
9069
9234
|
if (!cdpOk) {
|
|
9070
|
-
const cdpLog = agentLogStream("claude-agent-stream", accountDir,
|
|
9235
|
+
const cdpLog = agentLogStream("claude-agent-stream", accountDir, spawnLogKey);
|
|
9071
9236
|
cdpLog.write(`[${isoTs()}] [warn] ensureCdp failed \u2014 browser-specialist degraded
|
|
9072
9237
|
`);
|
|
9073
9238
|
cdpLog.end();
|
|
9074
9239
|
}
|
|
9075
9240
|
const ccUserId = sessionKey ? getUserIdForSession(sessionKey) : void 0;
|
|
9076
|
-
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId,
|
|
9241
|
+
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, spawnLogKey, ccUserId, enabledPlugins) });
|
|
9077
9242
|
const specialistsDir = resolve5(accountDir, "specialists");
|
|
9078
|
-
if (!existsSync5(specialistsDir)) agentLogStream("claude-agent-stream", accountDir,
|
|
9243
|
+
if (!existsSync5(specialistsDir)) agentLogStream("claude-agent-stream", accountDir, spawnLogKey).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
9079
9244
|
`);
|
|
9080
9245
|
const args = [
|
|
9081
9246
|
"--print",
|
|
@@ -9106,19 +9271,19 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
9106
9271
|
cwd: accountDir,
|
|
9107
9272
|
stdio: ["ignore", "pipe", "pipe"],
|
|
9108
9273
|
// Task 556: STREAM_LOG_PATH inherited by Bash-tool subprocesses.
|
|
9109
|
-
env: buildSpawnEnv(accountId, accountDir,
|
|
9274
|
+
env: buildSpawnEnv(accountId, accountDir, spawnLogKey)
|
|
9110
9275
|
});
|
|
9111
|
-
const stderrLog = agentLogStream("claude-agent-stderr", accountDir,
|
|
9276
|
+
const stderrLog = agentLogStream("claude-agent-stderr", accountDir, spawnLogKey);
|
|
9112
9277
|
stderrLog.on("error", () => {
|
|
9113
9278
|
});
|
|
9114
9279
|
proc.stderr?.pipe(stderrLog);
|
|
9115
|
-
const streamLog = agentLogStream("claude-agent-stream", accountDir,
|
|
9280
|
+
const streamLog = agentLogStream("claude-agent-stream", accountDir, spawnLogKey);
|
|
9116
9281
|
streamLog.on("error", () => {
|
|
9117
9282
|
});
|
|
9118
9283
|
teeProcStderrToStreamLog(proc, streamLog);
|
|
9119
9284
|
streamLog.write(`[${isoTs()}] [subproc-debug-unavailable] reason=bundled-bun-binary-ignores-node-debug pid=${proc.pid} cli=claude
|
|
9120
9285
|
`);
|
|
9121
|
-
streamLog.write(`[${isoTs()}] [spawn-env] STREAM_LOG_PATH=set pid=${proc.pid} conversationId=${spawnConvId} site=admin
|
|
9286
|
+
streamLog.write(`[${isoTs()}] [spawn-env] STREAM_LOG_PATH=set pid=${proc.pid} conversationId=${spawnConvId ?? "preflush"} logKey=${spawnLogKey} site=admin
|
|
9122
9287
|
`);
|
|
9123
9288
|
if (sessionKey) {
|
|
9124
9289
|
const prev = activeProcesses.get(sessionKey);
|
|
@@ -9128,7 +9293,7 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
9128
9293
|
}
|
|
9129
9294
|
activeProcesses.set(sessionKey, { pid: proc.pid, spawnedAt: Date.now() });
|
|
9130
9295
|
}
|
|
9131
|
-
streamLog.write(`[${isoTs()}] [spawn] pid=${proc.pid} resume=${resumeSessionId ?? "none"} sessionKey=${sessionKey ?? "none"} conversationId=${spawnConvId} pluginDir=${specialistsDir}
|
|
9296
|
+
streamLog.write(`[${isoTs()}] [spawn] pid=${proc.pid} resume=${resumeSessionId ?? "none"} sessionKey=${sessionKey ?? "none"} conversationId=${spawnConvId ?? "preflush"} logKey=${spawnLogKey} pluginDir=${specialistsDir}
|
|
9132
9297
|
`);
|
|
9133
9298
|
streamLog.write(`[${isoTs()}] [stdin] len=${fullMessage.length} preview=${JSON.stringify(fullMessage.slice(0, 80))}
|
|
9134
9299
|
`);
|
|
@@ -9200,7 +9365,7 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
9200
9365
|
}
|
|
9201
9366
|
if (event.type === "usage" && sessionKey && currentAgentSessionId) {
|
|
9202
9367
|
const peakReqPct = event.peak_request_pct ?? 0;
|
|
9203
|
-
if (peakReqPct >= COMPACTION_THRESHOLD) {
|
|
9368
|
+
if (peakReqPct >= COMPACTION_THRESHOLD && spawnConvId) {
|
|
9204
9369
|
const compactionIter = runCompactionTurn(accountDir, accountId, systemPrompt, currentAgentSessionId, adminModel, spawnConvId, enabledPlugins);
|
|
9205
9370
|
let step = await compactionIter.next();
|
|
9206
9371
|
while (!step.done) {
|
|
@@ -9310,9 +9475,9 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
9310
9475
|
} else {
|
|
9311
9476
|
gotDone = true;
|
|
9312
9477
|
if (!sessionWasReset) {
|
|
9478
|
+
const assistantTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
9313
9479
|
const convId = sessionKey ? sessionStore.get(sessionKey)?.conversationId : void 0;
|
|
9314
9480
|
if (convId) {
|
|
9315
|
-
const assistantTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
9316
9481
|
persistMessage(convId, "user", fullMessage, accountId, void 0, userTimestamp).catch(() => {
|
|
9317
9482
|
});
|
|
9318
9483
|
autoLabelSession(convId, fullMessage).catch(() => {
|
|
@@ -9320,7 +9485,14 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
9320
9485
|
if (responseText) persistMessage(convId, "assistant", responseText, accountId, capturedTokens, assistantTimestamp).catch(() => {
|
|
9321
9486
|
});
|
|
9322
9487
|
} else if (sessionKey) {
|
|
9323
|
-
|
|
9488
|
+
bufferPendingTurn(sessionKey, { role: "user", content: fullMessage, timestamp: userTimestamp });
|
|
9489
|
+
if (responseText) bufferPendingTurn(sessionKey, { role: "assistant", content: responseText, timestamp: assistantTimestamp, tokens: capturedTokens });
|
|
9490
|
+
const flushedId = await maybeFlushConversationBuffer(sessionKey, "admin", accountId);
|
|
9491
|
+
if (flushedId) {
|
|
9492
|
+
autoLabelSession(flushedId, fullMessage).catch(() => {
|
|
9493
|
+
});
|
|
9494
|
+
yield { type: "conversation_attributed", conversationId: flushedId };
|
|
9495
|
+
}
|
|
9324
9496
|
}
|
|
9325
9497
|
if (sessionKey) {
|
|
9326
9498
|
const commitSession = sessionStore.get(sessionKey);
|
|
@@ -9383,9 +9555,10 @@ ${summary}`;
|
|
|
9383
9555
|
async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accountId, adminModel, sessionKey, maxTurns = 20, attachments = [], retryCount = 0, enabledPlugins, clientTimestamp, adherenceConstraints, agentName) {
|
|
9384
9556
|
const userTimestamp = clientTimestamp ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
9385
9557
|
const managedConvId = getConversationIdForSession(sessionKey);
|
|
9386
|
-
if (!managedConvId) {
|
|
9387
|
-
throw new Error(`invokeManagedAdminAgent: conversationId missing for sessionKey=${sessionKey.slice(0, 8)} \u2014
|
|
9558
|
+
if (!managedConvId && getPendingTurnCount(sessionKey) >= 2) {
|
|
9559
|
+
throw new Error(`invokeManagedAdminAgent: conversationId missing post-flush for sessionKey=${sessionKey.slice(0, 8)} \u2014 maybeFlushConversationBuffer must bind it first`);
|
|
9388
9560
|
}
|
|
9561
|
+
const managedLogKey = managedConvId ?? preflushStreamLogKey(sessionKey);
|
|
9389
9562
|
const pendingTrimmed = consumePendingTrimmedMessages(sessionKey);
|
|
9390
9563
|
if (pendingTrimmed && pendingTrimmed.length > 0) {
|
|
9391
9564
|
const ok = await compactTrimmedMessages(accountId, pendingTrimmed);
|
|
@@ -9393,7 +9566,7 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
9393
9566
|
storePendingTrimmedMessages(sessionKey, pendingTrimmed);
|
|
9394
9567
|
}
|
|
9395
9568
|
}
|
|
9396
|
-
const streamLog = agentLogStream("claude-agent-stream", accountDir,
|
|
9569
|
+
const streamLog = agentLogStream("claude-agent-stream", accountDir, managedLogKey);
|
|
9397
9570
|
streamLog.on("error", () => {
|
|
9398
9571
|
});
|
|
9399
9572
|
const systemPromptTokens = estimateTokens(systemPrompt);
|
|
@@ -9424,7 +9597,7 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
9424
9597
|
if (!cdpOk) streamLog.write(`[${isoTs()}] [warn] ensureCdp failed \u2014 browser-specialist degraded
|
|
9425
9598
|
`);
|
|
9426
9599
|
const managedUserId = getUserIdForSession(sessionKey);
|
|
9427
|
-
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId,
|
|
9600
|
+
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, managedLogKey, managedUserId, enabledPlugins) });
|
|
9428
9601
|
const specialistsDir = resolve5(accountDir, "specialists");
|
|
9429
9602
|
if (!existsSync5(specialistsDir)) streamLog.write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
9430
9603
|
`);
|
|
@@ -9455,16 +9628,16 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
9455
9628
|
cwd: accountDir,
|
|
9456
9629
|
stdio: ["ignore", "pipe", "pipe"],
|
|
9457
9630
|
// Task 556: STREAM_LOG_PATH inherited by Bash-tool subprocesses.
|
|
9458
|
-
env: buildSpawnEnv(accountId, accountDir,
|
|
9631
|
+
env: buildSpawnEnv(accountId, accountDir, managedLogKey)
|
|
9459
9632
|
});
|
|
9460
|
-
const stderrLog = agentLogStream("claude-agent-stderr", accountDir,
|
|
9633
|
+
const stderrLog = agentLogStream("claude-agent-stderr", accountDir, managedLogKey);
|
|
9461
9634
|
stderrLog.on("error", () => {
|
|
9462
9635
|
});
|
|
9463
9636
|
proc.stderr?.pipe(stderrLog);
|
|
9464
9637
|
teeProcStderrToStreamLog(proc, streamLog);
|
|
9465
9638
|
streamLog.write(`[${isoTs()}] [subproc-debug-unavailable] reason=bundled-bun-binary-ignores-node-debug pid=${proc.pid} cli=claude
|
|
9466
9639
|
`);
|
|
9467
|
-
streamLog.write(`[${isoTs()}] [spawn-env] STREAM_LOG_PATH=set pid=${proc.pid} conversationId=${managedConvId} site=managed
|
|
9640
|
+
streamLog.write(`[${isoTs()}] [spawn-env] STREAM_LOG_PATH=set pid=${proc.pid} conversationId=${managedConvId ?? "preflush"} logKey=${managedLogKey} site=managed
|
|
9468
9641
|
`);
|
|
9469
9642
|
if (sessionKey) {
|
|
9470
9643
|
const prev = activeProcesses.get(sessionKey);
|
|
@@ -9474,13 +9647,13 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
9474
9647
|
}
|
|
9475
9648
|
activeProcesses.set(sessionKey, { pid: proc.pid, spawnedAt: Date.now() });
|
|
9476
9649
|
}
|
|
9477
|
-
streamLog.write(`[${isoTs()}] [managed-spawn] pid=${proc.pid} sessionKey=${sessionKey} conversationId=${managedConvId} historyMessages=${history.length} pluginDir=${specialistsDir}
|
|
9650
|
+
streamLog.write(`[${isoTs()}] [managed-spawn] pid=${proc.pid} sessionKey=${sessionKey} conversationId=${managedConvId ?? "preflush"} logKey=${managedLogKey} historyMessages=${history.length} pluginDir=${specialistsDir}
|
|
9478
9651
|
`);
|
|
9479
9652
|
streamLog.write(`[${isoTs()}] [stdin] len=${fullMessage.length} preview=${JSON.stringify(fullMessage.slice(0, 80))}
|
|
9480
9653
|
`);
|
|
9481
9654
|
proc.on("exit", (code, signal) => {
|
|
9482
|
-
console.log(`[process-exit] pid=${proc.pid} code=${code} signal=${signal} sessionKey=${sessionKey ?? "none"} conversationId=${managedConvId}`);
|
|
9483
|
-
if (!streamLog.destroyed && !streamLog.writableEnded) streamLog.write(`[${isoTs()}] [process-exit] pid=${proc.pid} code=${code} signal=${signal} conversationId=${managedConvId}
|
|
9655
|
+
console.log(`[process-exit] pid=${proc.pid} code=${code} signal=${signal} sessionKey=${sessionKey ?? "none"} conversationId=${managedConvId ?? "preflush"}`);
|
|
9656
|
+
if (!streamLog.destroyed && !streamLog.writableEnded) streamLog.write(`[${isoTs()}] [process-exit] pid=${proc.pid} code=${code} signal=${signal} conversationId=${managedConvId ?? "preflush"}
|
|
9484
9657
|
`);
|
|
9485
9658
|
if (sessionKey) activeProcesses.delete(sessionKey);
|
|
9486
9659
|
});
|
|
@@ -9630,17 +9803,24 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
9630
9803
|
`);
|
|
9631
9804
|
const successSession = sessionStore.get(sessionKey);
|
|
9632
9805
|
if (successSession) successSession.lastPeakContextPct = peakContextPct;
|
|
9806
|
+
const assistantTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
9633
9807
|
const convId = sessionStore.get(sessionKey)?.conversationId;
|
|
9634
9808
|
if (convId) {
|
|
9635
|
-
const assistantTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
9636
9809
|
persistMessage(convId, "user", fullMessage, accountId, void 0, userTimestamp).catch(() => {
|
|
9637
9810
|
});
|
|
9638
9811
|
autoLabelSession(convId, fullMessage).catch(() => {
|
|
9639
9812
|
});
|
|
9640
9813
|
if (responseText) persistMessage(convId, "assistant", responseText, accountId, capturedTokens, assistantTimestamp).catch(() => {
|
|
9641
9814
|
});
|
|
9642
|
-
} else
|
|
9643
|
-
|
|
9815
|
+
} else {
|
|
9816
|
+
bufferPendingTurn(sessionKey, { role: "user", content: fullMessage, timestamp: userTimestamp });
|
|
9817
|
+
if (responseText) bufferPendingTurn(sessionKey, { role: "assistant", content: responseText, timestamp: assistantTimestamp, tokens: capturedTokens });
|
|
9818
|
+
const flushedId = await maybeFlushConversationBuffer(sessionKey, "admin", accountId);
|
|
9819
|
+
if (flushedId) {
|
|
9820
|
+
autoLabelSession(flushedId, fullMessage).catch(() => {
|
|
9821
|
+
});
|
|
9822
|
+
yield { type: "conversation_attributed", conversationId: flushedId };
|
|
9823
|
+
}
|
|
9644
9824
|
}
|
|
9645
9825
|
const commitSession = sessionStore.get(sessionKey);
|
|
9646
9826
|
if (commitSession?.pendingCommitmentOffers && commitSession.pendingCommitmentOffers.length > 0) {
|
|
@@ -9712,10 +9892,14 @@ async function* invokePublicAgent(message, systemPrompt, accountId, accountDir,
|
|
|
9712
9892
|
return;
|
|
9713
9893
|
}
|
|
9714
9894
|
const publicConvId = sessionKey ? getConversationIdForSession(sessionKey) : void 0;
|
|
9715
|
-
if (!publicConvId) {
|
|
9716
|
-
throw new Error(`invokePublicAgent: conversationId missing for sessionKey=${sessionKey
|
|
9895
|
+
if (!publicConvId && sessionKey && getPendingTurnCount(sessionKey) >= 2) {
|
|
9896
|
+
throw new Error(`invokePublicAgent: conversationId missing post-flush for sessionKey=${sessionKey.slice(0, 8)} \u2014 maybeFlushConversationBuffer must bind it first`);
|
|
9897
|
+
}
|
|
9898
|
+
const publicLogKey = publicConvId ?? (sessionKey ? preflushStreamLogKey(sessionKey) : void 0);
|
|
9899
|
+
if (!publicLogKey) {
|
|
9900
|
+
throw new Error(`invokePublicAgent: sessionKey required \u2014 cannot resolve log stream without one`);
|
|
9717
9901
|
}
|
|
9718
|
-
const streamLog = agentLogStream("public-agent-stream", accountDir,
|
|
9902
|
+
const streamLog = agentLogStream("public-agent-stream", accountDir, publicLogKey);
|
|
9719
9903
|
streamLog.write(`[${isoTs()}] [public-user-message] ${JSON.stringify(message)}
|
|
9720
9904
|
`);
|
|
9721
9905
|
if (sessionKey) {
|
|
@@ -9952,10 +10136,10 @@ User messages are prefixed with the sender's name in brackets. Address participa
|
|
|
9952
10136
|
`);
|
|
9953
10137
|
}
|
|
9954
10138
|
const conversationId = sessionKey ? sessionStore.get(sessionKey)?.conversationId : void 0;
|
|
10139
|
+
const assistantTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
10140
|
+
const sess = sessionKey ? sessionStore.get(sessionKey) : void 0;
|
|
10141
|
+
const sender = sess?.groupSlug && sess.visitorId && sess.senderDisplayName ? { visitorId: sess.visitorId, displayName: sess.senderDisplayName } : void 0;
|
|
9955
10142
|
if (conversationId) {
|
|
9956
|
-
const assistantTimestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
9957
|
-
const sess = sessionKey ? sessionStore.get(sessionKey) : void 0;
|
|
9958
|
-
const sender = sess?.groupSlug && sess.visitorId && sess.senderDisplayName ? { visitorId: sess.visitorId, displayName: sess.senderDisplayName } : void 0;
|
|
9959
10143
|
persistMessage(conversationId, "user", message, accountId, void 0, userTimestamp, sender).catch(() => {
|
|
9960
10144
|
});
|
|
9961
10145
|
autoLabelSession(conversationId, message).catch(() => {
|
|
@@ -9963,7 +10147,14 @@ User messages are prefixed with the sender's name in brackets. Address participa
|
|
|
9963
10147
|
if (fullText) persistMessage(conversationId, "assistant", fullText, accountId, void 0, assistantTimestamp).catch(() => {
|
|
9964
10148
|
});
|
|
9965
10149
|
} else if (sessionKey) {
|
|
9966
|
-
|
|
10150
|
+
bufferPendingTurn(sessionKey, { role: "user", content: message, timestamp: userTimestamp, sender });
|
|
10151
|
+
if (fullText) bufferPendingTurn(sessionKey, { role: "assistant", content: fullText, timestamp: assistantTimestamp });
|
|
10152
|
+
const flushedId = await maybeFlushConversationBuffer(sessionKey, "public", accountId);
|
|
10153
|
+
if (flushedId) {
|
|
10154
|
+
autoLabelSession(flushedId, message).catch(() => {
|
|
10155
|
+
});
|
|
10156
|
+
yield { type: "conversation_attributed", conversationId: flushedId };
|
|
10157
|
+
}
|
|
9967
10158
|
}
|
|
9968
10159
|
streamLog.end();
|
|
9969
10160
|
}
|
|
@@ -10122,7 +10313,7 @@ ${sessionContext}`;
|
|
|
10122
10313
|
const skillPath = resolve5(PLATFORM_ROOT3, "plugins/admin/skills/onboarding/SKILL.md");
|
|
10123
10314
|
let skillContent = "";
|
|
10124
10315
|
try {
|
|
10125
|
-
skillContent =
|
|
10316
|
+
skillContent = readFileSync7(skillPath, "utf-8");
|
|
10126
10317
|
} catch (err) {
|
|
10127
10318
|
console.error(`[onboarding-inject] accountId=${accountId.slice(0, 8)}\u2026 error=skill-read-failed path=${skillPath} reason=${err instanceof Error ? err.message : String(err)}`);
|
|
10128
10319
|
}
|
|
@@ -10174,13 +10365,23 @@ ${manifest}`;
|
|
|
10174
10365
|
}
|
|
10175
10366
|
const graphRefPath = resolve5(PLATFORM_ROOT3, "plugins/memory/references/graph-primitives.md");
|
|
10176
10367
|
try {
|
|
10177
|
-
const graphRef =
|
|
10368
|
+
const graphRef = readFileSync7(graphRefPath, "utf-8");
|
|
10178
10369
|
baseSystemPrompt += `
|
|
10179
10370
|
|
|
10180
10371
|
${graphRef}`;
|
|
10181
10372
|
} catch (err) {
|
|
10182
10373
|
console.error(`[graph-primitives] reference missing at ${graphRefPath} \u2014 admin session will have no Cypher cookbook: ${err instanceof Error ? err.message : String(err)}`);
|
|
10183
10374
|
}
|
|
10375
|
+
const schemaCypherPath = resolve5(PLATFORM_ROOT3, "neo4j/schema.cypher");
|
|
10376
|
+
const schemaBlock = loadAdminSchemaBlock(schemaCypherPath);
|
|
10377
|
+
if (schemaBlock) {
|
|
10378
|
+
baseSystemPrompt += `
|
|
10379
|
+
|
|
10380
|
+
${schemaBlock.text}`;
|
|
10381
|
+
console.log(
|
|
10382
|
+
`[admin-identity] schemaBlockBytes=${schemaBlock.bytes} labels=${schemaBlock.labelCount} relationshipTypes=${schemaBlock.relationshipTypeCount}`
|
|
10383
|
+
);
|
|
10384
|
+
}
|
|
10184
10385
|
}
|
|
10185
10386
|
if (agentConfig?.budget) {
|
|
10186
10387
|
const pluginTokens = embeddedPlugins.reduce((sum, p) => sum + estimateTokens(p.body), 0);
|
|
@@ -10260,7 +10461,7 @@ Current session key: ${sessionKey}` : systemPromptBase;
|
|
|
10260
10461
|
|
|
10261
10462
|
${gwParts.join("\n")}`;
|
|
10262
10463
|
}
|
|
10263
|
-
if (sessionKey) {
|
|
10464
|
+
if (sessionKey && isDmChannelSessionKey(sessionKey)) {
|
|
10264
10465
|
try {
|
|
10265
10466
|
await ensureConversation(accountId, agentType, sessionKey, void 0, void 0, sessionUserId);
|
|
10266
10467
|
} catch (err) {
|
|
@@ -10389,7 +10590,7 @@ var clientIpMiddleware = async (c, next) => {
|
|
|
10389
10590
|
};
|
|
10390
10591
|
|
|
10391
10592
|
// server/routes/health.ts
|
|
10392
|
-
import { existsSync as existsSync10, readFileSync as
|
|
10593
|
+
import { existsSync as existsSync10, readFileSync as readFileSync12 } from "fs";
|
|
10393
10594
|
import { createConnection as createConnection2 } from "net";
|
|
10394
10595
|
|
|
10395
10596
|
// app/lib/network.ts
|
|
@@ -10414,7 +10615,7 @@ function getLanIp() {
|
|
|
10414
10615
|
import { basename as basename2 } from "path";
|
|
10415
10616
|
|
|
10416
10617
|
// app/lib/review-detector/rules.ts
|
|
10417
|
-
import { readFileSync as
|
|
10618
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync6, statSync as statSync4, mkdirSync as mkdirSync5, renameSync as renameSync2 } from "fs";
|
|
10418
10619
|
import { resolve as resolve6, dirname as dirname3 } from "path";
|
|
10419
10620
|
var DEFAULT_SCAN_INTERVAL_MS = 5e3;
|
|
10420
10621
|
var RATE_LIMIT_PATTERN = "rate[- ]?limit(?:ed| reached| hit)|(?:HTTP|status)[^a-z]{0,3}429|too many requests";
|
|
@@ -10862,7 +11063,7 @@ function loadRules(configDir2) {
|
|
|
10862
11063
|
if (!existsSync6(path2)) {
|
|
10863
11064
|
throw new Error(`rules file missing at ${path2}`);
|
|
10864
11065
|
}
|
|
10865
|
-
const raw2 =
|
|
11066
|
+
const raw2 = readFileSync8(path2, "utf-8");
|
|
10866
11067
|
let parsed;
|
|
10867
11068
|
try {
|
|
10868
11069
|
parsed = JSON.parse(raw2);
|
|
@@ -11027,7 +11228,7 @@ function validateRule(input, label, seenIds) {
|
|
|
11027
11228
|
}
|
|
11028
11229
|
|
|
11029
11230
|
// app/lib/review-detector/sources.ts
|
|
11030
|
-
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
|
|
11231
|
+
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 readFileSync9 } from "fs";
|
|
11031
11232
|
import { resolve as resolve7, join as join4, basename, dirname as dirname4 } from "path";
|
|
11032
11233
|
function tailStatePath(configDir2) {
|
|
11033
11234
|
return resolve7(configDir2, "review-state.json");
|
|
@@ -11036,7 +11237,7 @@ function loadTailState(configDir2) {
|
|
|
11036
11237
|
const path2 = tailStatePath(configDir2);
|
|
11037
11238
|
if (!existsSync7(path2)) return {};
|
|
11038
11239
|
try {
|
|
11039
|
-
const raw2 =
|
|
11240
|
+
const raw2 = readFileSync9(path2, "utf-8");
|
|
11040
11241
|
const parsed = JSON.parse(raw2);
|
|
11041
11242
|
if (!parsed || typeof parsed !== "object") return {};
|
|
11042
11243
|
const clean = {};
|
|
@@ -11206,7 +11407,7 @@ function sourceKey(file) {
|
|
|
11206
11407
|
}
|
|
11207
11408
|
|
|
11208
11409
|
// app/lib/review-detector/writer.ts
|
|
11209
|
-
import { appendFileSync as appendFileSync2, existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as
|
|
11410
|
+
import { appendFileSync as appendFileSync2, existsSync as existsSync8, mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync8, renameSync as renameSync4, statSync as statSync6 } from "fs";
|
|
11210
11411
|
import { resolve as resolve8, dirname as dirname5 } from "path";
|
|
11211
11412
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
11212
11413
|
function reviewLogPath(configDir2) {
|
|
@@ -11345,7 +11546,7 @@ function queueAlert(configDir2, accountId, match2) {
|
|
|
11345
11546
|
async function drainPendingAlerts(configDir2) {
|
|
11346
11547
|
const path2 = pendingAlertsPath(configDir2);
|
|
11347
11548
|
if (!existsSync8(path2)) return { drained: 0, remaining: 0 };
|
|
11348
|
-
const raw2 =
|
|
11549
|
+
const raw2 = readFileSync10(path2, "utf-8");
|
|
11349
11550
|
const lines = raw2.split("\n").filter((l) => l.trim().length > 0);
|
|
11350
11551
|
if (lines.length === 0) return { drained: 0, remaining: 0 };
|
|
11351
11552
|
const remaining = [];
|
|
@@ -12008,7 +12209,7 @@ var WhatsAppConfigSchema = z.object({
|
|
|
12008
12209
|
});
|
|
12009
12210
|
|
|
12010
12211
|
// app/lib/whatsapp/config-persist.ts
|
|
12011
|
-
import { readFileSync as
|
|
12212
|
+
import { readFileSync as readFileSync11, writeFileSync as writeFileSync9, existsSync as existsSync9 } from "fs";
|
|
12012
12213
|
import { resolve as resolve10, join as join5 } from "path";
|
|
12013
12214
|
var TAG3 = "[whatsapp:config]";
|
|
12014
12215
|
function configPath(accountDir) {
|
|
@@ -12017,7 +12218,7 @@ function configPath(accountDir) {
|
|
|
12017
12218
|
function readConfig(accountDir) {
|
|
12018
12219
|
const path2 = configPath(accountDir);
|
|
12019
12220
|
if (!existsSync9(path2)) throw new Error(`account.json not found at ${path2}`);
|
|
12020
|
-
return JSON.parse(
|
|
12221
|
+
return JSON.parse(readFileSync11(path2, "utf-8"));
|
|
12021
12222
|
}
|
|
12022
12223
|
function writeConfig(accountDir, config) {
|
|
12023
12224
|
const path2 = configPath(accountDir);
|
|
@@ -14309,7 +14510,7 @@ app.get("/", async (c) => {
|
|
|
14309
14510
|
let pinConfigured = false;
|
|
14310
14511
|
try {
|
|
14311
14512
|
if (existsSync10(USERS_FILE)) {
|
|
14312
|
-
const raw2 =
|
|
14513
|
+
const raw2 = readFileSync12(USERS_FILE, "utf-8").trim();
|
|
14313
14514
|
if (raw2) {
|
|
14314
14515
|
const users = JSON.parse(raw2);
|
|
14315
14516
|
pinConfigured = Array.isArray(users) && users.length > 0;
|
|
@@ -14665,8 +14866,6 @@ app2.post("/", async (c) => {
|
|
|
14665
14866
|
const newVisitorId = visitorId ?? crypto.randomUUID();
|
|
14666
14867
|
const sessionKey = crypto.randomUUID();
|
|
14667
14868
|
registerSession(sessionKey, "public", accountId, agentSlug);
|
|
14668
|
-
ensureConversation(accountId, "public", sessionKey, newVisitorId, agentSlug).catch(() => {
|
|
14669
|
-
});
|
|
14670
14869
|
const hasImage = agentConfig?.image ? "yes" : "no";
|
|
14671
14870
|
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}`);
|
|
14672
14871
|
return withVisitorCookie(
|
|
@@ -15428,7 +15627,7 @@ var group_default = app4;
|
|
|
15428
15627
|
|
|
15429
15628
|
// app/lib/access-gate.ts
|
|
15430
15629
|
import neo4j2 from "neo4j-driver";
|
|
15431
|
-
import { readFileSync as
|
|
15630
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
15432
15631
|
import { resolve as resolve13 } from "path";
|
|
15433
15632
|
import { randomUUID as randomUUID7, randomInt } from "crypto";
|
|
15434
15633
|
var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT ?? resolve13(process.cwd(), "..");
|
|
@@ -15437,7 +15636,7 @@ function readPassword2() {
|
|
|
15437
15636
|
if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
|
|
15438
15637
|
const passwordFile = resolve13(PLATFORM_ROOT5, "config/.neo4j-password");
|
|
15439
15638
|
try {
|
|
15440
|
-
return
|
|
15639
|
+
return readFileSync13(passwordFile, "utf-8").trim();
|
|
15441
15640
|
} catch {
|
|
15442
15641
|
throw new Error(
|
|
15443
15642
|
`Neo4j password not found. Expected at ${passwordFile} or in NEO4J_PASSWORD env var.`
|
|
@@ -15748,7 +15947,7 @@ async function findActiveGrantByContact(contactValue, agentSlug, accountId) {
|
|
|
15748
15947
|
}
|
|
15749
15948
|
|
|
15750
15949
|
// app/lib/brevo-sms.ts
|
|
15751
|
-
import { readFileSync as
|
|
15950
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync11, mkdirSync as mkdirSync9, existsSync as existsSync12, chmodSync } from "fs";
|
|
15752
15951
|
import { dirname as dirname6 } from "path";
|
|
15753
15952
|
import { resolve as resolve14 } from "path";
|
|
15754
15953
|
var BREVO_API_KEY_FILE = resolve14(MAXY_DIR, ".brevo-api-key");
|
|
@@ -15760,7 +15959,7 @@ if (platformRoot) {
|
|
|
15760
15959
|
try {
|
|
15761
15960
|
const brandPath = resolve14(platformRoot, "config", "brand.json");
|
|
15762
15961
|
if (existsSync12(brandPath)) {
|
|
15763
|
-
const brand = JSON.parse(
|
|
15962
|
+
const brand = JSON.parse(readFileSync14(brandPath, "utf-8"));
|
|
15764
15963
|
if (brand.productName) BREVO_SENDER = brand.productName;
|
|
15765
15964
|
}
|
|
15766
15965
|
} catch {
|
|
@@ -15768,7 +15967,7 @@ if (platformRoot) {
|
|
|
15768
15967
|
}
|
|
15769
15968
|
function readBrevoApiKey() {
|
|
15770
15969
|
try {
|
|
15771
|
-
const key =
|
|
15970
|
+
const key = readFileSync14(BREVO_API_KEY_FILE, "utf-8").trim();
|
|
15772
15971
|
if (!key) {
|
|
15773
15972
|
throw new Error(`Brevo API key file is empty: ${BREVO_API_KEY_FILE}`);
|
|
15774
15973
|
}
|
|
@@ -16199,7 +16398,7 @@ app5.post("/send-otp", async (c) => {
|
|
|
16199
16398
|
var access_default = app5;
|
|
16200
16399
|
|
|
16201
16400
|
// server/routes/telegram.ts
|
|
16202
|
-
import { existsSync as existsSync13, readFileSync as
|
|
16401
|
+
import { existsSync as existsSync13, readFileSync as readFileSync15 } from "fs";
|
|
16203
16402
|
import { timingSafeEqual } from "crypto";
|
|
16204
16403
|
|
|
16205
16404
|
// app/lib/telegram/access-control.ts
|
|
@@ -16237,7 +16436,7 @@ function getWebhookSecret(botType) {
|
|
|
16237
16436
|
const filePath = botType === "admin" ? TELEGRAM_ADMIN_WEBHOOK_SECRET_FILE : TELEGRAM_WEBHOOK_SECRET_FILE;
|
|
16238
16437
|
try {
|
|
16239
16438
|
if (!existsSync13(filePath)) return null;
|
|
16240
|
-
const secret =
|
|
16439
|
+
const secret = readFileSync15(filePath, "utf-8").trim();
|
|
16241
16440
|
return secret || null;
|
|
16242
16441
|
} catch {
|
|
16243
16442
|
return null;
|
|
@@ -16397,7 +16596,7 @@ var telegram_default = app6;
|
|
|
16397
16596
|
// server/routes/whatsapp.ts
|
|
16398
16597
|
import { join as join8, resolve as resolve15, basename as basename4 } from "path";
|
|
16399
16598
|
import { readFile as readFile2, stat as stat3 } from "fs/promises";
|
|
16400
|
-
import { realpathSync as realpathSync2, readdirSync as readdirSync4, readFileSync as
|
|
16599
|
+
import { realpathSync as realpathSync2, readdirSync as readdirSync4, readFileSync as readFileSync16, existsSync as existsSync14 } from "fs";
|
|
16401
16600
|
|
|
16402
16601
|
// app/lib/whatsapp/login.ts
|
|
16403
16602
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
@@ -16885,7 +17084,7 @@ app7.post("/config", async (c) => {
|
|
|
16885
17084
|
const configPath2 = resolve15(agentsDir, entry.name, "config.json");
|
|
16886
17085
|
if (!existsSync14(configPath2)) continue;
|
|
16887
17086
|
try {
|
|
16888
|
-
const config = JSON.parse(
|
|
17087
|
+
const config = JSON.parse(readFileSync16(configPath2, "utf-8"));
|
|
16889
17088
|
agents.push({ slug: entry.name, displayName: config.displayName ?? entry.name });
|
|
16890
17089
|
} catch {
|
|
16891
17090
|
console.error(`${TAG18} config action=list-public-agents error="failed to parse config.json for agent ${entry.name}" \u2014 skipping`);
|
|
@@ -17097,7 +17296,7 @@ var whatsapp_default = app7;
|
|
|
17097
17296
|
|
|
17098
17297
|
// server/routes/onboarding.ts
|
|
17099
17298
|
import { spawn as spawn3, execFileSync as execFileSync2 } from "child_process";
|
|
17100
|
-
import { openSync as openSync4, closeSync as closeSync4, writeFileSync as writeFileSync12, writeSync, existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as
|
|
17299
|
+
import { openSync as openSync4, closeSync as closeSync4, writeFileSync as writeFileSync12, writeSync, existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync17, unlinkSync as unlinkSync4 } from "fs";
|
|
17101
17300
|
import { resolve as resolve16, dirname as dirname7 } from "path";
|
|
17102
17301
|
import { createHash, randomUUID as randomUUID9 } from "crypto";
|
|
17103
17302
|
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
@@ -17106,7 +17305,7 @@ function hashPin(pin) {
|
|
|
17106
17305
|
}
|
|
17107
17306
|
function readUsersFile() {
|
|
17108
17307
|
if (!existsSync15(USERS_FILE)) return null;
|
|
17109
|
-
const raw2 =
|
|
17308
|
+
const raw2 = readFileSync17(USERS_FILE, "utf-8").trim();
|
|
17110
17309
|
if (!raw2) return [];
|
|
17111
17310
|
return JSON.parse(raw2);
|
|
17112
17311
|
}
|
|
@@ -17217,7 +17416,7 @@ app8.post("/set-pin", async (c) => {
|
|
|
17217
17416
|
const account = resolveAccount();
|
|
17218
17417
|
if (account) {
|
|
17219
17418
|
try {
|
|
17220
|
-
const config = JSON.parse(
|
|
17419
|
+
const config = JSON.parse(readFileSync17(`${account.accountDir}/account.json`, "utf-8"));
|
|
17221
17420
|
if (!config.admins) config.admins = [];
|
|
17222
17421
|
if (!config.admins.some((a) => a.userId === userId)) {
|
|
17223
17422
|
config.admins.push({ userId, role: "owner" });
|
|
@@ -17275,7 +17474,7 @@ app8.post("/skip", async (c) => {
|
|
|
17275
17474
|
const brandPath = PLATFORM_ROOT7 ? resolve16(PLATFORM_ROOT7, "config", "brand.json") : "";
|
|
17276
17475
|
if (brandPath && existsSync15(brandPath)) {
|
|
17277
17476
|
try {
|
|
17278
|
-
const brand = JSON.parse(
|
|
17477
|
+
const brand = JSON.parse(readFileSync17(brandPath, "utf-8"));
|
|
17279
17478
|
if (brand.productName) agentName = brand.productName;
|
|
17280
17479
|
} catch (err) {
|
|
17281
17480
|
console.error(`[onboarding-skip] brand.json read failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -17469,14 +17668,14 @@ app9.post("/", async (c) => {
|
|
|
17469
17668
|
var client_error_default = app9;
|
|
17470
17669
|
|
|
17471
17670
|
// server/routes/admin/session.ts
|
|
17472
|
-
import { readFileSync as
|
|
17671
|
+
import { readFileSync as readFileSync18, existsSync as existsSync17 } from "fs";
|
|
17473
17672
|
import { createHash as createHash2 } from "crypto";
|
|
17474
17673
|
function hashPin2(pin) {
|
|
17475
17674
|
return createHash2("sha256").update(pin).digest("hex");
|
|
17476
17675
|
}
|
|
17477
17676
|
function readUsersFile2() {
|
|
17478
17677
|
if (!existsSync17(USERS_FILE)) return null;
|
|
17479
|
-
const raw2 =
|
|
17678
|
+
const raw2 = readFileSync18(USERS_FILE, "utf-8").trim();
|
|
17480
17679
|
if (!raw2) return [];
|
|
17481
17680
|
return JSON.parse(raw2);
|
|
17482
17681
|
}
|
|
@@ -17498,11 +17697,7 @@ async function createAdminSession(accountId, thinkingView, userId, userName) {
|
|
|
17498
17697
|
businessName = branding?.name || void 0;
|
|
17499
17698
|
} catch {
|
|
17500
17699
|
}
|
|
17501
|
-
|
|
17502
|
-
if (userId) {
|
|
17503
|
-
conversationId = await createNewAdminConversation(userId, accountId, sessionKey);
|
|
17504
|
-
}
|
|
17505
|
-
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)}`);
|
|
17700
|
+
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)}`);
|
|
17506
17701
|
return {
|
|
17507
17702
|
session_key: sessionKey,
|
|
17508
17703
|
agent_id: "admin",
|
|
@@ -17511,7 +17706,7 @@ async function createAdminSession(accountId, thinkingView, userId, userName) {
|
|
|
17511
17706
|
thinkingView: effectiveThinkingView,
|
|
17512
17707
|
onboardingComplete,
|
|
17513
17708
|
businessName,
|
|
17514
|
-
conversationId
|
|
17709
|
+
conversationId: null
|
|
17515
17710
|
};
|
|
17516
17711
|
}
|
|
17517
17712
|
var app10 = new Hono2();
|
|
@@ -18176,7 +18371,7 @@ app12.post("/", requireAdminSession, async (c) => {
|
|
|
18176
18371
|
var compact_default = app12;
|
|
18177
18372
|
|
|
18178
18373
|
// server/routes/admin/logs.ts
|
|
18179
|
-
import { existsSync as existsSync18, readdirSync as readdirSync5, readFileSync as
|
|
18374
|
+
import { existsSync as existsSync18, readdirSync as readdirSync5, readFileSync as readFileSync19, statSync as statSync9 } from "fs";
|
|
18180
18375
|
import { resolve as resolve18, basename as basename5 } from "path";
|
|
18181
18376
|
var TAIL_BYTES = 8192;
|
|
18182
18377
|
var app13 = new Hono2();
|
|
@@ -18195,7 +18390,7 @@ app13.get("/", async (c) => {
|
|
|
18195
18390
|
const filePath = resolve18(dir, safe);
|
|
18196
18391
|
searched.push(filePath);
|
|
18197
18392
|
try {
|
|
18198
|
-
const content =
|
|
18393
|
+
const content = readFileSync19(filePath, "utf-8");
|
|
18199
18394
|
const headers = { "Content-Type": "text/plain; charset=utf-8" };
|
|
18200
18395
|
if (download) headers["Content-Disposition"] = `attachment; filename="${safe}"`;
|
|
18201
18396
|
return new Response(content, { headers });
|
|
@@ -18237,7 +18432,7 @@ app13.get("/", async (c) => {
|
|
|
18237
18432
|
const filePath = resolve18(dir, fileName);
|
|
18238
18433
|
searched.push(filePath);
|
|
18239
18434
|
try {
|
|
18240
|
-
const content =
|
|
18435
|
+
const content = readFileSync19(filePath, "utf-8");
|
|
18241
18436
|
const headers = { "Content-Type": "text/plain; charset=utf-8" };
|
|
18242
18437
|
if (download) headers["Content-Disposition"] = `attachment; filename="${fileName}"`;
|
|
18243
18438
|
return new Response(content, { headers });
|
|
@@ -18264,7 +18459,7 @@ app13.get("/", async (c) => {
|
|
|
18264
18459
|
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 }) => {
|
|
18265
18460
|
seen.add(name);
|
|
18266
18461
|
try {
|
|
18267
|
-
const content =
|
|
18462
|
+
const content = readFileSync19(resolve18(dir, name));
|
|
18268
18463
|
const tail = content.length > TAIL_BYTES ? content.subarray(content.length - TAIL_BYTES).toString("utf-8") : content.toString("utf-8");
|
|
18269
18464
|
logs[name] = tail.trim() || "(empty)";
|
|
18270
18465
|
} catch (err) {
|
|
@@ -18349,7 +18544,7 @@ app15.get("/:attachmentId", requireAdminSession, async (c) => {
|
|
|
18349
18544
|
var attachment_default = app15;
|
|
18350
18545
|
|
|
18351
18546
|
// server/routes/admin/account.ts
|
|
18352
|
-
import { readFileSync as
|
|
18547
|
+
import { readFileSync as readFileSync20, writeFileSync as writeFileSync13 } from "fs";
|
|
18353
18548
|
import { resolve as resolve20 } from "path";
|
|
18354
18549
|
var VALID_CONTEXT_MODES = ["managed", "claude-code"];
|
|
18355
18550
|
var app16 = new Hono2();
|
|
@@ -18368,7 +18563,7 @@ app16.patch("/", requireAdminSession, async (c) => {
|
|
|
18368
18563
|
if (!account) return c.json({ error: "No account configured" }, 500);
|
|
18369
18564
|
const configPath2 = resolve20(account.accountDir, "account.json");
|
|
18370
18565
|
try {
|
|
18371
|
-
const raw2 =
|
|
18566
|
+
const raw2 = readFileSync20(configPath2, "utf-8");
|
|
18372
18567
|
const config = JSON.parse(raw2);
|
|
18373
18568
|
config.contextMode = contextMode;
|
|
18374
18569
|
writeFileSync13(configPath2, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
@@ -18383,7 +18578,7 @@ var account_default = app16;
|
|
|
18383
18578
|
|
|
18384
18579
|
// server/routes/admin/agents.ts
|
|
18385
18580
|
import { resolve as resolve21 } from "path";
|
|
18386
|
-
import { readdirSync as readdirSync6, readFileSync as
|
|
18581
|
+
import { readdirSync as readdirSync6, readFileSync as readFileSync21, existsSync as existsSync20, rmSync as rmSync3 } from "fs";
|
|
18387
18582
|
var app17 = new Hono2();
|
|
18388
18583
|
app17.get("/", (c) => {
|
|
18389
18584
|
const account = resolveAccount();
|
|
@@ -18399,7 +18594,7 @@ app17.get("/", (c) => {
|
|
|
18399
18594
|
const configPath2 = resolve21(agentsDir, entry.name, "config.json");
|
|
18400
18595
|
if (!existsSync20(configPath2)) continue;
|
|
18401
18596
|
try {
|
|
18402
|
-
const config = JSON.parse(
|
|
18597
|
+
const config = JSON.parse(readFileSync21(configPath2, "utf-8"));
|
|
18403
18598
|
agents.push({
|
|
18404
18599
|
slug: entry.name,
|
|
18405
18600
|
displayName: config.displayName ?? entry.name,
|
|
@@ -18441,7 +18636,7 @@ app17.delete("/:slug", (c) => {
|
|
|
18441
18636
|
var agents_default = app17;
|
|
18442
18637
|
|
|
18443
18638
|
// server/routes/admin/version.ts
|
|
18444
|
-
import { existsSync as existsSync21, readFileSync as
|
|
18639
|
+
import { existsSync as existsSync21, readFileSync as readFileSync22 } from "fs";
|
|
18445
18640
|
import { resolve as resolve22, join as join10 } from "path";
|
|
18446
18641
|
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ?? resolve22(process.cwd(), "..");
|
|
18447
18642
|
var brandHostname = "maxy";
|
|
@@ -18449,7 +18644,7 @@ var brandNpmPackage = "@rubytech/create-maxy";
|
|
|
18449
18644
|
var brandJsonPath = join10(PLATFORM_ROOT8, "config", "brand.json");
|
|
18450
18645
|
if (existsSync21(brandJsonPath)) {
|
|
18451
18646
|
try {
|
|
18452
|
-
const brand = JSON.parse(
|
|
18647
|
+
const brand = JSON.parse(readFileSync22(brandJsonPath, "utf-8"));
|
|
18453
18648
|
if (brand.hostname) brandHostname = brand.hostname;
|
|
18454
18649
|
if (brand.npm?.packageName) brandNpmPackage = brand.npm.packageName;
|
|
18455
18650
|
} catch {
|
|
@@ -18461,7 +18656,7 @@ var REGISTRY_URL = `https://registry.npmjs.org/${NPM_PACKAGE}/latest`;
|
|
|
18461
18656
|
var FETCH_TIMEOUT_MS = 5e3;
|
|
18462
18657
|
function readInstalled() {
|
|
18463
18658
|
if (!existsSync21(VERSION_FILE)) return "unknown";
|
|
18464
|
-
const content =
|
|
18659
|
+
const content = readFileSync22(VERSION_FILE, "utf-8").trim();
|
|
18465
18660
|
return content || "unknown";
|
|
18466
18661
|
}
|
|
18467
18662
|
async function fetchLatest() {
|
|
@@ -18571,16 +18766,10 @@ app19.post("/new", requireAdminSession, async (c) => {
|
|
|
18571
18766
|
const newSessionKey = crypto3.randomUUID();
|
|
18572
18767
|
const userName = getUserNameForSession(oldSessionKey);
|
|
18573
18768
|
registerSession(newSessionKey, "admin", accountId, void 0, userId, userName);
|
|
18574
|
-
const conversationId = await createNewAdminConversation(userId, accountId, newSessionKey);
|
|
18575
|
-
if (!conversationId) {
|
|
18576
|
-
unregisterSession(newSessionKey);
|
|
18577
|
-
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`);
|
|
18578
|
-
return c.json({ error: "Failed to create conversation" }, 500);
|
|
18579
|
-
}
|
|
18580
18769
|
const previousConversationId = clearSessionHistory(oldSessionKey);
|
|
18581
18770
|
unregisterSession(oldSessionKey);
|
|
18582
|
-
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
|
|
18583
|
-
return c.json({ session_key: newSessionKey, conversationId });
|
|
18771
|
+
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`);
|
|
18772
|
+
return c.json({ session_key: newSessionKey, conversationId: null });
|
|
18584
18773
|
});
|
|
18585
18774
|
app19.delete("/:id", requireAdminSession, async (c) => {
|
|
18586
18775
|
const conversationId = c.req.param("id");
|
|
@@ -18956,7 +19145,7 @@ var events_default = app23;
|
|
|
18956
19145
|
// server/routes/admin/cloudflare.ts
|
|
18957
19146
|
import { homedir as homedir3 } from "os";
|
|
18958
19147
|
import { resolve as resolve24 } from "path";
|
|
18959
|
-
import { readFileSync as
|
|
19148
|
+
import { readFileSync as readFileSync24 } from "fs";
|
|
18960
19149
|
|
|
18961
19150
|
// app/lib/dns-label.ts
|
|
18962
19151
|
var VALID_LABEL = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$/;
|
|
@@ -18972,14 +19161,14 @@ function isValidDomain(value) {
|
|
|
18972
19161
|
}
|
|
18973
19162
|
|
|
18974
19163
|
// app/lib/alias-domains.ts
|
|
18975
|
-
import { existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as
|
|
19164
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync12, readFileSync as readFileSync23, writeFileSync as writeFileSync14 } from "fs";
|
|
18976
19165
|
import { dirname as dirname9 } from "path";
|
|
18977
19166
|
import { resolve as resolve23 } from "path";
|
|
18978
19167
|
var ALIAS_DOMAINS_PATH = resolve23(MAXY_DIR, "alias-domains.json");
|
|
18979
19168
|
function readExisting() {
|
|
18980
19169
|
if (!existsSync22(ALIAS_DOMAINS_PATH)) return /* @__PURE__ */ new Set();
|
|
18981
19170
|
try {
|
|
18982
|
-
const parsed = JSON.parse(
|
|
19171
|
+
const parsed = JSON.parse(readFileSync23(ALIAS_DOMAINS_PATH, "utf-8"));
|
|
18983
19172
|
if (!Array.isArray(parsed)) return /* @__PURE__ */ new Set();
|
|
18984
19173
|
return new Set(parsed.filter((h) => typeof h === "string"));
|
|
18985
19174
|
} catch {
|
|
@@ -19001,7 +19190,7 @@ function loadBrandInfo() {
|
|
|
19001
19190
|
const platformRoot2 = process.env.MAXY_PLATFORM_ROOT ?? resolve24(process.cwd(), "..");
|
|
19002
19191
|
const brandPath = resolve24(platformRoot2, "config", "brand.json");
|
|
19003
19192
|
try {
|
|
19004
|
-
const parsed = JSON.parse(
|
|
19193
|
+
const parsed = JSON.parse(readFileSync24(brandPath, "utf-8"));
|
|
19005
19194
|
const hostname2 = typeof parsed.hostname === "string" && parsed.hostname ? parsed.hostname : "maxy";
|
|
19006
19195
|
const configDir2 = typeof parsed.configDir === "string" && parsed.configDir ? parsed.configDir : ".maxy";
|
|
19007
19196
|
return { hostname: hostname2, configDir: configDir2 };
|
|
@@ -19404,25 +19593,72 @@ async function trashNode(params) {
|
|
|
19404
19593
|
const setNullClauses = Object.keys(originalKeys).map((k) => `n.\`${k}\` = null`).join(", ");
|
|
19405
19594
|
const setNullSuffix = setNullClauses ? `, ${setNullClauses}` : "";
|
|
19406
19595
|
const trashedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
19407
|
-
|
|
19408
|
-
|
|
19409
|
-
|
|
19410
|
-
|
|
19411
|
-
|
|
19412
|
-
|
|
19413
|
-
|
|
19414
|
-
|
|
19415
|
-
|
|
19416
|
-
|
|
19417
|
-
|
|
19418
|
-
|
|
19419
|
-
|
|
19596
|
+
const isConversation = baseLabels.includes("Conversation");
|
|
19597
|
+
const messageUniqueKeys = UNIQUE_KEYS_BY_LABEL["Message"] ?? [];
|
|
19598
|
+
let cascadedMessageCount = 0;
|
|
19599
|
+
await session.executeWrite(async (tx) => {
|
|
19600
|
+
await tx.run(
|
|
19601
|
+
`MATCH (n) WHERE elementId(n) = $eid
|
|
19602
|
+
SET n:Trashed,
|
|
19603
|
+
n.trashedAt = datetime($trashedAt),
|
|
19604
|
+
n.trashedBy = $by,
|
|
19605
|
+
n.trashReason = $reason,
|
|
19606
|
+
n._trashedKeys = $trashedKeysJson${setNullSuffix}`,
|
|
19607
|
+
{
|
|
19608
|
+
eid: elementId,
|
|
19609
|
+
trashedAt,
|
|
19610
|
+
by,
|
|
19611
|
+
reason: reason ?? null,
|
|
19612
|
+
trashedKeysJson: JSON.stringify(originalKeys)
|
|
19613
|
+
}
|
|
19614
|
+
);
|
|
19615
|
+
if (isConversation) {
|
|
19616
|
+
const collectKeys = messageUniqueKeys.length > 0 ? messageUniqueKeys.map((k) => `\`${k}\`: m.\`${k}\``).join(", ") : "";
|
|
19617
|
+
const collectMsgProps = collectKeys ? `, {${collectKeys}}` : ", {}";
|
|
19618
|
+
const collected = await tx.run(
|
|
19619
|
+
`MATCH (c) WHERE elementId(c) = $eid
|
|
19620
|
+
MATCH (m:Message)-[:PART_OF]->(c)
|
|
19621
|
+
WHERE NOT m:Trashed
|
|
19622
|
+
RETURN elementId(m) AS meid${collectMsgProps} AS keys`,
|
|
19623
|
+
{ eid: elementId }
|
|
19624
|
+
);
|
|
19625
|
+
for (const rec of collected.records) {
|
|
19626
|
+
const meid = rec.get("meid");
|
|
19627
|
+
const keys = rec.get("keys");
|
|
19628
|
+
const liveKeys = {};
|
|
19629
|
+
for (const k of messageUniqueKeys) {
|
|
19630
|
+
if (keys[k] !== void 0 && keys[k] !== null) liveKeys[k] = keys[k];
|
|
19631
|
+
}
|
|
19632
|
+
const msgSetNulls = Object.keys(liveKeys).length > 0 ? ", " + Object.keys(liveKeys).map((k) => `m.\`${k}\` = null`).join(", ") : "";
|
|
19633
|
+
await tx.run(
|
|
19634
|
+
`MATCH (m) WHERE elementId(m) = $meid
|
|
19635
|
+
SET m:Trashed,
|
|
19636
|
+
m.trashedAt = datetime($trashedAt),
|
|
19637
|
+
m.trashedBy = $by,
|
|
19638
|
+
m.trashReason = $reason,
|
|
19639
|
+
m._trashedKeys = $trashedKeysJson${msgSetNulls}`,
|
|
19640
|
+
{
|
|
19641
|
+
meid,
|
|
19642
|
+
trashedAt,
|
|
19643
|
+
by: `${by}:cascade-from-conversation`,
|
|
19644
|
+
reason: reason ?? `cascade from Conversation ${elementId}`,
|
|
19645
|
+
trashedKeysJson: JSON.stringify(liveKeys)
|
|
19646
|
+
}
|
|
19647
|
+
);
|
|
19648
|
+
}
|
|
19649
|
+
cascadedMessageCount = collected.records.length;
|
|
19420
19650
|
}
|
|
19421
|
-
);
|
|
19651
|
+
});
|
|
19422
19652
|
process.stderr.write(
|
|
19423
19653
|
`[trash:marked] accountId=${accountId} elementId=${elementId} labels=${baseLabels.join(",")} by=${by} reason=${reason ?? "null"}
|
|
19424
19654
|
`
|
|
19425
19655
|
);
|
|
19656
|
+
if (isConversation) {
|
|
19657
|
+
process.stderr.write(
|
|
19658
|
+
`[trash:cascaded] accountId=${accountId} conversationElementId=${elementId} messageCount=${cascadedMessageCount} by=${by}
|
|
19659
|
+
`
|
|
19660
|
+
);
|
|
19661
|
+
}
|
|
19426
19662
|
return {
|
|
19427
19663
|
trashed: true,
|
|
19428
19664
|
alreadyTrashed: false,
|
|
@@ -19473,16 +19709,50 @@ async function restoreNode(params) {
|
|
|
19473
19709
|
const setSuffix = setClauses ? `, ${setClauses}` : "";
|
|
19474
19710
|
const setParams = { eid: elementId };
|
|
19475
19711
|
for (const [k, v] of Object.entries(originalKeys)) setParams[`val_${k}`] = v;
|
|
19476
|
-
|
|
19477
|
-
|
|
19478
|
-
|
|
19479
|
-
|
|
19480
|
-
|
|
19481
|
-
|
|
19712
|
+
const isConversation = baseLabels.includes("Conversation");
|
|
19713
|
+
let cascadedMessageCount = 0;
|
|
19714
|
+
await session.executeWrite(async (tx) => {
|
|
19715
|
+
await tx.run(
|
|
19716
|
+
`MATCH (n:Trashed) WHERE elementId(n) = $eid
|
|
19717
|
+
REMOVE n:Trashed, n.trashedAt, n.trashedBy, n.trashReason, n._trashedKeys
|
|
19718
|
+
SET n.restoredAt = datetime()${setSuffix}`,
|
|
19719
|
+
setParams
|
|
19720
|
+
);
|
|
19721
|
+
if (isConversation) {
|
|
19722
|
+
const collected = await tx.run(
|
|
19723
|
+
`MATCH (c) WHERE elementId(c) = $eid
|
|
19724
|
+
MATCH (m:Trashed:Message)-[:PART_OF]->(c)
|
|
19725
|
+
WHERE m.trashedBy ENDS WITH ':cascade-from-conversation'
|
|
19726
|
+
RETURN elementId(m) AS meid, m._trashedKeys AS keysJson`,
|
|
19727
|
+
{ eid: elementId }
|
|
19728
|
+
);
|
|
19729
|
+
for (const rec of collected.records) {
|
|
19730
|
+
const meid = rec.get("meid");
|
|
19731
|
+
const keysJson2 = rec.get("keysJson");
|
|
19732
|
+
const keys = keysJson2 ? JSON.parse(keysJson2) : {};
|
|
19733
|
+
const setClause = Object.keys(keys).length > 0 ? ", " + Object.keys(keys).map((k) => `m.\`${k}\` = $val_${k}`).join(", ") : "";
|
|
19734
|
+
const msgParams = { meid };
|
|
19735
|
+
for (const [k, v] of Object.entries(keys)) msgParams[`val_${k}`] = v;
|
|
19736
|
+
await tx.run(
|
|
19737
|
+
`MATCH (m) WHERE elementId(m) = $meid
|
|
19738
|
+
REMOVE m:Trashed, m.trashedAt, m.trashedBy, m.trashReason, m._trashedKeys
|
|
19739
|
+
SET m.restoredAt = datetime()${setClause}`,
|
|
19740
|
+
msgParams
|
|
19741
|
+
);
|
|
19742
|
+
}
|
|
19743
|
+
cascadedMessageCount = collected.records.length;
|
|
19744
|
+
}
|
|
19745
|
+
});
|
|
19482
19746
|
process.stderr.write(
|
|
19483
19747
|
`[trash:restored] accountId=${accountId} elementId=${elementId} labels=${baseLabels.join(",")}
|
|
19484
19748
|
`
|
|
19485
19749
|
);
|
|
19750
|
+
if (isConversation) {
|
|
19751
|
+
process.stderr.write(
|
|
19752
|
+
`[trash:cascade-restored] accountId=${accountId} conversationElementId=${elementId} messageCount=${cascadedMessageCount}
|
|
19753
|
+
`
|
|
19754
|
+
);
|
|
19755
|
+
}
|
|
19486
19756
|
return {
|
|
19487
19757
|
restored: true,
|
|
19488
19758
|
nodeId: elementId,
|
|
@@ -19940,24 +20210,35 @@ var GRAPH_LABEL_COLOURS = {
|
|
|
19940
20210
|
Review: "#059669",
|
|
19941
20211
|
ImageObject: "#6EE7B7",
|
|
19942
20212
|
// Conversational
|
|
20213
|
+
//
|
|
20214
|
+
// Task 649 palette — the four sublabelled conversation/message labels
|
|
20215
|
+
// (AdminConversation, PublicConversation, UserMessage, AssistantMessage)
|
|
20216
|
+
// are assigned four pairwise-distinguishable hues: indigo / purple /
|
|
20217
|
+
// orange / yellow. The Task 633 violet-shade split (darker for admin,
|
|
20218
|
+
// lighter for public) was indistinguishable on canvas and in the legend;
|
|
20219
|
+
// hue-shift replaces lightness-shift.
|
|
20220
|
+
//
|
|
20221
|
+
// Constraints honoured:
|
|
20222
|
+
// - Pairwise distinct among the four in-family labels.
|
|
20223
|
+
// - UserMessage=#F97316 reserved (Person/Preference orange cue) —
|
|
20224
|
+
// the other three are non-orange.
|
|
20225
|
+
// - No collision with Task/Project/Event pinks (#DB2777/#BE185D/#EC4899),
|
|
20226
|
+
// Knowledge greens, Workflow cyans, LocalBusiness blues.
|
|
20227
|
+
// - Conversation base (#7C3AED) kept as pre-backfill fallback for
|
|
20228
|
+
// nodes that pre-date the AdminConversation/PublicConversation split.
|
|
20229
|
+
// - Message base reassigned away from old #A78BFA to avoid near-
|
|
20230
|
+
// collision with the new PublicConversation=#A855F7; slate signals
|
|
20231
|
+
// "legacy/system/tool" — nodes carrying only :Message without a
|
|
20232
|
+
// role-sublabel.
|
|
20233
|
+
// - Out of family (intentionally not touched): OnboardingState=#8B5CF6
|
|
20234
|
+
// sits in the violet neighbourhood; palette reassignment outside
|
|
20235
|
+
// the conversation/message family is out of scope for Task 649.
|
|
19943
20236
|
Conversation: "#7C3AED",
|
|
19944
|
-
|
|
19945
|
-
|
|
19946
|
-
|
|
19947
|
-
// alone. Both within the Conversation family — the hue shift is subtle
|
|
19948
|
-
// enough to stay legible on adjacent nodes yet distinct enough to be
|
|
19949
|
-
// unmistakable in the popover legend.
|
|
19950
|
-
AdminConversation: "#5B21B6",
|
|
19951
|
-
PublicConversation: "#A78BFA",
|
|
19952
|
-
Message: "#A78BFA",
|
|
19953
|
-
// Task 633 — user vs assistant disambiguation on /graph. UserMessage
|
|
19954
|
-
// picks up the Person/Preference orange family cue (the user is a
|
|
19955
|
-
// person); AssistantMessage stays violet-adjacent to reinforce the
|
|
19956
|
-
// AdminConversation cue (both are admin-side). system/tool Messages
|
|
19957
|
-
// (not written by the persist path today; reserved shape) stay on the
|
|
19958
|
-
// base `:Message` colour.
|
|
20237
|
+
AdminConversation: "#4338CA",
|
|
20238
|
+
PublicConversation: "#A855F7",
|
|
20239
|
+
Message: "#64748B",
|
|
19959
20240
|
UserMessage: "#F97316",
|
|
19960
|
-
AssistantMessage: "#
|
|
20241
|
+
AssistantMessage: "#FACC15",
|
|
19961
20242
|
ToolCall: "#C4B5FD",
|
|
19962
20243
|
// Tasks / projects / events
|
|
19963
20244
|
Task: "#DB2777",
|
|
@@ -20729,7 +21010,7 @@ if (BRAND_JSON_PATH && !existsSync23(BRAND_JSON_PATH)) {
|
|
|
20729
21010
|
}
|
|
20730
21011
|
if (BRAND_JSON_PATH && existsSync23(BRAND_JSON_PATH)) {
|
|
20731
21012
|
try {
|
|
20732
|
-
const parsed = JSON.parse(
|
|
21013
|
+
const parsed = JSON.parse(readFileSync25(BRAND_JSON_PATH, "utf-8"));
|
|
20733
21014
|
BRAND = { ...BRAND, ...parsed };
|
|
20734
21015
|
} catch (err) {
|
|
20735
21016
|
console.error(`[brand] Failed to parse brand.json: ${err.message}`);
|
|
@@ -20752,7 +21033,7 @@ var ALIAS_DOMAINS_PATH2 = join12(homedir4(), BRAND.configDir, "alias-domains.jso
|
|
|
20752
21033
|
function loadAliasDomains() {
|
|
20753
21034
|
try {
|
|
20754
21035
|
if (!existsSync23(ALIAS_DOMAINS_PATH2)) return null;
|
|
20755
|
-
const parsed = JSON.parse(
|
|
21036
|
+
const parsed = JSON.parse(readFileSync25(ALIAS_DOMAINS_PATH2, "utf-8"));
|
|
20756
21037
|
if (!Array.isArray(parsed)) {
|
|
20757
21038
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
20758
21039
|
return null;
|
|
@@ -20877,10 +21158,7 @@ app35.post("/__remote-auth/login", async (c) => {
|
|
|
20877
21158
|
}
|
|
20878
21159
|
});
|
|
20879
21160
|
});
|
|
20880
|
-
app35.get("/__remote-auth/logout", (
|
|
20881
|
-
const cookieHeader = c.req.header("cookie");
|
|
20882
|
-
const token = parseCookie(cookieHeader, "__remote_session");
|
|
20883
|
-
if (token) invalidateRemoteSession(token);
|
|
21161
|
+
app35.get("/__remote-auth/logout", () => {
|
|
20884
21162
|
return new Response(null, {
|
|
20885
21163
|
status: 302,
|
|
20886
21164
|
headers: {
|
|
@@ -21046,16 +21324,6 @@ app35.use("*", async (c, next) => {
|
|
|
21046
21324
|
console.error(`[remote-auth] login required ip=${clientIp} path=${path2}`);
|
|
21047
21325
|
return c.html(renderLoginPage({ ...resolveRemoteAuthOpts(), redirect: path2 }), 200);
|
|
21048
21326
|
});
|
|
21049
|
-
function parseCookie(cookieHeader, name) {
|
|
21050
|
-
if (!cookieHeader) return null;
|
|
21051
|
-
const match2 = cookieHeader.match(new RegExp(`(?:^|;\\s*)${name}=([^;]*)`));
|
|
21052
|
-
if (!match2) return null;
|
|
21053
|
-
try {
|
|
21054
|
-
return decodeURIComponent(match2[1]);
|
|
21055
|
-
} catch {
|
|
21056
|
-
return null;
|
|
21057
|
-
}
|
|
21058
|
-
}
|
|
21059
21327
|
app35.route("/api/health", health_default);
|
|
21060
21328
|
app35.route("/api/session", session_default);
|
|
21061
21329
|
app35.route("/api/chat", chat_default);
|
|
@@ -21105,7 +21373,7 @@ app35.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
21105
21373
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
21106
21374
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
21107
21375
|
console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
|
|
21108
|
-
const body =
|
|
21376
|
+
const body = readFileSync25(filePath);
|
|
21109
21377
|
return c.body(body, 200, {
|
|
21110
21378
|
"Content-Type": contentType,
|
|
21111
21379
|
"Cache-Control": "public, max-age=3600"
|
|
@@ -21135,7 +21403,7 @@ app35.get("/generated/:filename", (c) => {
|
|
|
21135
21403
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
21136
21404
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
21137
21405
|
console.log(`[generated] serve file=${filename} status=200`);
|
|
21138
|
-
const body =
|
|
21406
|
+
const body = readFileSync25(filePath);
|
|
21139
21407
|
return c.body(body, 200, {
|
|
21140
21408
|
"Content-Type": contentType,
|
|
21141
21409
|
"Cache-Control": "public, max-age=86400"
|
|
@@ -21146,7 +21414,7 @@ var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
|
21146
21414
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
21147
21415
|
if (BRAND_JSON_PATH && existsSync23(BRAND_JSON_PATH)) {
|
|
21148
21416
|
try {
|
|
21149
|
-
const fullBrand = JSON.parse(
|
|
21417
|
+
const fullBrand = JSON.parse(readFileSync25(BRAND_JSON_PATH, "utf-8"));
|
|
21150
21418
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
21151
21419
|
brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
|
|
21152
21420
|
} catch {
|
|
@@ -21165,7 +21433,7 @@ function readInstalledVersion() {
|
|
|
21165
21433
|
if (!PLATFORM_ROOT10) return "unknown";
|
|
21166
21434
|
const versionFile = join12(PLATFORM_ROOT10, "config", `.${BRAND.hostname}-version`);
|
|
21167
21435
|
if (!existsSync23(versionFile)) return "unknown";
|
|
21168
|
-
const content =
|
|
21436
|
+
const content = readFileSync25(versionFile, "utf-8").trim();
|
|
21169
21437
|
return content || "unknown";
|
|
21170
21438
|
} catch {
|
|
21171
21439
|
return "unknown";
|
|
@@ -21206,7 +21474,7 @@ var clientErrorReporterScript = `<script>
|
|
|
21206
21474
|
function cachedHtml(file) {
|
|
21207
21475
|
let html = htmlCache.get(file);
|
|
21208
21476
|
if (!html) {
|
|
21209
|
-
html =
|
|
21477
|
+
html = readFileSync25(resolve27(process.cwd(), "public", file), "utf-8");
|
|
21210
21478
|
html = html.replace("<title>Maxy</title>", `<title>${escapeHtml(BRAND.productName)}</title>`);
|
|
21211
21479
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
21212
21480
|
const headInjection = file === "index.html" ? `${brandScript}
|
|
@@ -21225,12 +21493,12 @@ function loadBrandingCache(agentSlug) {
|
|
|
21225
21493
|
try {
|
|
21226
21494
|
const accountJsonPath = join12(configDir2, "account.json");
|
|
21227
21495
|
if (!existsSync23(accountJsonPath)) return null;
|
|
21228
|
-
const account = JSON.parse(
|
|
21496
|
+
const account = JSON.parse(readFileSync25(accountJsonPath, "utf-8"));
|
|
21229
21497
|
const accountId = account.accountId;
|
|
21230
21498
|
if (!accountId) return null;
|
|
21231
21499
|
const cachePath = join12(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
21232
21500
|
if (!existsSync23(cachePath)) return null;
|
|
21233
|
-
return JSON.parse(
|
|
21501
|
+
return JSON.parse(readFileSync25(cachePath, "utf-8"));
|
|
21234
21502
|
} catch {
|
|
21235
21503
|
return null;
|
|
21236
21504
|
}
|
|
@@ -21240,7 +21508,7 @@ function resolveDefaultSlug() {
|
|
|
21240
21508
|
const configDir2 = join12(homedir4(), BRAND.configDir);
|
|
21241
21509
|
const accountJsonPath = join12(configDir2, "account.json");
|
|
21242
21510
|
if (!existsSync23(accountJsonPath)) return null;
|
|
21243
|
-
const account = JSON.parse(
|
|
21511
|
+
const account = JSON.parse(readFileSync25(accountJsonPath, "utf-8"));
|
|
21244
21512
|
return account.defaultAgent || null;
|
|
21245
21513
|
} catch {
|
|
21246
21514
|
return null;
|
|
@@ -21313,7 +21581,7 @@ app35.use("/vnc-popout.html", logViewerFetch);
|
|
|
21313
21581
|
app35.get("/vnc-popout.html", (c) => {
|
|
21314
21582
|
let html = htmlCache.get("vnc-popout.html");
|
|
21315
21583
|
if (!html) {
|
|
21316
|
-
html =
|
|
21584
|
+
html = readFileSync25(resolve27(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
21317
21585
|
const name = escapeHtml(BRAND.productName);
|
|
21318
21586
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
21319
21587
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -21404,7 +21672,7 @@ try {
|
|
|
21404
21672
|
try {
|
|
21405
21673
|
let userId = "";
|
|
21406
21674
|
if (existsSync23(USERS_FILE)) {
|
|
21407
|
-
const users = JSON.parse(
|
|
21675
|
+
const users = JSON.parse(readFileSync25(USERS_FILE, "utf-8").trim() || "[]");
|
|
21408
21676
|
userId = users[0]?.userId ?? "";
|
|
21409
21677
|
}
|
|
21410
21678
|
await backfillNullUserIdConversations(userId);
|