@rubytech/create-realagent 1.0.678 → 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/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/server.js +284 -112
|
@@ -14,6 +14,14 @@
|
|
|
14
14
|
* are snapshotted into `_trashedKeys` (JSON) and nulled on the node, so
|
|
15
15
|
* MERGE against the same key won't collide. `restoreNode` writes them back
|
|
16
16
|
* and fails loudly when an active node already occupies the slot.
|
|
17
|
+
*
|
|
18
|
+
* Label-aware cascade (Task 655): when `trashNode` trashes a `:Conversation`,
|
|
19
|
+
* it also trashes every `(m:Message)-[:PART_OF]->(c)` Message in the same
|
|
20
|
+
* managed transaction. This makes `MATCH (m:Trashed)` a correct audit
|
|
21
|
+
* primitive again — pre-Task-655 a Conversation trash left attached Messages
|
|
22
|
+
* un-labelled, so audits that filtered `m:Trashed` silently under-reported
|
|
23
|
+
* the blast radius of the 2026-04-22 incident by a factor of 10+.
|
|
24
|
+
* `restoreNode` reverses both sides.
|
|
17
25
|
*/
|
|
18
26
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
27
|
exports.TRASH_METADATA_PROPS = void 0;
|
|
@@ -92,19 +100,72 @@ async function trashNode(params) {
|
|
|
92
100
|
.join(", ");
|
|
93
101
|
const setNullSuffix = setNullClauses ? `, ${setNullClauses}` : "";
|
|
94
102
|
const trashedAt = new Date().toISOString();
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
const isConversation = baseLabels.includes("Conversation");
|
|
104
|
+
// Message unique-key list computed once — matches UNIQUE_KEYS_BY_LABEL["Message"].
|
|
105
|
+
const messageUniqueKeys = UNIQUE_KEYS_BY_LABEL["Message"] ?? [];
|
|
106
|
+
// Root trash + Conversation→Message cascade in one managed transaction so a
|
|
107
|
+
// DB error mid-sweep can't leave a Conversation :Trashed with some Messages
|
|
108
|
+
// un-trashed (the 2026-04-22 audit correctness gap). For non-Conversation
|
|
109
|
+
// labels, the cascade branch is a no-op — same one-write shape as before.
|
|
110
|
+
let cascadedMessageCount = 0;
|
|
111
|
+
await session.executeWrite(async (tx) => {
|
|
112
|
+
await tx.run(`MATCH (n) WHERE elementId(n) = $eid
|
|
113
|
+
SET n:Trashed,
|
|
114
|
+
n.trashedAt = datetime($trashedAt),
|
|
115
|
+
n.trashedBy = $by,
|
|
116
|
+
n.trashReason = $reason,
|
|
117
|
+
n._trashedKeys = $trashedKeysJson${setNullSuffix}`, {
|
|
118
|
+
eid: elementId,
|
|
119
|
+
trashedAt,
|
|
120
|
+
by,
|
|
121
|
+
reason: reason ?? null,
|
|
122
|
+
trashedKeysJson: JSON.stringify(originalKeys),
|
|
123
|
+
});
|
|
124
|
+
if (isConversation) {
|
|
125
|
+
// Two-step cascade: collect Message elementIds and their unique-key
|
|
126
|
+
// values first, then apply :Trashed + snapshot + null in one SET per
|
|
127
|
+
// message using parameter-passed snapshot JSON. This keeps JSON
|
|
128
|
+
// construction in the Node process (where JSON.stringify is trivially
|
|
129
|
+
// correct) rather than building escaped JSON inside Cypher.
|
|
130
|
+
const collectKeys = messageUniqueKeys.length > 0
|
|
131
|
+
? messageUniqueKeys.map((k) => `\`${k}\`: m.\`${k}\``).join(", ")
|
|
132
|
+
: "";
|
|
133
|
+
const collectMsgProps = collectKeys ? `, {${collectKeys}}` : ", {}";
|
|
134
|
+
const collected = await tx.run(`MATCH (c) WHERE elementId(c) = $eid
|
|
135
|
+
MATCH (m:Message)-[:PART_OF]->(c)
|
|
136
|
+
WHERE NOT m:Trashed
|
|
137
|
+
RETURN elementId(m) AS meid${collectMsgProps} AS keys`, { eid: elementId });
|
|
138
|
+
for (const rec of collected.records) {
|
|
139
|
+
const meid = rec.get("meid");
|
|
140
|
+
const keys = rec.get("keys");
|
|
141
|
+
const liveKeys = {};
|
|
142
|
+
for (const k of messageUniqueKeys) {
|
|
143
|
+
if (keys[k] !== undefined && keys[k] !== null)
|
|
144
|
+
liveKeys[k] = keys[k];
|
|
145
|
+
}
|
|
146
|
+
const msgSetNulls = Object.keys(liveKeys).length > 0
|
|
147
|
+
? ", " + Object.keys(liveKeys).map((k) => `m.\`${k}\` = null`).join(", ")
|
|
148
|
+
: "";
|
|
149
|
+
await tx.run(`MATCH (m) WHERE elementId(m) = $meid
|
|
150
|
+
SET m:Trashed,
|
|
151
|
+
m.trashedAt = datetime($trashedAt),
|
|
152
|
+
m.trashedBy = $by,
|
|
153
|
+
m.trashReason = $reason,
|
|
154
|
+
m._trashedKeys = $trashedKeysJson${msgSetNulls}`, {
|
|
155
|
+
meid,
|
|
156
|
+
trashedAt,
|
|
157
|
+
by: `${by}:cascade-from-conversation`,
|
|
158
|
+
reason: reason ?? `cascade from Conversation ${elementId}`,
|
|
159
|
+
trashedKeysJson: JSON.stringify(liveKeys),
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
cascadedMessageCount = collected.records.length;
|
|
163
|
+
}
|
|
106
164
|
});
|
|
107
165
|
process.stderr.write(`[trash:marked] accountId=${accountId} elementId=${elementId} labels=${baseLabels.join(",")} by=${by} reason=${reason ?? "null"}\n`);
|
|
166
|
+
if (isConversation) {
|
|
167
|
+
process.stderr.write(`[trash:cascaded] accountId=${accountId} conversationElementId=${elementId} messageCount=${cascadedMessageCount} by=${by}\n`);
|
|
168
|
+
}
|
|
108
169
|
return {
|
|
109
170
|
trashed: true,
|
|
110
171
|
alreadyTrashed: false,
|
|
@@ -150,10 +211,44 @@ async function restoreNode(params) {
|
|
|
150
211
|
const setParams = { eid: elementId };
|
|
151
212
|
for (const [k, v] of Object.entries(originalKeys))
|
|
152
213
|
setParams[`val_${k}`] = v;
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
214
|
+
const isConversation = baseLabels.includes("Conversation");
|
|
215
|
+
let cascadedMessageCount = 0;
|
|
216
|
+
await session.executeWrite(async (tx) => {
|
|
217
|
+
await tx.run(`MATCH (n:Trashed) WHERE elementId(n) = $eid
|
|
218
|
+
REMOVE n:Trashed, n.trashedAt, n.trashedBy, n.trashReason, n._trashedKeys
|
|
219
|
+
SET n.restoredAt = datetime()${setSuffix}`, setParams);
|
|
220
|
+
if (isConversation) {
|
|
221
|
+
// Reverse of the trashNode cascade: re-label and restore unique-key
|
|
222
|
+
// snapshots for every `(m:Message)-[:PART_OF]->(c)` that was trashed
|
|
223
|
+
// by the same cascade chain (i.e. m._trashedKeys exists + m was
|
|
224
|
+
// trashed by a `*:cascade-from-conversation` provenance). We scope by
|
|
225
|
+
// the second condition so a user-initiated single-Message trash that
|
|
226
|
+
// happens to sit inside this Conversation isn't accidentally undone.
|
|
227
|
+
const collected = await tx.run(`MATCH (c) WHERE elementId(c) = $eid
|
|
228
|
+
MATCH (m:Trashed:Message)-[:PART_OF]->(c)
|
|
229
|
+
WHERE m.trashedBy ENDS WITH ':cascade-from-conversation'
|
|
230
|
+
RETURN elementId(m) AS meid, m._trashedKeys AS keysJson`, { eid: elementId });
|
|
231
|
+
for (const rec of collected.records) {
|
|
232
|
+
const meid = rec.get("meid");
|
|
233
|
+
const keysJson = rec.get("keysJson");
|
|
234
|
+
const keys = keysJson ? JSON.parse(keysJson) : {};
|
|
235
|
+
const setClause = Object.keys(keys).length > 0
|
|
236
|
+
? ", " + Object.keys(keys).map((k) => `m.\`${k}\` = $val_${k}`).join(", ")
|
|
237
|
+
: "";
|
|
238
|
+
const msgParams = { meid };
|
|
239
|
+
for (const [k, v] of Object.entries(keys))
|
|
240
|
+
msgParams[`val_${k}`] = v;
|
|
241
|
+
await tx.run(`MATCH (m) WHERE elementId(m) = $meid
|
|
242
|
+
REMOVE m:Trashed, m.trashedAt, m.trashedBy, m.trashReason, m._trashedKeys
|
|
243
|
+
SET m.restoredAt = datetime()${setClause}`, msgParams);
|
|
244
|
+
}
|
|
245
|
+
cascadedMessageCount = collected.records.length;
|
|
246
|
+
}
|
|
247
|
+
});
|
|
156
248
|
process.stderr.write(`[trash:restored] accountId=${accountId} elementId=${elementId} labels=${baseLabels.join(",")}\n`);
|
|
249
|
+
if (isConversation) {
|
|
250
|
+
process.stderr.write(`[trash:cascade-restored] accountId=${accountId} conversationElementId=${elementId} messageCount=${cascadedMessageCount}\n`);
|
|
251
|
+
}
|
|
157
252
|
return {
|
|
158
253
|
restored: true,
|
|
159
254
|
nodeId: elementId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,6 +13,14 @@
|
|
|
13
13
|
* are snapshotted into `_trashedKeys` (JSON) and nulled on the node, so
|
|
14
14
|
* MERGE against the same key won't collide. `restoreNode` writes them back
|
|
15
15
|
* and fails loudly when an active node already occupies the slot.
|
|
16
|
+
*
|
|
17
|
+
* Label-aware cascade (Task 655): when `trashNode` trashes a `:Conversation`,
|
|
18
|
+
* it also trashes every `(m:Message)-[:PART_OF]->(c)` Message in the same
|
|
19
|
+
* managed transaction. This makes `MATCH (m:Trashed)` a correct audit
|
|
20
|
+
* primitive again — pre-Task-655 a Conversation trash left attached Messages
|
|
21
|
+
* un-labelled, so audits that filtered `m:Trashed` silently under-reported
|
|
22
|
+
* the blast radius of the 2026-04-22 incident by a factor of 10+.
|
|
23
|
+
* `restoreNode` reverses both sides.
|
|
16
24
|
*/
|
|
17
25
|
|
|
18
26
|
import type { Session } from "neo4j-driver";
|
|
@@ -118,25 +126,89 @@ export async function trashNode(params: TrashParams): Promise<TrashResult> {
|
|
|
118
126
|
const setNullSuffix = setNullClauses ? `, ${setNullClauses}` : "";
|
|
119
127
|
|
|
120
128
|
const trashedAt = new Date().toISOString();
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
129
|
+
const isConversation = baseLabels.includes("Conversation");
|
|
130
|
+
// Message unique-key list computed once — matches UNIQUE_KEYS_BY_LABEL["Message"].
|
|
131
|
+
const messageUniqueKeys = UNIQUE_KEYS_BY_LABEL["Message"] ?? [];
|
|
132
|
+
|
|
133
|
+
// Root trash + Conversation→Message cascade in one managed transaction so a
|
|
134
|
+
// DB error mid-sweep can't leave a Conversation :Trashed with some Messages
|
|
135
|
+
// un-trashed (the 2026-04-22 audit correctness gap). For non-Conversation
|
|
136
|
+
// labels, the cascade branch is a no-op — same one-write shape as before.
|
|
137
|
+
let cascadedMessageCount = 0;
|
|
138
|
+
await session.executeWrite(async (tx) => {
|
|
139
|
+
await tx.run(
|
|
140
|
+
`MATCH (n) WHERE elementId(n) = $eid
|
|
141
|
+
SET n:Trashed,
|
|
142
|
+
n.trashedAt = datetime($trashedAt),
|
|
143
|
+
n.trashedBy = $by,
|
|
144
|
+
n.trashReason = $reason,
|
|
145
|
+
n._trashedKeys = $trashedKeysJson${setNullSuffix}`,
|
|
146
|
+
{
|
|
147
|
+
eid: elementId,
|
|
148
|
+
trashedAt,
|
|
149
|
+
by,
|
|
150
|
+
reason: reason ?? null,
|
|
151
|
+
trashedKeysJson: JSON.stringify(originalKeys),
|
|
152
|
+
},
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
if (isConversation) {
|
|
156
|
+
// Two-step cascade: collect Message elementIds and their unique-key
|
|
157
|
+
// values first, then apply :Trashed + snapshot + null in one SET per
|
|
158
|
+
// message using parameter-passed snapshot JSON. This keeps JSON
|
|
159
|
+
// construction in the Node process (where JSON.stringify is trivially
|
|
160
|
+
// correct) rather than building escaped JSON inside Cypher.
|
|
161
|
+
const collectKeys = messageUniqueKeys.length > 0
|
|
162
|
+
? messageUniqueKeys.map((k) => `\`${k}\`: m.\`${k}\``).join(", ")
|
|
163
|
+
: "";
|
|
164
|
+
const collectMsgProps = collectKeys ? `, {${collectKeys}}` : ", {}";
|
|
165
|
+
|
|
166
|
+
const collected = await tx.run(
|
|
167
|
+
`MATCH (c) WHERE elementId(c) = $eid
|
|
168
|
+
MATCH (m:Message)-[:PART_OF]->(c)
|
|
169
|
+
WHERE NOT m:Trashed
|
|
170
|
+
RETURN elementId(m) AS meid${collectMsgProps} AS keys`,
|
|
171
|
+
{ eid: elementId },
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
for (const rec of collected.records) {
|
|
175
|
+
const meid = rec.get("meid") as string;
|
|
176
|
+
const keys = rec.get("keys") as Record<string, unknown>;
|
|
177
|
+
const liveKeys: Record<string, unknown> = {};
|
|
178
|
+
for (const k of messageUniqueKeys) {
|
|
179
|
+
if (keys[k] !== undefined && keys[k] !== null) liveKeys[k] = keys[k];
|
|
180
|
+
}
|
|
181
|
+
const msgSetNulls = Object.keys(liveKeys).length > 0
|
|
182
|
+
? ", " + Object.keys(liveKeys).map((k) => `m.\`${k}\` = null`).join(", ")
|
|
183
|
+
: "";
|
|
184
|
+
await tx.run(
|
|
185
|
+
`MATCH (m) WHERE elementId(m) = $meid
|
|
186
|
+
SET m:Trashed,
|
|
187
|
+
m.trashedAt = datetime($trashedAt),
|
|
188
|
+
m.trashedBy = $by,
|
|
189
|
+
m.trashReason = $reason,
|
|
190
|
+
m._trashedKeys = $trashedKeysJson${msgSetNulls}`,
|
|
191
|
+
{
|
|
192
|
+
meid,
|
|
193
|
+
trashedAt,
|
|
194
|
+
by: `${by}:cascade-from-conversation`,
|
|
195
|
+
reason: reason ?? `cascade from Conversation ${elementId}`,
|
|
196
|
+
trashedKeysJson: JSON.stringify(liveKeys),
|
|
197
|
+
},
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
cascadedMessageCount = collected.records.length;
|
|
201
|
+
}
|
|
202
|
+
});
|
|
136
203
|
|
|
137
204
|
process.stderr.write(
|
|
138
205
|
`[trash:marked] accountId=${accountId} elementId=${elementId} labels=${baseLabels.join(",")} by=${by} reason=${reason ?? "null"}\n`,
|
|
139
206
|
);
|
|
207
|
+
if (isConversation) {
|
|
208
|
+
process.stderr.write(
|
|
209
|
+
`[trash:cascaded] accountId=${accountId} conversationElementId=${elementId} messageCount=${cascadedMessageCount} by=${by}\n`,
|
|
210
|
+
);
|
|
211
|
+
}
|
|
140
212
|
|
|
141
213
|
return {
|
|
142
214
|
trashed: true,
|
|
@@ -210,16 +282,59 @@ export async function restoreNode(params: RestoreParams): Promise<RestoreResult>
|
|
|
210
282
|
const setParams: Record<string, unknown> = { eid: elementId };
|
|
211
283
|
for (const [k, v] of Object.entries(originalKeys)) setParams[`val_${k}`] = v;
|
|
212
284
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
285
|
+
const isConversation = baseLabels.includes("Conversation");
|
|
286
|
+
let cascadedMessageCount = 0;
|
|
287
|
+
|
|
288
|
+
await session.executeWrite(async (tx) => {
|
|
289
|
+
await tx.run(
|
|
290
|
+
`MATCH (n:Trashed) WHERE elementId(n) = $eid
|
|
291
|
+
REMOVE n:Trashed, n.trashedAt, n.trashedBy, n.trashReason, n._trashedKeys
|
|
292
|
+
SET n.restoredAt = datetime()${setSuffix}`,
|
|
293
|
+
setParams,
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
if (isConversation) {
|
|
297
|
+
// Reverse of the trashNode cascade: re-label and restore unique-key
|
|
298
|
+
// snapshots for every `(m:Message)-[:PART_OF]->(c)` that was trashed
|
|
299
|
+
// by the same cascade chain (i.e. m._trashedKeys exists + m was
|
|
300
|
+
// trashed by a `*:cascade-from-conversation` provenance). We scope by
|
|
301
|
+
// the second condition so a user-initiated single-Message trash that
|
|
302
|
+
// happens to sit inside this Conversation isn't accidentally undone.
|
|
303
|
+
const collected = await tx.run(
|
|
304
|
+
`MATCH (c) WHERE elementId(c) = $eid
|
|
305
|
+
MATCH (m:Trashed:Message)-[:PART_OF]->(c)
|
|
306
|
+
WHERE m.trashedBy ENDS WITH ':cascade-from-conversation'
|
|
307
|
+
RETURN elementId(m) AS meid, m._trashedKeys AS keysJson`,
|
|
308
|
+
{ eid: elementId },
|
|
309
|
+
);
|
|
310
|
+
for (const rec of collected.records) {
|
|
311
|
+
const meid = rec.get("meid") as string;
|
|
312
|
+
const keysJson = rec.get("keysJson") as string | null;
|
|
313
|
+
const keys: Record<string, unknown> = keysJson ? JSON.parse(keysJson) : {};
|
|
314
|
+
const setClause = Object.keys(keys).length > 0
|
|
315
|
+
? ", " + Object.keys(keys).map((k) => `m.\`${k}\` = $val_${k}`).join(", ")
|
|
316
|
+
: "";
|
|
317
|
+
const msgParams: Record<string, unknown> = { meid };
|
|
318
|
+
for (const [k, v] of Object.entries(keys)) msgParams[`val_${k}`] = v;
|
|
319
|
+
await tx.run(
|
|
320
|
+
`MATCH (m) WHERE elementId(m) = $meid
|
|
321
|
+
REMOVE m:Trashed, m.trashedAt, m.trashedBy, m.trashReason, m._trashedKeys
|
|
322
|
+
SET m.restoredAt = datetime()${setClause}`,
|
|
323
|
+
msgParams,
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
cascadedMessageCount = collected.records.length;
|
|
327
|
+
}
|
|
328
|
+
});
|
|
219
329
|
|
|
220
330
|
process.stderr.write(
|
|
221
331
|
`[trash:restored] accountId=${accountId} elementId=${elementId} labels=${baseLabels.join(",")}\n`,
|
|
222
332
|
);
|
|
333
|
+
if (isConversation) {
|
|
334
|
+
process.stderr.write(
|
|
335
|
+
`[trash:cascade-restored] accountId=${accountId} conversationElementId=${elementId} messageCount=${cascadedMessageCount}\n`,
|
|
336
|
+
);
|
|
337
|
+
}
|
|
223
338
|
|
|
224
339
|
return {
|
|
225
340
|
restored: true,
|
|
@@ -90,10 +90,14 @@ Conversations and Messages carry role/channel sublabels so you can read the chat
|
|
|
90
90
|
|
|
91
91
|
**Save a default view:** once you have the rows you want, click **Set default view** in the filter menu. Next time you open **Graph**, those rows are pre-selected and your data renders immediately. The default is per-admin, per-account — each admin on each account has their own.
|
|
92
92
|
|
|
93
|
-
**Delete a node:** drag it to the trash icon top-right of the canvas. No confirmation — deletes are reversible for 30 days. To restore, toggle **Show trashed** inside the filter menu and click **Restore** on the node, or ask Maxy in chat ("restore the <label> I just deleted").
|
|
93
|
+
**Delete a node:** drag it to the trash icon top-right of the canvas. No confirmation — deletes are reversible for 30 days. To restore, toggle **Show trashed** inside the filter menu and click **Restore** on the node, or ask Maxy in chat ("restore the <label> I just deleted"). Deleting a conversation also trashes its messages in the same step, so they reappear together on restore.
|
|
94
|
+
|
|
95
|
+
**Bulk cleanup of conversations in chat:** when you ask Maxy to clean up conversations in bulk ("trash all empty conversations," "clean up the single-assistant tests"), the agent uses a deterministic selector with a fixed set of filter names — it cannot author custom delete queries. The server re-runs the same filter on every candidate before it trashes, so a stale list can't destroy something the filter wouldn't match now. If the filter matches nothing, Maxy reports "no candidates" and nothing happens.
|
|
94
96
|
|
|
95
97
|
The page reads only your own brand's Neo4j — a Maxy device and a Real Agent device share no graph state even when on the same laptop. No credentials are required; the view inherits your admin session.
|
|
96
98
|
|
|
99
|
+
**Typo-proof cypher.** When Maxy runs direct Cypher to answer a relational question, the query is checked against your Neo4j's live label and relationship-type taxonomy before it executes. Cypher that references an unknown name (an edge or label that does not exist in your graph) is rejected for writes and flagged with a warnings header for reads, so Maxy never silently acts on a query that targeted the wrong set of nodes. You should not see this — it runs invisibly — but it is the safety net that stops a fabricated edge name from producing "empty" results that are really just unreachable. Before acting on a bulk operation Maxy surfaces the result count and a sample; if it ever describes a cypher rejection, that means its first attempt was malformed and it corrected itself.
|
|
100
|
+
|
|
97
101
|
## Privacy
|
|
98
102
|
|
|
99
103
|
All memory is stored on your local Raspberry Pi. The Neo4j database never leaves your network. Maxy does not sync memory to any cloud service or third party.
|
|
@@ -52,7 +52,7 @@ The memory graph is stored on your Pi. It never leaves your network.
|
|
|
52
52
|
|
|
53
53
|
## The Web Interface
|
|
54
54
|
|
|
55
|
-
The web app runs on your Pi on port 19200. A small always-on front door (`maxy-edge`) owns that port and the remote terminal transport — so when the Software Update command restarts the app server, the browser-side terminal keeps streaming bytes exactly like an SSH session would. It provides:
|
|
55
|
+
The web app runs on your Pi on port 19200. A small always-on front door (`maxy-edge`) owns that port and the remote terminal transport — so when the Software Update command restarts the app server, the browser-side terminal keeps streaming bytes exactly like an SSH session would. Login cookies are HMAC-signed with a shared key on disk, so both processes recognise the same session without any coordination and you do not have to log in again after an update. It provides:
|
|
56
56
|
|
|
57
57
|
- **Admin chat** (at `/`) — your primary interface, PIN-protected
|
|
58
58
|
- **Public chat** (at `/{agent-name}`) — visitor-facing agents, each with their own URL. On public hostnames, the root path serves the default agent.
|
|
@@ -133,6 +133,24 @@ DISPLAY=:99 xdpyinfo >/dev/null 2>&1 && echo "display :99 ok" || echo "display d
|
|
|
133
133
|
|
|
134
134
|
---
|
|
135
135
|
|
|
136
|
+
## Upgrade terminal stays black after clicking Upgrade (VNC WebSocket rejected)
|
|
137
|
+
|
|
138
|
+
**Symptom:** The launch-upgrade POST returns 200 (the server-side spawn succeeded), but the browser's VNC iframe shows a black rectangle and the DevTools console spins on `WebSocket connection to '/websockify' failed` / `Connection closed (code: 1006)` every few seconds.
|
|
139
|
+
|
|
140
|
+
**What it means:** The browser has a session cookie the `/websockify` auth gate refuses to accept. Since Task 653, every rejection logs three fields that identify the failing layer:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
sudo tail -200 ~/.maxy/logs/vnc-boot.log | grep 'decision="rejected"'
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
- `cookieHeaderPresent=false` → the browser never sent a cookie. Check the iframe context / SameSite policy / cookie domain. Most commonly: the user opened admin over HTTP instead of HTTPS, or the cookie is scoped to a different subdomain.
|
|
147
|
+
- `cookieHeaderPresent=true tokenPresent=false` → the cookie is present but the server rejects its signature. This is either tampering or a corrupted/missing shared secret. Check that `~/.maxy/credentials/remote-session-secret` exists on disk (mode `0600`, 64 hex chars) and has not been recently overwritten.
|
|
148
|
+
- `tokenExpired=true` → the 24h session TTL elapsed. Log in again.
|
|
149
|
+
|
|
150
|
+
If the secret file is missing entirely, re-run `npx -y @rubytech/create-maxy@latest` — the installer provisions it idempotently, preserving an existing file on upgrade and creating a fresh one on first install.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
136
154
|
## Upgrade terminal opens but npx never runs
|
|
137
155
|
|
|
138
156
|
**Symptom:** You clicked **Upgrade**, the VNC overlay opened with a visible shell prompt, but `npx -y @rubytech/create-maxy@latest` does not execute and the shell is idle.
|
|
@@ -14,6 +14,7 @@ import { memoryIngest } from "./tools/memory-ingest.js";
|
|
|
14
14
|
import { memoryIngestWeb } from "./tools/memory-ingest-web.js";
|
|
15
15
|
import { memoryUpdate } from "./tools/memory-update.js";
|
|
16
16
|
import { memoryDelete } from "./tools/memory-delete.js";
|
|
17
|
+
import { memoryFindCandidates } from "./tools/memory-find-candidates.js";
|
|
17
18
|
import { memoryRestore } from "./tools/memory-restore.js";
|
|
18
19
|
import { memoryEmptyTrash } from "./tools/memory-empty-trash.js";
|
|
19
20
|
import { memoryListAttachments } from "./tools/memory-list-attachments.js";
|
|
@@ -636,16 +637,63 @@ if (!readOnly) {
|
|
|
636
637
|
};
|
|
637
638
|
}
|
|
638
639
|
});
|
|
640
|
+
server.tool("memory-find-candidates", "Deterministic bulk-delete selector for Conversation nodes. Returns a structured list of candidates " +
|
|
641
|
+
"matching a fixed-name filter ('empty' = zero :PART_OF messages; 'single-assistant' = exactly one " +
|
|
642
|
+
"assistant-role message) and an opaque filterToken. Pair with memory-delete(filterToken=…) — the server " +
|
|
643
|
+
"re-runs the same predicate per elementId before trashing, so a stale or forged token never deletes. " +
|
|
644
|
+
"Never author custom delete-selection Cypher for bulk flows; always use this tool.", {
|
|
645
|
+
filter: z.enum(["empty", "single-assistant"]).describe("Selection predicate. 'empty' = no :PART_OF Messages. 'single-assistant' = exactly one :PART_OF " +
|
|
646
|
+
"Message with role='assistant'. Content-matching filter ('no-real-user-input') is deferred to Task 656."),
|
|
647
|
+
agentType: z.enum(["admin", "public"]).optional().describe("Scope to conversations matching c.agentType. Omit to scope across both."),
|
|
648
|
+
limit: z.number().int().positive().optional().describe("Max candidates to return (default 200, hard cap 1000)."),
|
|
649
|
+
dryRun: z.boolean().optional().describe("Preview matches without minting an actionable filterToken. Candidates are listed in the response, but no delete can be performed against them — caller must re-run without dryRun to receive a token."),
|
|
650
|
+
}, async ({ filter, agentType, limit, dryRun }) => {
|
|
651
|
+
try {
|
|
652
|
+
const result = await memoryFindCandidates({ accountId, filter, agentType, limit, dryRun });
|
|
653
|
+
if (result.items.length === 0) {
|
|
654
|
+
return { content: [{ type: "text", text: `No candidates matched filter=${filter} agentType=${agentType ?? "any"}. No delete will happen.` }] };
|
|
655
|
+
}
|
|
656
|
+
const tokenLine = result.dryRun
|
|
657
|
+
? "filterToken=(none — dryRun; re-run without dryRun to get an actionable token)"
|
|
658
|
+
: `filterToken=${result.filterToken}`;
|
|
659
|
+
const trailer = result.dryRun
|
|
660
|
+
? "This is a dryRun preview. No delete can be performed until you re-run without dryRun."
|
|
661
|
+
: "Pass each elementId to memory-delete with the filterToken above. The server re-verifies on every call.";
|
|
662
|
+
const lines = [
|
|
663
|
+
`Matched ${result.matched} candidate(s) for filter=${filter} agentType=${agentType ?? "any"}.`,
|
|
664
|
+
tokenLine,
|
|
665
|
+
"",
|
|
666
|
+
trailer,
|
|
667
|
+
"",
|
|
668
|
+
...result.items.map((i) => {
|
|
669
|
+
const preview = i.firstUserMsgPreview ? ` userPreview=${JSON.stringify(i.firstUserMsgPreview)}` : "";
|
|
670
|
+
return `- elementId=${i.elementId} conversationId=${i.conversationId ?? "null"} assistantMsgs=${i.assistantMsgCount} realUserMsgs=${i.realUserMsgCount}${preview}`;
|
|
671
|
+
}),
|
|
672
|
+
];
|
|
673
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
674
|
+
}
|
|
675
|
+
catch (err) {
|
|
676
|
+
return {
|
|
677
|
+
content: [{ type: "text", text: `memory-find-candidates failed: ${err instanceof Error ? err.message : String(err)}` }],
|
|
678
|
+
isError: true,
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
});
|
|
639
682
|
server.tool("memory-delete", "Soft-delete a single graph node by elementId. Adds the :Trashed label, preserves relationships, " +
|
|
640
683
|
"and excludes the node from memory-search and any read filtered via notTrashed. For KnowledgeDocument, " +
|
|
641
|
-
"cascades to linked Section and Chunk children.
|
|
642
|
-
"after the grace period (default 30 days).
|
|
643
|
-
"
|
|
644
|
-
|
|
684
|
+
"cascades to linked Section and Chunk children. For Conversation, cascades to attached :PART_OF Messages " +
|
|
685
|
+
"(Task 655). Hard removal happens later via memory-empty-trash after the grace period (default 30 days). " +
|
|
686
|
+
"For bulk-delete flows, pass filterToken from memory-find-candidates — the server re-verifies the predicate " +
|
|
687
|
+
"before trashing. For GDPR Right-to-Erasure, use contact-erase instead (bypasses this primitive and " +
|
|
688
|
+
"hard-deletes per Article 17).", {
|
|
689
|
+
elementId: z.string().describe("Neo4j elementId of the node to trash (from memory-search or memory-find-candidates)."),
|
|
645
690
|
reason: z.string().optional().describe("Optional human-readable explanation; persisted as trashReason for audit."),
|
|
646
|
-
|
|
691
|
+
filterToken: z.string().optional().describe("Opaque token from memory-find-candidates. Required for bulk-selector flows; the server re-runs the " +
|
|
692
|
+
"canonical predicate on this elementId before trashing. Omit for single-node deletes where the caller " +
|
|
693
|
+
"chose the target directly (e.g. from a memory-search result)."),
|
|
694
|
+
}, async ({ elementId, reason, filterToken }) => {
|
|
647
695
|
try {
|
|
648
|
-
const result = await memoryDelete({ accountId, elementId, reason });
|
|
696
|
+
const result = await memoryDelete({ accountId, elementId, reason, filterToken });
|
|
649
697
|
const head = result.alreadyTrashed
|
|
650
698
|
? `Already trashed: [${result.labels.join(", ")}] elementId=${elementId}`
|
|
651
699
|
: `Trashed: [${result.labels.join(", ")}] elementId=${elementId}`;
|