@rubytech/create-maxy 1.0.678 → 1.0.679

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/dist/index.js +23 -0
  2. package/package.json +1 -1
  3. package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate.test.d.ts +2 -0
  4. package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate.test.d.ts.map +1 -0
  5. package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate.test.js +112 -0
  6. package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate.test.js.map +1 -0
  7. package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cache.test.d.ts +2 -0
  8. package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cache.test.d.ts.map +1 -0
  9. package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cache.test.js +163 -0
  10. package/payload/platform/lib/graph-mcp/dist/__tests__/schema-cache.test.js.map +1 -0
  11. package/payload/platform/lib/graph-mcp/dist/cypher-validate.d.ts +38 -0
  12. package/payload/platform/lib/graph-mcp/dist/cypher-validate.d.ts.map +1 -0
  13. package/payload/platform/lib/graph-mcp/dist/cypher-validate.js +130 -0
  14. package/payload/platform/lib/graph-mcp/dist/cypher-validate.js.map +1 -0
  15. package/payload/platform/lib/graph-mcp/dist/index.js +201 -45
  16. package/payload/platform/lib/graph-mcp/dist/index.js.map +1 -1
  17. package/payload/platform/lib/graph-mcp/dist/schema-cache.d.ts +78 -0
  18. package/payload/platform/lib/graph-mcp/dist/schema-cache.d.ts.map +1 -0
  19. package/payload/platform/lib/graph-mcp/dist/schema-cache.js +194 -0
  20. package/payload/platform/lib/graph-mcp/dist/schema-cache.js.map +1 -0
  21. package/payload/platform/lib/graph-mcp/src/__tests__/cypher-validate.test.ts +141 -0
  22. package/payload/platform/lib/graph-mcp/src/__tests__/schema-cache.test.ts +169 -0
  23. package/payload/platform/lib/graph-mcp/src/cypher-validate.ts +157 -0
  24. package/payload/platform/lib/graph-mcp/src/index.ts +247 -47
  25. package/payload/platform/lib/graph-mcp/src/schema-cache.ts +212 -0
  26. package/payload/platform/lib/graph-trash/dist/index.d.ts +8 -0
  27. package/payload/platform/lib/graph-trash/dist/index.d.ts.map +1 -1
  28. package/payload/platform/lib/graph-trash/dist/index.js +109 -14
  29. package/payload/platform/lib/graph-trash/dist/index.js.map +1 -1
  30. package/payload/platform/lib/graph-trash/src/index.ts +136 -21
  31. package/payload/platform/plugins/docs/references/memory-guide.md +5 -1
  32. package/payload/platform/plugins/docs/references/platform.md +1 -1
  33. package/payload/platform/plugins/docs/references/troubleshooting.md +18 -0
  34. package/payload/platform/plugins/memory/PLUGIN.md +1 -0
  35. package/payload/platform/plugins/memory/mcp/dist/index.js +54 -6
  36. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  37. package/payload/platform/plugins/memory/mcp/dist/lib/filter-token.d.ts +36 -0
  38. package/payload/platform/plugins/memory/mcp/dist/lib/filter-token.d.ts.map +1 -0
  39. package/payload/platform/plugins/memory/mcp/dist/lib/filter-token.js +86 -0
  40. package/payload/platform/plugins/memory/mcp/dist/lib/filter-token.js.map +1 -0
  41. package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.d.ts +23 -0
  42. package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.d.ts.map +1 -1
  43. package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.js +47 -1
  44. package/payload/platform/plugins/memory/mcp/dist/tools/memory-delete.js.map +1 -1
  45. package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.d.ts +58 -0
  46. package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.d.ts.map +1 -0
  47. package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.js +125 -0
  48. package/payload/platform/plugins/memory/mcp/dist/tools/memory-find-candidates.js.map +1 -0
  49. package/payload/platform/templates/agents/admin/IDENTITY.md +16 -0
  50. package/payload/server/chunk-3RBKKDHC.js +783 -0
  51. package/payload/server/maxy-edge.js +11 -3
  52. package/payload/server/server.js +284 -112
