@rubytech/create-realagent 1.0.678 → 1.0.681
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 +232 -39
- 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/deployment.md +4 -2
- 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 +20 -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/scripts/vnc.sh +12 -409
- package/payload/platform/templates/agents/admin/IDENTITY.md +16 -0
- package/payload/platform/templates/dotfiles/.tmux.conf +1 -0
- package/payload/platform/templates/systemd/maxy-ttyd.service +25 -0
- package/payload/server/chunk-3RBKKDHC.js +783 -0
- package/payload/server/maxy-edge.js +377 -8
- package/payload/server/public/assets/admin-CIkyOur7.js +362 -0
- package/payload/server/public/assets/admin-kHJ-D0s7.css +1 -0
- package/payload/server/public/index.html +2 -1
- package/payload/server/server.js +391 -412
- package/payload/server/public/assets/admin-BBL1no_g.js +0 -352
|
@@ -1,9 +1,51 @@
|
|
|
1
1
|
import { getSession } from "../lib/neo4j.js";
|
|
2
2
|
import { trashNode } from "../../../../../lib/graph-trash/dist/index.js";
|
|
3
|
+
import { verifyFilterToken, filterTokenFingerprint } from "../lib/filter-token.js";
|
|
4
|
+
import { reverifyCandidate } from "./memory-find-candidates.js";
|
|
5
|
+
export class FilterTokenError extends Error {
|
|
6
|
+
reason;
|
|
7
|
+
constructor(message, reason) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.reason = reason;
|
|
10
|
+
this.name = "FilterTokenError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
3
13
|
export async function memoryDelete(params) {
|
|
4
|
-
const
|
|
14
|
+
const t0 = Date.now();
|
|
15
|
+
const { accountId, elementId, reason, filterToken } = params;
|
|
5
16
|
const session = getSession();
|
|
6
17
|
try {
|
|
18
|
+
// Filter-token re-verification (Task 655). Must run before trashNode so a
|
|
19
|
+
// rejection never mutates the graph. Observable outcome: the
|
|
20
|
+
// `[memory-delete]` log line carries `reverified=true|false` and
|
|
21
|
+
// `outcome=trashed|rejected` — never silent.
|
|
22
|
+
let reverified = false;
|
|
23
|
+
if (filterToken) {
|
|
24
|
+
const verify = verifyFilterToken(filterToken);
|
|
25
|
+
if (!verify.ok) {
|
|
26
|
+
const ms = Date.now() - t0;
|
|
27
|
+
process.stderr.write(`[memory-delete] elementId=${elementId} filterToken=${filterTokenFingerprint(filterToken)} reverified=false outcome=rejected reason=${verify.reason} ms=${ms}\n`);
|
|
28
|
+
throw new FilterTokenError(`memory-delete: filter token rejected (${verify.reason})`, verify.reason);
|
|
29
|
+
}
|
|
30
|
+
if (verify.payload.accountId !== accountId) {
|
|
31
|
+
const ms = Date.now() - t0;
|
|
32
|
+
process.stderr.write(`[memory-delete] elementId=${elementId} filterToken=${filterTokenFingerprint(filterToken)} reverified=false outcome=rejected reason=account-mismatch ms=${ms}\n`);
|
|
33
|
+
throw new FilterTokenError(`memory-delete: filter token account mismatch`, "mismatch");
|
|
34
|
+
}
|
|
35
|
+
const stillMatches = await reverifyCandidate({
|
|
36
|
+
session,
|
|
37
|
+
accountId,
|
|
38
|
+
filter: verify.payload.filter,
|
|
39
|
+
agentType: verify.payload.agentType,
|
|
40
|
+
elementId,
|
|
41
|
+
});
|
|
42
|
+
if (!stillMatches) {
|
|
43
|
+
const ms = Date.now() - t0;
|
|
44
|
+
process.stderr.write(`[memory-delete] elementId=${elementId} filterToken=${filterTokenFingerprint(filterToken)} reverified=false outcome=rejected reason=predicate-no-longer-matches ms=${ms}\n`);
|
|
45
|
+
throw new FilterTokenError(`memory-delete: target ${elementId} no longer matches filter '${verify.payload.filter}'`, "mismatch");
|
|
46
|
+
}
|
|
47
|
+
reverified = true;
|
|
48
|
+
}
|
|
7
49
|
const root = await trashNode({
|
|
8
50
|
session,
|
|
9
51
|
accountId,
|
|
@@ -45,6 +87,10 @@ export async function memoryDelete(params) {
|
|
|
45
87
|
chunks++;
|
|
46
88
|
}
|
|
47
89
|
}
|
|
90
|
+
const ms = Date.now() - t0;
|
|
91
|
+
const outcome = root.alreadyTrashed ? "already-trashed" : "trashed";
|
|
92
|
+
const tokenFp = filterToken ? filterTokenFingerprint(filterToken) : "none";
|
|
93
|
+
process.stderr.write(`[memory-delete] elementId=${elementId} filterToken=${tokenFp} reverified=${reverified} outcome=${outcome} ms=${ms}\n`);
|
|
48
94
|
return {
|
|
49
95
|
elementId,
|
|
50
96
|
labels: root.labels,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-delete.js","sourceRoot":"","sources":["../../src/tools/memory-delete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAoB,MAAM,8CAA8C,CAAC;
|
|
1
|
+
{"version":3,"file":"memory-delete.js","sourceRoot":"","sources":["../../src/tools/memory-delete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAoB,MAAM,8CAA8C,CAAC;AAC3F,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACnF,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAqDhE,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAGvB;IAFlB,YACE,OAAe,EACC,MAA8D;QAE9E,KAAK,CAAC,OAAO,CAAC,CAAC;QAFC,WAAM,GAAN,MAAM,CAAwD;QAG9E,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAA0B;IAE1B,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACtB,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;IAC7D,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAE7B,IAAI,CAAC;QACH,0EAA0E;QAC1E,6DAA6D;QAC7D,iEAAiE;QACjE,6CAA6C;QAC7C,IAAI,UAAU,GAAG,KAAK,CAAC;QACvB,IAAI,WAAW,EAAE,CAAC;YAChB,MAAM,MAAM,GAAG,iBAAiB,CAAC,WAAW,CAAC,CAAC;YAC9C,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;gBACf,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC;gBAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,6BAA6B,SAAS,gBAAgB,sBAAsB,CAAC,WAAW,CAAC,6CAA6C,MAAM,CAAC,MAAM,OAAO,EAAE,IAAI,CACjK,CAAC;gBACF,MAAM,IAAI,gBAAgB,CACxB,yCAAyC,MAAM,CAAC,MAAM,GAAG,EACzD,MAAM,CAAC,MAAM,CACd,CAAC;YACJ,CAAC;YACD,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;gBAC3C,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC;gBAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,6BAA6B,SAAS,gBAAgB,sBAAsB,CAAC,WAAW,CAAC,iEAAiE,EAAE,IAAI,CACjK,CAAC;gBACF,MAAM,IAAI,gBAAgB,CACxB,8CAA8C,EAC9C,UAAU,CACX,CAAC;YACJ,CAAC;YACD,MAAM,YAAY,GAAG,MAAM,iBAAiB,CAAC;gBAC3C,OAAO;gBACP,SAAS;gBACT,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,MAAM;gBAC7B,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,SAAS;gBACnC,SAAS;aACV,CAAC,CAAC;YACH,IAAI,CAAC,YAAY,EAAE,CAAC;gBAClB,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC;gBAC3B,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,6BAA6B,SAAS,gBAAgB,sBAAsB,CAAC,WAAW,CAAC,4EAA4E,EAAE,IAAI,CAC5K,CAAC;gBACF,MAAM,IAAI,gBAAgB,CACxB,yBAAyB,SAAS,8BAA8B,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,EACxF,UAAU,CACX,CAAC;YACJ,CAAC;YACD,UAAU,GAAG,IAAI,CAAC;QACpB,CAAC;QAED,MAAM,IAAI,GAAgB,MAAM,SAAS,CAAC;YACxC,OAAO;YACP,SAAS;YACT,SAAS;YACT,EAAE,EAAE,eAAe;YACnB,MAAM;SACP,CAAC,CAAC;QAEH,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,IAAI,MAAM,GAAG,CAAC,CAAC;QAEf,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;YAC9C,MAAM,WAAW,GAAG,MAAM,OAAO,CAAC,GAAG,CACnC;;;;;+EAKuE,EACvE,EAAE,GAAG,EAAE,SAAS,EAAE,CACnB,CAAC;YACF,MAAM,UAAU,GAAI,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,YAAY,CAAc,IAAI,EAAE,CAAC;YACjF,MAAM,QAAQ,GAAI,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,UAAU,CAAc,IAAI,EAAE,CAAC;YAE7E,KAAK,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;gBAC7B,MAAM,CAAC,GAAG,MAAM,SAAS,CAAC;oBACxB,OAAO;oBACP,SAAS;oBACT,SAAS,EAAE,GAAG;oBACd,EAAE,EAAE,8BAA8B,SAAS,EAAE;oBAC7C,MAAM,EAAE,MAAM,IAAI,kCAAkC,SAAS,EAAE;iBAChE,CAAC,CAAC;gBACH,IAAI,CAAC,CAAC,OAAO;oBAAE,QAAQ,EAAE,CAAC;YAC5B,CAAC;YACD,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;gBAC3B,MAAM,CAAC,GAAG,MAAM,SAAS,CAAC;oBACxB,OAAO;oBACP,SAAS;oBACT,SAAS,EAAE,GAAG;oBACd,EAAE,EAAE,8BAA8B,SAAS,EAAE;oBAC7C,MAAM,EAAE,MAAM,IAAI,kCAAkC,SAAS,EAAE;iBAChE,CAAC,CAAC;gBACH,IAAI,CAAC,CAAC,OAAO;oBAAE,MAAM,EAAE,CAAC;YAC1B,CAAC;QACH,CAAC;QAED,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,SAAS,CAAC;QACpE,MAAM,OAAO,GAAG,WAAW,CAAC,CAAC,CAAC,sBAAsB,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;QAC3E,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,6BAA6B,SAAS,gBAAgB,OAAO,eAAe,UAAU,YAAY,OAAO,OAAO,EAAE,IAAI,CACvH,CAAC;QAEF,OAAO;YACL,SAAS;YACT,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,cAAc,EAAE,IAAI,CAAC,cAAc;YACnC,OAAO,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE;SAC9B,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type CandidatesFilter } from "../lib/filter-token.js";
|
|
2
|
+
/**
|
|
3
|
+
* memory-find-candidates — deterministic bulk-delete selector.
|
|
4
|
+
*
|
|
5
|
+
* Closes the 2026-04-22 failure class where the admin agent authored a
|
|
6
|
+
* selection cypher against a schema it had not loaded (`[:HAS_MESSAGE]`
|
|
7
|
+
* instead of `[:PART_OF]`), producing a vacuously-true filter that trashed
|
|
8
|
+
* 175 Conversation nodes (69 with real user content). The fix removes the
|
|
9
|
+
* LLM from the selection predicate: the agent chooses a filter name from a
|
|
10
|
+
* fixed set, and the server runs the canonical cypher.
|
|
11
|
+
*
|
|
12
|
+
* Two filters ship in v1 (Task 655):
|
|
13
|
+
* - `empty` — zero `(m:Message)-[:PART_OF]->(c:Conversation)` Messages
|
|
14
|
+
* - `single-assistant` — exactly one `:PART_OF` Message, with `role='assistant'`
|
|
15
|
+
*
|
|
16
|
+
* A third filter `no-real-user-input` is deferred to Task 656 (content-match
|
|
17
|
+
* predicate has a distinct false-positive risk profile).
|
|
18
|
+
*
|
|
19
|
+
* The returned `filterToken` is a signed opaque bundle that `memory-delete`
|
|
20
|
+
* re-verifies at trash time. There is no path by which the agent can feed a
|
|
21
|
+
* hand-rolled list to `memory-delete` with a token — the token only exists
|
|
22
|
+
* when `memory-find-candidates` minted it, and the server re-runs the same
|
|
23
|
+
* predicate on each elementId before trashing.
|
|
24
|
+
*/
|
|
25
|
+
export interface MemoryFindCandidatesParams {
|
|
26
|
+
accountId: string;
|
|
27
|
+
filter: CandidatesFilter;
|
|
28
|
+
agentType?: "admin" | "public";
|
|
29
|
+
limit?: number;
|
|
30
|
+
dryRun?: boolean;
|
|
31
|
+
}
|
|
32
|
+
export interface MemoryFindCandidatesItem {
|
|
33
|
+
elementId: string;
|
|
34
|
+
conversationId: string | null;
|
|
35
|
+
assistantMsgCount: number;
|
|
36
|
+
realUserMsgCount: number;
|
|
37
|
+
firstUserMsgPreview: string | null;
|
|
38
|
+
trashedAt: string | null;
|
|
39
|
+
}
|
|
40
|
+
export interface MemoryFindCandidatesResult {
|
|
41
|
+
filter: CandidatesFilter;
|
|
42
|
+
agentType?: "admin" | "public";
|
|
43
|
+
matched: number;
|
|
44
|
+
sampled: number;
|
|
45
|
+
items: MemoryFindCandidatesItem[];
|
|
46
|
+
filterToken: string;
|
|
47
|
+
dryRun: boolean;
|
|
48
|
+
}
|
|
49
|
+
/** Server-side re-check for `memory-delete` token verification. */
|
|
50
|
+
export declare function reverifyCandidate(params: {
|
|
51
|
+
session: import("neo4j-driver").Session;
|
|
52
|
+
accountId: string;
|
|
53
|
+
filter: CandidatesFilter;
|
|
54
|
+
agentType?: "admin" | "public";
|
|
55
|
+
elementId: string;
|
|
56
|
+
}): Promise<boolean>;
|
|
57
|
+
export declare function memoryFindCandidates(params: MemoryFindCandidatesParams): Promise<MemoryFindCandidatesResult>;
|
|
58
|
+
//# sourceMappingURL=memory-find-candidates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-find-candidates.d.ts","sourceRoot":"","sources":["../../src/tools/memory-find-candidates.ts"],"names":[],"mappings":"AACA,OAAO,EAAmB,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAEhF;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,MAAM,WAAW,0BAA0B;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,gBAAgB,CAAC;IACzB,SAAS,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,gBAAgB,CAAC;IACzB,SAAS,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,wBAAwB,EAAE,CAAC;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC;CACjB;AAwDD,mEAAmE;AACnE,wBAAsB,iBAAiB,CAAC,MAAM,EAAE;IAC9C,OAAO,EAAE,OAAO,cAAc,EAAE,OAAO,CAAC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,gBAAgB,CAAC;IACzB,SAAS,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,OAAO,CAAC,CA+BnB;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,0BAA0B,GACjC,OAAO,CAAC,0BAA0B,CAAC,CA6CrC"}
|
|
@@ -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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"}
|