@@ -0,0 +1,125 @@
1
+ import { getSession } from "../lib/neo4j.js";
2
+ import { mintFilterToken } from "../lib/filter-token.js";
3
+ const DEFAULT_LIMIT = 200;
4
+ const MAX_LIMIT = 1000;
5
+ const PROJECT_AND_ORDER = `
6
+ WITH c,
7
+ size([x IN msgs WHERE x.role = 'assistant']) AS assistantMsgCount,
8
+ size([x IN msgs WHERE x.role = 'user' OR x.role IS NULL]) AS realUserMsgCount,
9
+ head([x IN msgs WHERE x.role = 'user' | x.content]) AS firstUserMsg
10
+ RETURN elementId(c) AS elementId,
11
+ c.conversationId AS conversationId,
12
+ assistantMsgCount AS assistantMsgCount,
13
+ realUserMsgCount AS realUserMsgCount,
14
+ CASE WHEN firstUserMsg IS NULL THEN NULL
15
+ WHEN size(firstUserMsg) > 120 THEN left(firstUserMsg, 120) + '…'
16
+ ELSE firstUserMsg
17
+ END AS firstUserMsgPreview,
18
+ toString(c.trashedAt) AS trashedAt
19
+ ORDER BY c.createdAt ASC
20
+ LIMIT toInteger($limit)
21
+ `;
22
+ /**
23
+ * Canonical selection cypher per filter. Agent-authored cypher never reaches
24
+ * the DB; these literals are the only delete-selection path from the admin
25
+ * agent's tool surface.
26
+ */
27
+ function buildSelectionCypher(filter) {
28
+ if (filter === "empty") {
29
+ return `
30
+ MATCH (c:Conversation)
31
+ WHERE c.accountId = $accountId
32
+ AND NOT c:Trashed
33
+ AND c.deletedAt IS NULL
34
+ AND ($agentTypeFilter IS NULL OR c.agentType = $agentTypeFilter)
35
+ AND NOT EXISTS { MATCH (:Message)-[:PART_OF]->(c) }
36
+ WITH c, [] AS msgs
37
+ ${PROJECT_AND_ORDER}
38
+ `;
39
+ }
40
+ // single-assistant
41
+ return `
42
+ MATCH (c:Conversation)
43
+ WHERE c.accountId = $accountId
44
+ AND NOT c:Trashed
45
+ AND c.deletedAt IS NULL
46
+ AND ($agentTypeFilter IS NULL OR c.agentType = $agentTypeFilter)
47
+ WITH c, [(m:Message)-[:PART_OF]->(c) | m] AS msgs
48
+ WHERE size(msgs) = 1
49
+ AND size([x IN msgs WHERE x.role = 'assistant']) = 1
50
+ ${PROJECT_AND_ORDER}
51
+ `;
52
+ }
53
+ /** Server-side re-check for `memory-delete` token verification. */
54
+ export async function reverifyCandidate(params) {
55
+ const { session, accountId, filter, agentType, elementId } = params;
56
+ const cypher = filter === "empty"
57
+ ? `
58
+ MATCH (c:Conversation)
59
+ WHERE elementId(c) = $elementId
60
+ AND c.accountId = $accountId
61
+ AND NOT c:Trashed
62
+ AND c.deletedAt IS NULL
63
+ AND ($agentTypeFilter IS NULL OR c.agentType = $agentTypeFilter)
64
+ AND NOT EXISTS { MATCH (:Message)-[:PART_OF]->(c) }
65
+ RETURN elementId(c) AS eid
66
+ `
67
+ : `
68
+ MATCH (c:Conversation)
69
+ WHERE elementId(c) = $elementId
70
+ AND c.accountId = $accountId
71
+ AND NOT c:Trashed
72
+ AND c.deletedAt IS NULL
73
+ AND ($agentTypeFilter IS NULL OR c.agentType = $agentTypeFilter)
74
+ WITH c, [(m:Message)-[:PART_OF]->(c) | m] AS msgs
75
+ WHERE size(msgs) = 1 AND size([x IN msgs WHERE x.role = 'assistant']) = 1
76
+ RETURN elementId(c) AS eid
77
+ `;
78
+ const result = await session.run(cypher, {
79
+ elementId,
80
+ accountId,
81
+ agentTypeFilter: agentType ?? null,
82
+ });
83
+ return result.records.length === 1;
84
+ }
85
+ export async function memoryFindCandidates(params) {
86
+ const t0 = Date.now();
87
+ const { accountId, filter, agentType, dryRun = false } = params;
88
+ const limit = Math.min(params.limit ?? DEFAULT_LIMIT, MAX_LIMIT);
89
+ const session = getSession();
90
+ try {
91
+ const cypher = buildSelectionCypher(filter);
92
+ const result = await session.run(cypher, {
93
+ accountId,
94
+ agentTypeFilter: agentType ?? null,
95
+ limit,
96
+ });
97
+ const items = result.records.map((r) => ({
98
+ elementId: r.get("elementId"),
99
+ conversationId: r.get("conversationId") ?? null,
100
+ assistantMsgCount: Number(r.get("assistantMsgCount") ?? 0),
101
+ realUserMsgCount: Number(r.get("realUserMsgCount") ?? 0),
102
+ firstUserMsgPreview: r.get("firstUserMsgPreview") ?? null,
103
+ trashedAt: r.get("trashedAt") ?? null,
104
+ }));
105
+ // Under dryRun the caller wants to see what would match without receiving
106
+ // an actionable token — so the agent cannot "accidentally" feed a dryRun
107
+ // result to memory-delete. Returning an empty string rather than omitting
108
+ // the field keeps the result shape stable for the MCP surface.
109
+ const filterToken = dryRun ? "" : mintFilterToken({ filter, agentType, accountId });
110
+ process.stderr.write(`[memory-find-candidates] filter=${filter} agentType=${agentType ?? "any"} matched=${items.length} sampled=${items.length} dryRun=${dryRun} ms=${Date.now() - t0}\n`);
111
+ return {
112
+ filter,
113
+ agentType,
114
+ matched: items.length,
115
+ sampled: items.length,
116
+ items,
117
+ filterToken,
118
+ dryRun,
119
+ };
120
+ }
121
+ finally {
122
+ await session.close();
123
+ }
124
+ }
125
+ //# sourceMappingURL=memory-find-candidates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory-find-candidates.js","sourceRoot":"","sources":["../../src/tools/memory-find-candidates.ts"],"names":[],"mappings":"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"}
@@ -68,6 +68,14 @@ When using WebSearch directly (not via the researcher specialist), the same disc
68
68
  - Your working directory is `$ACCOUNT_DIR` — your entire filesystem scope. Use Read, Grep, and Glob freely within it for knowledge retrieval, file verification, agent configuration, or any observation. Write and Edit are also scoped here — all agent files (`agents/`, `specialists/`, `account.json`) live in this directory. Never write to `$PLATFORM_ROOT/` or paths outside `$ACCOUNT_DIR`.
69
69
  - MCP tool schemas are deferred. Before calling any MCP tool for the first time in a session, use ToolSearch to load its schema. Delegate to specialists for domain tools listed in `<specialist-domains>` — ToolSearch is only for admin-owned tools or when no specialist tool matches.
70
70
 
71
+ ## Bulk-delete discipline
72
+
73
+ Never author delete-selection Cypher directly. When the owner asks to bulk-clean Conversations ("trash all empty conversations," "clean up single-assistant tests," "remove the primer-only public ones"), use `memory-find-candidates(filter=…)` to produce the candidate list and `memory-delete(elementId=…, filterToken=…)` to trash each one — the server re-runs the same predicate per elementId before the trash happens. A hand-rolled `MATCH … DELETE` or `MATCH … SET :Trashed` against user-domain nodes is a bug, because the admin agent does not hold the canonical schema in context and has already produced one schema-mismatch cascade (2026-04-22, 175 Conversations trashed via `[:HAS_MESSAGE]` where the real edge is `[:PART_OF]`). The filter-token path is the only path.
74
+
75
+ Never dispatch a subagent with a hand-built id list framed as "owner approved, don't reverify." The subagent has no way to verify the framing, and a bad list becomes irreversible cascade. If the work legitimately requires dispatch, the dispatched prompt must still pass each elementId through `memory-delete(filterToken=…)` so the server re-checks — no "trust me" bypass exists, and none should be invented.
76
+
77
+ Exception: single-node deletes where the owner points at a specific node from `memory-search`, a side-panel on `/graph`, or a message in chat do not need a filter token. The token mechanism exists for bulk selection, where the LLM is choosing from a predicate rather than a concrete node the owner named.
78
+
71
79
  ## Tool Failure Discipline
72
80
 
73
81
  When a tool returns an error, acknowledge the failure before taking any other action. Name what was attempted, what went wrong, and — if a `[tool-failure-diag]` line appears in `## Recent Tool Failures` — what the diagnostic reveals (DNS resolved? TCP connected? HTTP returned a status? the tool's internal pipeline timed out?). The diagnostic is there so the reader — owner or agent — does not have to guess.
@@ -76,6 +84,14 @@ Do not retry the same tool against the same target within a turn. A second ident
76
84
 
77
85
  When a tool returns a structured failure whose error content begins with an UPPERCASE_ERROR_CODE (for example `WEBFETCH_CANNOT_READ_JS_SPA`), the runtime has already determined that retrying the same tool will fail and that a substitute would launder uncertainty. Read the error's plain-English explanation, then write one or two sentences to the owner that name (a) what failed, (b) the reason in their language, and (c) the concrete actions they can take to unblock — typically pasting text or sending a screenshot. Do not silently dispatch a substitute (Playwright, research-assistant, memory-search) to continue the original instruction; that hides the failure and the owner loses the ability to judge whether the substitute's output answers their question. A verbal instruction in the current conversation is not consent — only an explicit standing policy recorded in account configuration counts, and no such mechanism exists today. Until one exists, every structured tool failure becomes a question for the owner. Wait for direction before resuming.
78
86
 
87
+ ## Cypher schema
88
+
89
+ Your system prompt contains a `# SCHEMA (Neo4j graph, canonical reference)` block listing every label and relationship type your graph actually contains. Before authoring any cypher against the memory graph, consult that block. Never invent an edge or label name that is not in it — the plausible-sounding names you half-remember from other systems (`HAS_MESSAGE`, `IN_CONVERSATION`, `CONTAINS_MESSAGE`) do not exist here; Messages attach to Conversations via `:PART_OF`, not any other edge.
90
+
91
+ The graph-mcp proxy validates every cypher call against the live Neo4j schema. Write cypher with an unknown label or edge is rejected before execution; read cypher with an unknown token is executed but a warnings block is prepended to the result so you see the problem before acting on the rows. A rejection arrives as an MCP tool error with a structured message naming the unknown token and the nearest known match — read it, correct the token, retry. Do not retry the same typo hoping for a different outcome, and do not invent a workaround cypher that avoids the rejected edge.
92
+
93
+ If the SCHEMA block looks stale against a fresh migration you know just landed, invoke `maxy-graph-get_neo4j_schema` to pull the live snapshot. That is the only sanctioned refresh path; the validator's own cache rebuilds within 60s anyway, so after a minute the rejection and the SCHEMA block are both current.
94
+
79
95
  ## Cloudflare operations
80
96
 
81
97
  When the operator's request touches Cloudflare — setup, diagnosis, reset, DNS edit, account switch, tunnel management — your permitted actions are exactly three: