@rubytech/create-maxy 1.0.742 → 1.0.744

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 (51) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate-write.test.d.ts +2 -0
  3. package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate-write.test.d.ts.map +1 -0
  4. package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate-write.test.js +97 -0
  5. package/payload/platform/lib/graph-mcp/dist/__tests__/cypher-validate-write.test.js.map +1 -0
  6. package/payload/platform/lib/graph-mcp/dist/cypher-validate.d.ts +13 -1
  7. package/payload/platform/lib/graph-mcp/dist/cypher-validate.d.ts.map +1 -1
  8. package/payload/platform/lib/graph-mcp/dist/cypher-validate.js +70 -3
  9. package/payload/platform/lib/graph-mcp/dist/cypher-validate.js.map +1 -1
  10. package/payload/platform/lib/graph-mcp/dist/index.js +154 -11
  11. package/payload/platform/lib/graph-mcp/dist/index.js.map +1 -1
  12. package/payload/platform/lib/graph-mcp/src/__tests__/cypher-validate-write.test.ts +150 -0
  13. package/payload/platform/lib/graph-mcp/src/cypher-validate.ts +95 -3
  14. package/payload/platform/lib/graph-mcp/src/index.ts +202 -17
  15. package/payload/platform/lib/graph-write/dist/__tests__/audit.test.d.ts +2 -0
  16. package/payload/platform/lib/graph-write/dist/__tests__/audit.test.d.ts.map +1 -0
  17. package/payload/platform/lib/graph-write/dist/__tests__/audit.test.js +147 -0
  18. package/payload/platform/lib/graph-write/dist/__tests__/audit.test.js.map +1 -0
  19. package/payload/platform/lib/graph-write/dist/audit.d.ts +84 -0
  20. package/payload/platform/lib/graph-write/dist/audit.d.ts.map +1 -0
  21. package/payload/platform/lib/graph-write/dist/audit.js +129 -0
  22. package/payload/platform/lib/graph-write/dist/audit.js.map +1 -0
  23. package/payload/platform/lib/graph-write/dist/index.d.ts +1 -0
  24. package/payload/platform/lib/graph-write/dist/index.d.ts.map +1 -1
  25. package/payload/platform/lib/graph-write/dist/index.js +18 -22
  26. package/payload/platform/lib/graph-write/dist/index.js.map +1 -1
  27. package/payload/platform/lib/graph-write/src/__tests__/audit.test.ts +162 -0
  28. package/payload/platform/lib/graph-write/src/audit.ts +182 -0
  29. package/payload/platform/lib/graph-write/src/index.ts +5 -0
  30. package/payload/platform/package.json +2 -2
  31. package/payload/platform/plugins/docs/references/memory-guide.md +2 -0
  32. package/payload/platform/plugins/docs/references/troubleshooting.md +16 -0
  33. package/payload/platform/plugins/scheduling/mcp/dist/index.js +11 -4
  34. package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
  35. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/getUserTimezone.test.d.ts +2 -0
  36. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/getUserTimezone.test.d.ts.map +1 -0
  37. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/getUserTimezone.test.js +119 -0
  38. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/getUserTimezone.test.js.map +1 -0
  39. package/payload/platform/plugins/scheduling/mcp/dist/lib/neo4j.d.ts +22 -3
  40. package/payload/platform/plugins/scheduling/mcp/dist/lib/neo4j.d.ts.map +1 -1
  41. package/payload/platform/plugins/scheduling/mcp/dist/lib/neo4j.js +33 -7
  42. package/payload/platform/plugins/scheduling/mcp/dist/lib/neo4j.js.map +1 -1
  43. package/payload/platform/plugins/scheduling/mcp/package.json +4 -2
  44. package/payload/platform/plugins/scheduling/mcp/vitest.config.ts +9 -0
  45. package/payload/platform/templates/specialists/agents/database-operator.md +39 -6
  46. package/payload/server/chunk-2T4RRIJK.js +9462 -0
  47. package/payload/server/chunk-JLVVVQN7.js +9447 -0
  48. package/payload/server/chunk-TXPEEAV6.js +2997 -0
  49. package/payload/server/client-pool-TCKGDZLE.js +28 -0
  50. package/payload/server/maxy-edge.js +95 -17
  51. package/payload/server/server.js +3 -3
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_test_1 = __importDefault(require("node:test"));
7
+ const strict_1 = __importDefault(require("node:assert/strict"));
8
+ const audit_js_1 = require("../audit.js");
9
+ const baseInput = {
10
+ schema: {
11
+ labels: new Set(["Person", "Organization", "Task", "KnowledgeDocument"]),
12
+ relationshipTypes: new Set(["KNOWS", "PARTICIPANT", "REFERENCES", "MENTIONS", "PART_OF"]),
13
+ },
14
+ agentName: "database-operator",
15
+ sessionId: "session-abc",
16
+ nodesCreated: 0,
17
+ relsCreated: 0,
18
+ orphanIds: [],
19
+ };
20
+ (0, node_test_1.default)("audit: clean write with provenance + known types yields zero warnings", () => {
21
+ const cypher = `
22
+ MATCH (p:Person {name: $name}), (o:Organization {name: $org})
23
+ MERGE (p)-[r:KNOWS]->(o)
24
+ SET r.createdAt = datetime(),
25
+ r.createdByAgent = $agent,
26
+ r.createdByTool = 'graph-cypher-write',
27
+ r.createdBySession = $sessionId
28
+ `;
29
+ const warnings = (0, audit_js_1.auditCypherWrite)({ ...baseInput, cypher, relsCreated: 1 });
30
+ strict_1.default.deepEqual(warnings, []);
31
+ });
32
+ (0, node_test_1.default)("audit: unknown edge type yields unknown-type-warning", () => {
33
+ const cypher = "MATCH (a:Person), (b:Person) CREATE (a)-[:HAS_KNOWN]->(b)";
34
+ const warnings = (0, audit_js_1.auditCypherWrite)({ ...baseInput, cypher, relsCreated: 1 });
35
+ const w = warnings.find((x) => x.kind === "unknown-type-warning");
36
+ strict_1.default.ok(w, "expected unknown-type-warning");
37
+ strict_1.default.equal(w.kind, "unknown-type-warning");
38
+ if (w.kind === "unknown-type-warning") {
39
+ strict_1.default.equal(w.type, "HAS_KNOWN");
40
+ }
41
+ });
42
+ (0, node_test_1.default)("audit: known APOC edge in YIELD does not trip unknown-type", () => {
43
+ const cypher = "MATCH (a:Person), (b:Person) WHERE a.email = b.email AND id(a) < id(b) WITH a, b CALL apoc.refactor.mergeNodes([a, b]) YIELD node RETURN node";
44
+ const warnings = (0, audit_js_1.auditCypherWrite)({ ...baseInput, cypher });
45
+ const unknownTypeWarn = warnings.find((x) => x.kind === "unknown-type-warning");
46
+ strict_1.default.equal(unknownTypeWarn, undefined);
47
+ });
48
+ (0, node_test_1.default)("audit: CREATE with no provenance stamps yields missing-provenance-warning", () => {
49
+ const cypher = "CREATE (n:Person {name: 'Foo'})";
50
+ const warnings = (0, audit_js_1.auditCypherWrite)({ ...baseInput, cypher, nodesCreated: 1 });
51
+ const w = warnings.find((x) => x.kind === "missing-provenance-warning");
52
+ strict_1.default.ok(w, "expected missing-provenance-warning");
53
+ if (w.kind === "missing-provenance-warning") {
54
+ strict_1.default.equal(w.created, 1);
55
+ strict_1.default.equal(w.stamped, 0);
56
+ }
57
+ });
58
+ (0, node_test_1.default)("audit: MERGE with createdBy* stamps does not trip missing-provenance", () => {
59
+ const cypher = "MERGE (n:Person {email: $email}) ON CREATE SET n.createdByAgent = $agent, n.createdBySession = $sid";
60
+ const warnings = (0, audit_js_1.auditCypherWrite)({ ...baseInput, cypher, nodesCreated: 1 });
61
+ const w = warnings.find((x) => x.kind === "missing-provenance-warning");
62
+ strict_1.default.equal(w, undefined);
63
+ });
64
+ (0, node_test_1.default)("audit: provenance check ignores stamps inside string literals", () => {
65
+ const cypher = "CREATE (n:Person {bio: 'I have createdByAgent in my bio'})";
66
+ const warnings = (0, audit_js_1.auditCypherWrite)({ ...baseInput, cypher, nodesCreated: 1 });
67
+ const w = warnings.find((x) => x.kind === "missing-provenance-warning");
68
+ strict_1.default.ok(w, "expected missing-provenance-warning despite stamp-like substring in literal");
69
+ });
70
+ (0, node_test_1.default)("audit: idempotent MERGE that matched existing node (nodesCreated=0) emits no missing-provenance-warning", () => {
71
+ // Reviewer-flagged false-positive case. MERGE (n:Person {email}) without
72
+ // ON CREATE SET stamps — static count = 1, stamps = 0. If the node
73
+ // already existed, upstream reports nodesCreated=0 and no provenance was
74
+ // needed; the audit must not warn.
75
+ const cypher = "MERGE (n:Person {email: $email})";
76
+ const warnings = (0, audit_js_1.auditCypherWrite)({ ...baseInput, cypher, nodesCreated: 0 });
77
+ const w = warnings.find((x) => x.kind === "missing-provenance-warning");
78
+ strict_1.default.equal(w, undefined, "must not warn when nodesCreated=0");
79
+ });
80
+ (0, node_test_1.default)("audit: orphanIds populated yields orphan-warning", () => {
81
+ const warnings = (0, audit_js_1.auditCypherWrite)({
82
+ ...baseInput,
83
+ cypher: "CREATE (n:Person)",
84
+ nodesCreated: 1,
85
+ orphanIds: ["4:abc:1", "4:abc:2"],
86
+ });
87
+ const w = warnings.find((x) => x.kind === "orphan-warning");
88
+ strict_1.default.ok(w, "expected orphan-warning");
89
+ if (w.kind === "orphan-warning") {
90
+ strict_1.default.deepEqual(w.orphanIds, ["4:abc:1", "4:abc:2"]);
91
+ }
92
+ });
93
+ (0, node_test_1.default)("audit: empty orphanIds emits no orphan-warning", () => {
94
+ const warnings = (0, audit_js_1.auditCypherWrite)({
95
+ ...baseInput,
96
+ cypher: "MATCH (a:Person), (b:Person) CREATE (a)-[:KNOWS]->(b)",
97
+ relsCreated: 1,
98
+ orphanIds: [],
99
+ });
100
+ const w = warnings.find((x) => x.kind === "orphan-warning");
101
+ strict_1.default.equal(w, undefined);
102
+ });
103
+ (0, node_test_1.default)("formatAuditLine: accepted line includes counters + provenance", () => {
104
+ const line = (0, audit_js_1.formatAuditLine)({
105
+ kind: "accepted",
106
+ cypherPrefix: "MATCH (a:Person) CREATE",
107
+ nodesCreated: 1,
108
+ relsCreated: 4,
109
+ agentName: "database-operator",
110
+ sessionId: "abc-123",
111
+ });
112
+ strict_1.default.match(line, /^\[graph-cypher-write\] accepted /);
113
+ strict_1.default.match(line, /nodesCreated=1/);
114
+ strict_1.default.match(line, /relsCreated=4/);
115
+ strict_1.default.match(line, /agentName=database-operator/);
116
+ strict_1.default.match(line, /sessionId=abc-123/);
117
+ });
118
+ (0, node_test_1.default)("formatAuditLine: orphan-warning line lists orphanIds", () => {
119
+ const line = (0, audit_js_1.formatAuditLine)({
120
+ kind: "orphan-warning",
121
+ cypherPrefix: "CREATE (n:Person)",
122
+ orphanIds: ["4:abc:1", "4:abc:2"],
123
+ });
124
+ strict_1.default.match(line, /^\[graph-cypher-write\] orphan-warning /);
125
+ strict_1.default.match(line, /orphanIds=4:abc:1,4:abc:2/);
126
+ });
127
+ (0, node_test_1.default)("formatAuditLine: unknown-type-warning names the type", () => {
128
+ const line = (0, audit_js_1.formatAuditLine)({
129
+ kind: "unknown-type-warning",
130
+ cypherPrefix: "CREATE (a)-[:HAS_KNOWN]->(b)",
131
+ type: "HAS_KNOWN",
132
+ });
133
+ strict_1.default.match(line, /^\[graph-cypher-write\] unknown-type-warning /);
134
+ strict_1.default.match(line, /type=HAS_KNOWN/);
135
+ });
136
+ (0, node_test_1.default)("formatAuditLine: missing-provenance-warning shows created vs stamped counts", () => {
137
+ const line = (0, audit_js_1.formatAuditLine)({
138
+ kind: "missing-provenance-warning",
139
+ cypherPrefix: "CREATE (n:Person)",
140
+ created: 3,
141
+ stamped: 1,
142
+ });
143
+ strict_1.default.match(line, /^\[graph-cypher-write\] missing-provenance-warning /);
144
+ strict_1.default.match(line, /created=3/);
145
+ strict_1.default.match(line, /stamped=1/);
146
+ });
147
+ //# sourceMappingURL=audit.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.test.js","sourceRoot":"","sources":["../../src/__tests__/audit.test.ts"],"names":[],"mappings":";;;;;AAAA,0DAA6B;AAC7B,gEAAwC;AACxC,0CAIqB;AAErB,MAAM,SAAS,GAA0C;IACvD,MAAM,EAAE;QACN,MAAM,EAAE,IAAI,GAAG,CAAC,CAAC,QAAQ,EAAE,cAAc,EAAE,MAAM,EAAE,mBAAmB,CAAC,CAAC;QACxE,iBAAiB,EAAE,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;KAC1F;IACD,SAAS,EAAE,mBAAmB;IAC9B,SAAS,EAAE,aAAa;IACxB,YAAY,EAAE,CAAC;IACf,WAAW,EAAE,CAAC;IACd,SAAS,EAAE,EAAE;CACd,CAAC;AAEF,IAAA,mBAAI,EAAC,uEAAuE,EAAE,GAAG,EAAE;IACjF,MAAM,MAAM,GAAG;;;;;;;GAOd,CAAC;IACF,MAAM,QAAQ,GAAG,IAAA,2BAAgB,EAAC,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC,CAAC;IAC5E,gBAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;AACjC,CAAC,CAAC,CAAC;AAEH,IAAA,mBAAI,EAAC,sDAAsD,EAAE,GAAG,EAAE;IAChE,MAAM,MAAM,GAAG,2DAA2D,CAAC;IAC3E,MAAM,QAAQ,GAAG,IAAA,2BAAgB,EAAC,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC,CAAC;IAC5E,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,sBAAsB,CAAC,CAAC;IAClE,gBAAM,CAAC,EAAE,CAAC,CAAC,EAAE,+BAA+B,CAAC,CAAC;IAC9C,gBAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,sBAAsB,CAAC,CAAC;IAC7C,IAAI,CAAC,CAAC,IAAI,KAAK,sBAAsB,EAAE,CAAC;QACtC,gBAAM,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IACpC,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAA,mBAAI,EAAC,4DAA4D,EAAE,GAAG,EAAE;IACtE,MAAM,MAAM,GACV,+IAA+I,CAAC;IAClJ,MAAM,QAAQ,GAAG,IAAA,2BAAgB,EAAC,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;IAC5D,MAAM,eAAe,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,sBAAsB,CAAC,CAAC;IAChF,gBAAM,CAAC,KAAK,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC;AAC3C,CAAC,CAAC,CAAC;AAEH,IAAA,mBAAI,EAAC,2EAA2E,EAAE,GAAG,EAAE;IACrF,MAAM,MAAM,GAAG,iCAAiC,CAAC;IACjD,MAAM,QAAQ,GAAG,IAAA,2BAAgB,EAAC,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC,CAAC;IAC7E,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,4BAA4B,CAAC,CAAC;IACxE,gBAAM,CAAC,EAAE,CAAC,CAAC,EAAE,qCAAqC,CAAC,CAAC;IACpD,IAAI,CAAC,CAAC,IAAI,KAAK,4BAA4B,EAAE,CAAC;QAC5C,gBAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QAC3B,gBAAM,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC7B,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAA,mBAAI,EAAC,sEAAsE,EAAE,GAAG,EAAE;IAChF,MAAM,MAAM,GACV,qGAAqG,CAAC;IACxG,MAAM,QAAQ,GAAG,IAAA,2BAAgB,EAAC,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC,CAAC;IAC7E,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,4BAA4B,CAAC,CAAC;IACxE,gBAAM,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;AAC7B,CAAC,CAAC,CAAC;AAEH,IAAA,mBAAI,EAAC,+DAA+D,EAAE,GAAG,EAAE;IACzE,MAAM,MAAM,GACV,4DAA4D,CAAC;IAC/D,MAAM,QAAQ,GAAG,IAAA,2BAAgB,EAAC,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC,CAAC;IAC7E,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,4BAA4B,CAAC,CAAC;IACxE,gBAAM,CAAC,EAAE,CAAC,CAAC,EAAE,6EAA6E,CAAC,CAAC;AAC9F,CAAC,CAAC,CAAC;AAEH,IAAA,mBAAI,EAAC,yGAAyG,EAAE,GAAG,EAAE;IACnH,yEAAyE;IACzE,mEAAmE;IACnE,yEAAyE;IACzE,mCAAmC;IACnC,MAAM,MAAM,GAAG,kCAAkC,CAAC;IAClD,MAAM,QAAQ,GAAG,IAAA,2BAAgB,EAAC,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC,CAAC;IAC7E,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,4BAA4B,CAAC,CAAC;IACxE,gBAAM,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,EAAE,mCAAmC,CAAC,CAAC;AAClE,CAAC,CAAC,CAAC;AAEH,IAAA,mBAAI,EAAC,kDAAkD,EAAE,GAAG,EAAE;IAC5D,MAAM,QAAQ,GAAG,IAAA,2BAAgB,EAAC;QAChC,GAAG,SAAS;QACZ,MAAM,EAAE,mBAAmB;QAC3B,YAAY,EAAE,CAAC;QACf,SAAS,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC;KAClC,CAAC,CAAC;IACH,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,gBAAgB,CAAC,CAAC;IAC5D,gBAAM,CAAC,EAAE,CAAC,CAAC,EAAE,yBAAyB,CAAC,CAAC;IACxC,IAAI,CAAC,CAAC,IAAI,KAAK,gBAAgB,EAAE,CAAC;QAChC,gBAAM,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC;IACxD,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAA,mBAAI,EAAC,gDAAgD,EAAE,GAAG,EAAE;IAC1D,MAAM,QAAQ,GAAG,IAAA,2BAAgB,EAAC;QAChC,GAAG,SAAS;QACZ,MAAM,EAAE,uDAAuD;QAC/D,WAAW,EAAE,CAAC;QACd,SAAS,EAAE,EAAE;KACd,CAAC,CAAC;IACH,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,gBAAgB,CAAC,CAAC;IAC5D,gBAAM,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;AAC7B,CAAC,CAAC,CAAC;AAEH,IAAA,mBAAI,EAAC,+DAA+D,EAAE,GAAG,EAAE;IACzE,MAAM,IAAI,GAAG,IAAA,0BAAe,EAAC;QAC3B,IAAI,EAAE,UAAU;QAChB,YAAY,EAAE,yBAAyB;QACvC,YAAY,EAAE,CAAC;QACf,WAAW,EAAE,CAAC;QACd,SAAS,EAAE,mBAAmB;QAC9B,SAAS,EAAE,SAAS;KACrB,CAAC,CAAC;IACH,gBAAM,CAAC,KAAK,CAAC,IAAI,EAAE,mCAAmC,CAAC,CAAC;IACxD,gBAAM,CAAC,KAAK,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;IACrC,gBAAM,CAAC,KAAK,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;IACpC,gBAAM,CAAC,KAAK,CAAC,IAAI,EAAE,6BAA6B,CAAC,CAAC;IAClD,gBAAM,CAAC,KAAK,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC;AAC1C,CAAC,CAAC,CAAC;AAEH,IAAA,mBAAI,EAAC,sDAAsD,EAAE,GAAG,EAAE;IAChE,MAAM,IAAI,GAAG,IAAA,0BAAe,EAAC;QAC3B,IAAI,EAAE,gBAAgB;QACtB,YAAY,EAAE,mBAAmB;QACjC,SAAS,EAAE,CAAC,SAAS,EAAE,SAAS,CAAC;KAClC,CAAC,CAAC;IACH,gBAAM,CAAC,KAAK,CAAC,IAAI,EAAE,yCAAyC,CAAC,CAAC;IAC9D,gBAAM,CAAC,KAAK,CAAC,IAAI,EAAE,2BAA2B,CAAC,CAAC;AAClD,CAAC,CAAC,CAAC;AAEH,IAAA,mBAAI,EAAC,sDAAsD,EAAE,GAAG,EAAE;IAChE,MAAM,IAAI,GAAG,IAAA,0BAAe,EAAC;QAC3B,IAAI,EAAE,sBAAsB;QAC5B,YAAY,EAAE,8BAA8B;QAC5C,IAAI,EAAE,WAAW;KAClB,CAAC,CAAC;IACH,gBAAM,CAAC,KAAK,CAAC,IAAI,EAAE,+CAA+C,CAAC,CAAC;IACpE,gBAAM,CAAC,KAAK,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;AACvC,CAAC,CAAC,CAAC;AAEH,IAAA,mBAAI,EAAC,6EAA6E,EAAE,GAAG,EAAE;IACvF,MAAM,IAAI,GAAG,IAAA,0BAAe,EAAC;QAC3B,IAAI,EAAE,4BAA4B;QAClC,YAAY,EAAE,mBAAmB;QACjC,OAAO,EAAE,CAAC;QACV,OAAO,EAAE,CAAC;KACX,CAAC,CAAC;IACH,gBAAM,CAAC,KAAK,CAAC,IAAI,EAAE,qDAAqD,CAAC,CAAC;IAC1E,gBAAM,CAAC,KAAK,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IAChC,gBAAM,CAAC,KAAK,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;AAClC,CAAC,CAAC,CAAC"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Post-write audit for the database-operator's raw `write_neo4j_cypher` tool
3
+ * (Task 796). Sibling to `[graph-write] accepted` for the wrapped writers
4
+ * (`memory-write`, `contact-create`, etc. — Task 673) — same module so the
5
+ * `[graph-*]` log family stays uniform across both write surfaces.
6
+ *
7
+ * Design posture:
8
+ * - Static parse first. Pre-flight regex on the cypher text catches missing
9
+ * provenance stamps and unknown edge types cheaply, no driver round-trip.
10
+ * - Dynamic orphan detection is the caller's responsibility — the graph-mcp
11
+ * shim runs the detection query against Neo4j after the upstream commits
12
+ * and passes the resulting elementId list as `orphanIds`. Keeping the
13
+ * module pure (no neo4j-driver import) makes it unit-testable without a
14
+ * live database.
15
+ * - Audit is observational. Warnings never block the write. The
16
+ * prompt-level Graph Stewardship Doctrine in
17
+ * [database-operator.md](../../../templates/specialists/agents/database-operator.md)
18
+ * names the discipline; the audit is the verification stream.
19
+ *
20
+ * String literals are stripped before pattern checks so that property values
21
+ * containing words like `'createdByAgent in my bio'` cannot trip a false
22
+ * provenance count, and edge-like patterns inside quoted strings cannot
23
+ * trip a false unknown-type warning. The regex shape is duplicated from
24
+ * [cypher-validate.ts](../../graph-mcp/src/cypher-validate.ts) intentionally:
25
+ * graph-write does not depend on graph-mcp, and a one-line regex helper
26
+ * doesn't justify a third package. Drift in either copy is a regression
27
+ * surfaced by both the validator tests and the audit tests.
28
+ */
29
+ export interface SchemaSnapshot {
30
+ readonly labels: ReadonlySet<string>;
31
+ readonly relationshipTypes: ReadonlySet<string>;
32
+ }
33
+ export interface CypherWriteAuditInput {
34
+ cypher: string;
35
+ schema: SchemaSnapshot;
36
+ agentName: string;
37
+ sessionId: string;
38
+ /** Counter from upstream response — drives missing-provenance arithmetic. */
39
+ nodesCreated: number;
40
+ /** Counter from upstream response — informational only. */
41
+ relsCreated: number;
42
+ /**
43
+ * elementIds of nodes the post-write orphan query identified. Caller scopes
44
+ * the query to (createdBySession, createdAt >= writeStartTimestamp) so this
45
+ * list cannot include nodes from prior writes in the same session. Empty
46
+ * array = no orphans (or no scope to check).
47
+ */
48
+ orphanIds: string[];
49
+ }
50
+ export type AuditWarning = {
51
+ kind: "orphan-warning";
52
+ orphanIds: string[];
53
+ } | {
54
+ kind: "unknown-type-warning";
55
+ type: string;
56
+ } | {
57
+ kind: "missing-provenance-warning";
58
+ created: number;
59
+ stamped: number;
60
+ };
61
+ export type AuditLine = {
62
+ kind: "accepted";
63
+ cypherPrefix: string;
64
+ nodesCreated: number;
65
+ relsCreated: number;
66
+ agentName: string;
67
+ sessionId: string;
68
+ } | {
69
+ kind: "orphan-warning";
70
+ cypherPrefix: string;
71
+ orphanIds: string[];
72
+ } | {
73
+ kind: "unknown-type-warning";
74
+ cypherPrefix: string;
75
+ type: string;
76
+ } | {
77
+ kind: "missing-provenance-warning";
78
+ cypherPrefix: string;
79
+ created: number;
80
+ stamped: number;
81
+ };
82
+ export declare function auditCypherWrite(input: CypherWriteAuditInput): AuditWarning[];
83
+ export declare function formatAuditLine(line: AuditLine): string;
84
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACrC,QAAQ,CAAC,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;CACjD;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,YAAY,EAAE,MAAM,CAAC;IACrB,2DAA2D;IAC3D,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,sBAAsB,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,4BAA4B,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7E,MAAM,MAAM,SAAS,GACjB;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,GACD;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,EAAE,CAAA;CAAE,GACrE;IAAE,IAAI,EAAE,sBAAsB,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACpE;IACE,IAAI,EAAE,4BAA4B,CAAC;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AA8CN,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,YAAY,EAAE,CAgD7E;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAYvD"}
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ /**
3
+ * Post-write audit for the database-operator's raw `write_neo4j_cypher` tool
4
+ * (Task 796). Sibling to `[graph-write] accepted` for the wrapped writers
5
+ * (`memory-write`, `contact-create`, etc. — Task 673) — same module so the
6
+ * `[graph-*]` log family stays uniform across both write surfaces.
7
+ *
8
+ * Design posture:
9
+ * - Static parse first. Pre-flight regex on the cypher text catches missing
10
+ * provenance stamps and unknown edge types cheaply, no driver round-trip.
11
+ * - Dynamic orphan detection is the caller's responsibility — the graph-mcp
12
+ * shim runs the detection query against Neo4j after the upstream commits
13
+ * and passes the resulting elementId list as `orphanIds`. Keeping the
14
+ * module pure (no neo4j-driver import) makes it unit-testable without a
15
+ * live database.
16
+ * - Audit is observational. Warnings never block the write. The
17
+ * prompt-level Graph Stewardship Doctrine in
18
+ * [database-operator.md](../../../templates/specialists/agents/database-operator.md)
19
+ * names the discipline; the audit is the verification stream.
20
+ *
21
+ * String literals are stripped before pattern checks so that property values
22
+ * containing words like `'createdByAgent in my bio'` cannot trip a false
23
+ * provenance count, and edge-like patterns inside quoted strings cannot
24
+ * trip a false unknown-type warning. The regex shape is duplicated from
25
+ * [cypher-validate.ts](../../graph-mcp/src/cypher-validate.ts) intentionally:
26
+ * graph-write does not depend on graph-mcp, and a one-line regex helper
27
+ * doesn't justify a third package. Drift in either copy is a regression
28
+ * surfaced by both the validator tests and the audit tests.
29
+ */
30
+ Object.defineProperty(exports, "__esModule", { value: true });
31
+ exports.auditCypherWrite = auditCypherWrite;
32
+ exports.formatAuditLine = formatAuditLine;
33
+ // Mirrors cypher-validate's pattern. Captures TYPE(|TYPE)* alternation; the
34
+ // audit splits on `|` to enumerate every referenced type.
35
+ const EDGE_PATTERN = /\[[^\]]*?:([A-Z_][A-Za-z0-9_]*(?:\|[A-Z_][A-Za-z0-9_]*)*)[^\]]*?\]/g;
36
+ // Counts CREATE (n:Label) and MERGE (n:Label) statements that introduce a
37
+ // node. The trailing `[A-Z]` requires at least one label (so `CREATE INDEX`
38
+ // and bare `CREATE (a)-[:R]->(b)` shapes don't trip this — bare patterns
39
+ // reference a previously-MATCHed alias and don't introduce new nodes).
40
+ const CREATE_OR_MERGE_NODE = /\b(?:CREATE|MERGE)\s*\(\s*[A-Za-z_][A-Za-z0-9_]*\s*:\s*[A-Z]/g;
41
+ // Stamp tokens — at least one of these MUST appear per created node for the
42
+ // write to satisfy provenance discipline. The audit counts occurrences;
43
+ // stamp-count >= node-count is the pass criterion.
44
+ const PROVENANCE_TOKEN = /\bcreatedBy(?:Agent|Tool|Session|Source)\b/g;
45
+ function stripStringLiterals(cypher) {
46
+ // Identical regex to cypher-validate.ts. Replaces single- and double-
47
+ // quoted literals with empty quotes so e.g. 'CREATE (n:Person)' inside a
48
+ // property value cannot inflate the create-count.
49
+ return cypher.replace(/'[^']*'|"[^"]*"/g, '""');
50
+ }
51
+ function extractEdgeTypes(cleaned) {
52
+ const out = new Set();
53
+ // Use String.matchAll for stateless iteration — no shared regex lastIndex.
54
+ for (const m of cleaned.matchAll(EDGE_PATTERN)) {
55
+ for (const t of m[1].split("|")) {
56
+ const clean = t.trim();
57
+ if (clean)
58
+ out.add(clean);
59
+ }
60
+ }
61
+ return out;
62
+ }
63
+ function countCreateOrMergeNodes(cleaned) {
64
+ const matches = cleaned.match(CREATE_OR_MERGE_NODE);
65
+ return matches ? matches.length : 0;
66
+ }
67
+ function countProvenanceStamps(cleaned) {
68
+ const matches = cleaned.match(PROVENANCE_TOKEN);
69
+ return matches ? matches.length : 0;
70
+ }
71
+ function auditCypherWrite(input) {
72
+ const warnings = [];
73
+ const cleaned = stripStringLiterals(input.cypher);
74
+ // 1. Unknown edge type — cypher names a type not in the live ontology.
75
+ // Enumerate every referenced type; emit one warning per unknown.
76
+ const referencedTypes = extractEdgeTypes(cleaned);
77
+ for (const t of referencedTypes) {
78
+ if (!input.schema.relationshipTypes.has(t)) {
79
+ warnings.push({ kind: "unknown-type-warning", type: t });
80
+ }
81
+ }
82
+ // 2. Missing provenance — count CREATE/MERGE-with-label statements vs
83
+ // `createdBy*` token occurrences. Stamps may live in inline maps
84
+ // (`{createdByAgent: $agent}`) or SET clauses; both shapes contribute
85
+ // to the token count. The arithmetic is precision-imprecise — a
86
+ // multi-stamp single CREATE inflates `stamped`, a multi-CREATE single
87
+ // SET undercounts — but the directional signal (zero stamps for many
88
+ // creates) catches the failure mode this audit exists to surface.
89
+ //
90
+ // nodesCreated=0 short-circuits the check: the upstream counter is
91
+ // ground truth for whether any node was actually persisted. An
92
+ // idempotent MERGE that matched an existing node has a static
93
+ // create-count of 1 but contributed no new node — no provenance was
94
+ // needed, and warning here would be a false positive.
95
+ if (input.nodesCreated > 0) {
96
+ const createOrMergeNodes = countCreateOrMergeNodes(cleaned);
97
+ if (createOrMergeNodes > 0) {
98
+ const stamps = countProvenanceStamps(cleaned);
99
+ if (stamps < createOrMergeNodes) {
100
+ warnings.push({
101
+ kind: "missing-provenance-warning",
102
+ created: createOrMergeNodes,
103
+ stamped: stamps,
104
+ });
105
+ }
106
+ }
107
+ }
108
+ // 3. Orphans — caller-supplied. The dynamic detection happens at the
109
+ // graph-mcp shim layer (which has the Neo4j driver); the audit module
110
+ // only renders the warning shape.
111
+ if (input.orphanIds.length > 0) {
112
+ warnings.push({ kind: "orphan-warning", orphanIds: input.orphanIds });
113
+ }
114
+ return warnings;
115
+ }
116
+ function formatAuditLine(line) {
117
+ const prefixField = `query="${line.cypherPrefix.replace(/"/g, "'")}"`;
118
+ switch (line.kind) {
119
+ case "accepted":
120
+ return `[graph-cypher-write] accepted ${prefixField} nodesCreated=${line.nodesCreated} relsCreated=${line.relsCreated} agentName=${line.agentName} sessionId=${line.sessionId}`;
121
+ case "orphan-warning":
122
+ return `[graph-cypher-write] orphan-warning ${prefixField} orphanIds=${line.orphanIds.join(",")}`;
123
+ case "unknown-type-warning":
124
+ return `[graph-cypher-write] unknown-type-warning ${prefixField} type=${line.type}`;
125
+ case "missing-provenance-warning":
126
+ return `[graph-cypher-write] missing-provenance-warning ${prefixField} created=${line.created} stamped=${line.stamped}`;
127
+ }
128
+ }
129
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../src/audit.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ export * from "./audit.js";
1
2
  /**
2
3
  * Write doctrine (Task 673): a node without at least one adjacency is noise,
3
4
  * not knowledge. `writeNodeWithEdges` is the single primitive every graph
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,UAAU,GAAG,UAAU,CAAC;IACnC,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,SAAS;IACxB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6FAA6F;IAC7F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uFAAuF;IACvF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,4EAA4E;IAC5E,aAAa,EAAE,iBAAiB,EAAE,CAAC;IACnC,SAAS,EAAE,SAAS,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,uDAAuD;AACvD,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,SAAS,EAAE,SAAS,GACnB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQzB;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,wBAAwB,GAC/B,OAAO,CAAC,eAAe,CAAC,CA8G1B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,cAAc,YAAY,CAAC;AAE3B;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,UAAU,GAAG,UAAU,CAAC;IACnC,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,SAAS;IACxB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6FAA6F;IAC7F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uFAAuF;IACvF,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,4EAA4E;IAC5E,aAAa,EAAE,iBAAiB,EAAE,CAAC;IACnC,SAAS,EAAE,SAAS,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,uDAAuD;AACvD,wBAAgB,cAAc,CAC5B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,SAAS,EAAE,SAAS,GACnB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQzB;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,wBAAwB,GAC/B,OAAO,CAAC,eAAe,CAAC,CA8G1B"}
@@ -1,29 +1,25 @@
1
1
  "use strict";
2
- /**
3
- * Write doctrine (Task 673): a node without at least one adjacency is noise,
4
- * not knowledge. `writeNodeWithEdges` is the single primitive every graph
5
- * writer should call; it rejects zero-relationship writes, verifies every
6
- * relationship target resolves before the node is created, and commits the
7
- * node + all edges in one managed transaction. Provenance is stamped on the
8
- * node as flattened `createdBy*` properties (Neo4j does not persist nested
9
- * maps, and flat props are queryable — `MATCH (n) WHERE n.createdBySession
10
- * = $id RETURN n` is the forensic entry point).
11
- *
12
- * Rejection paths (every one emits a stderr log the admin server pipes to
13
- * server.log, so orphan pressure is visible per-write not just in the
14
- * hourly [graph-health] signal):
15
- * - zero relationships → `[graph-write] reject reason=zero-relationships`
16
- * - unresolved target id → `[graph-write] reject reason=unresolved-target`
17
- *
18
- * The `createdBy.agent` field is advisory, not authoritative — it is sourced
19
- * from the MCP server's AGENT_SLUG env var, which is set by the trusted
20
- * spawning code (admin server / workflow runner). A misconfigured spawner
21
- * will write "unknown"; that's the signal something is wrong. Do not use
22
- * this field for access control — it is forensic, not a security boundary.
23
- */
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
24
16
  Object.defineProperty(exports, "__esModule", { value: true });
25
17
  exports.stampCreatedBy = stampCreatedBy;
26
18
  exports.writeNodeWithEdges = writeNodeWithEdges;
19
+ // Task 796: re-export the post-write audit so consumers (graph-mcp shim)
20
+ // import from the same package as `writeNodeWithEdges`. Keeps the
21
+ // `[graph-*]` log family colocated.
22
+ __exportStar(require("./audit.js"), exports);
27
23
  /** Stamp flattened provenance into node properties. */
28
24
  function stampCreatedBy(props, createdBy) {
29
25
  return {
@@ -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":";;;;;;;;;;;;;;;;AA+DA,wCAWC;AAQD,gDAgHC;AAlMD,yEAAyE;AACzE,kEAAkE;AAClE,oCAAoC;AACpC,6CAA2B;AA2D3B,uDAAuD;AACvD,SAAgB,cAAc,CAC5B,KAA8B,EAC9B,SAAoB;IAEpB,OAAO;QACL,GAAG,KAAK;QACR,cAAc,EAAE,SAAS,CAAC,KAAK,IAAI,SAAS;QAC5C,gBAAgB,EAAE,SAAS,CAAC,OAAO,IAAI,SAAS;QAChD,aAAa,EAAE,SAAS,CAAC,IAAI,IAAI,IAAI;QACrC,eAAe,EAAE,SAAS,CAAC,MAAM,IAAI,IAAI;KAC1C,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACI,KAAK,UAAU,kBAAkB,CACtC,MAAgC;IAEhC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,GAAG,MAAM,CAAC;IAEpE,MAAM,UAAU,GAAG,SAAS,CAAC,KAAK,IAAI,SAAS,CAAC,MAAM,IAAI,SAAS,CAAC;IACpE,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAElC,IAAI,CAAC,aAAa,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC/C,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,yDAAyD,QAAQ,UAAU,UAAU,IAAI,CAC1F,CAAC;QACF,MAAM,IAAI,KAAK,CACb,sHAAsH,CACvH,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3E,MAAM,SAAS,GAAG,cAAc,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;IAEnD,OAAO,MAAM,OAAO,CAAC,YAAY,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;QAC7C,MAAM,SAAS,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;QAC3D,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,GAAG,CACxB,uFAAuF,EACvF,EAAE,GAAG,EAAE,SAAS,EAAE,CACnB,CAAC;QACF,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;QACvD,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC;QAChD,IAAI,KAAK,KAAK,eAAe,EAAE,CAAC;YAC9B,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,wDAAwD,QAAQ,UAAU,UAAU,cAAc,eAAe,UAAU,KAAK,IAAI,CACrI,CAAC;YACF,MAAM,IAAI,KAAK,CACb,4BAA4B,eAAe,GAAG,KAAK,OAAO,eAAe,gFAAgF,CAC1J,CAAC;QACJ,CAAC;QAED,IAAI,OAAO,CAAC;QACZ,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,EAAE,CAAC,GAAG,CACpB,aAAa,QAAQ,iEAAiE,EACtF,EAAE,KAAK,EAAE,SAAS,EAAE,CACrB,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,sEAAsE;YACtE,oEAAoE;YACpE,uEAAuE;YACvE,kEAAkE;YAClE,sEAAsE;YACtE,+DAA+D;YAC/D,sEAAsE;YACtE,iCAAiC;YACjC,MAAM,IAAI,GAAI,GAAgC,EAAE,IAAI,IAAI,EAAE,CAAC;YAC3D,IAAI,IAAI,KAAK,mDAAmD,IAAI,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;gBACnG,MAAM,aAAa,GAAI,SAAqC,CAAC,SAAS,CAAC;gBACvE,MAAM,UAAU,GAAI,SAAqC,CAAC,MAAM,CAAC;gBACjE,MAAM,SAAS,GAAG,OAAO,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;gBAC5F,MAAM,SAAS,GAAG,OAAO,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;gBACtF,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,2EAA2E,SAAS,WAAW,SAAS,WAAW,UAAU,IAAI,CAClI,CAAC;YACJ,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;QACD,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAW,CAAC;QAC1D,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,YAAY,CAAa,CAAC;QAEpE,uEAAuE;QACvE,iEAAiE;QACjE,uEAAuE;QACvE,wEAAwE;QACxE,oEAAoE;QACpE,uEAAuE;QACvE,iEAAiE;QACjE,IAAI,YAAY,GAAG,CAAC,CAAC;QACrB,KAAK,MAAM,GAAG,IAAI,aAAa,EAAE,CAAC;YAChC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACxC,MAAM,CAAC,GACL,GAAG,CAAC,SAAS,KAAK,UAAU;gBAC1B,CAAC,CAAC,mFAAmF,IAAI,UAAU;gBACnG,CAAC,CAAC,mFAAmF,IAAI,UAAU,CAAC;YACxG,MAAM,CAAC,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC;YAClE,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,oBAAoB,CAAC;YAClE,IAAI,OAAO,KAAK,CAAC,EAAE,CAAC;gBAClB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,kEAAkE,QAAQ,UAAU,UAAU,YAAY,GAAG,CAAC,IAAI,aAAa,GAAG,CAAC,YAAY,IAAI,CACpJ,CAAC;gBACF,MAAM,IAAI,KAAK,CACb,0DAA0D,GAAG,CAAC,YAAY,kGAAkG,CAC7K,CAAC;YACJ,CAAC;YACD,YAAY,IAAI,OAAO,CAAC;QAC1B,CAAC;QAED,IAAI,YAAY,KAAK,aAAa,CAAC,MAAM,EAAE,CAAC;YAC1C,mEAAmE;YACnE,+DAA+D;YAC/D,+CAA+C;YAC/C,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,0DAA0D,QAAQ,UAAU,UAAU,cAAc,aAAa,CAAC,MAAM,YAAY,YAAY,IAAI,CACrJ,CAAC;YACF,MAAM,IAAI,KAAK,CACb,qCAAqC,aAAa,CAAC,MAAM,mBAAmB,YAAY,4BAA4B,CACrH,CAAC;QACJ,CAAC;QAED,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,iCAAiC,QAAQ,UAAU,YAAY,mBAAmB,SAAS,CAAC,KAAK,IAAI,SAAS,kBAAkB,SAAS,CAAC,IAAI,IAAI,SAAS,CAAC,MAAM,IAAI,SAAS,IAAI,CACpL,CAAC;QAEF,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC;IACtD,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,162 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import {
4
+ auditCypherWrite,
5
+ formatAuditLine,
6
+ type CypherWriteAuditInput,
7
+ } from "../audit.js";
8
+
9
+ const baseInput: Omit<CypherWriteAuditInput, "cypher"> = {
10
+ schema: {
11
+ labels: new Set(["Person", "Organization", "Task", "KnowledgeDocument"]),
12
+ relationshipTypes: new Set(["KNOWS", "PARTICIPANT", "REFERENCES", "MENTIONS", "PART_OF"]),
13
+ },
14
+ agentName: "database-operator",
15
+ sessionId: "session-abc",
16
+ nodesCreated: 0,
17
+ relsCreated: 0,
18
+ orphanIds: [],
19
+ };
20
+
21
+ test("audit: clean write with provenance + known types yields zero warnings", () => {
22
+ const cypher = `
23
+ MATCH (p:Person {name: $name}), (o:Organization {name: $org})
24
+ MERGE (p)-[r:KNOWS]->(o)
25
+ SET r.createdAt = datetime(),
26
+ r.createdByAgent = $agent,
27
+ r.createdByTool = 'graph-cypher-write',
28
+ r.createdBySession = $sessionId
29
+ `;
30
+ const warnings = auditCypherWrite({ ...baseInput, cypher, relsCreated: 1 });
31
+ assert.deepEqual(warnings, []);
32
+ });
33
+
34
+ test("audit: unknown edge type yields unknown-type-warning", () => {
35
+ const cypher = "MATCH (a:Person), (b:Person) CREATE (a)-[:HAS_KNOWN]->(b)";
36
+ const warnings = auditCypherWrite({ ...baseInput, cypher, relsCreated: 1 });
37
+ const w = warnings.find((x) => x.kind === "unknown-type-warning");
38
+ assert.ok(w, "expected unknown-type-warning");
39
+ assert.equal(w.kind, "unknown-type-warning");
40
+ if (w.kind === "unknown-type-warning") {
41
+ assert.equal(w.type, "HAS_KNOWN");
42
+ }
43
+ });
44
+
45
+ test("audit: known APOC edge in YIELD does not trip unknown-type", () => {
46
+ const cypher =
47
+ "MATCH (a:Person), (b:Person) WHERE a.email = b.email AND id(a) < id(b) WITH a, b CALL apoc.refactor.mergeNodes([a, b]) YIELD node RETURN node";
48
+ const warnings = auditCypherWrite({ ...baseInput, cypher });
49
+ const unknownTypeWarn = warnings.find((x) => x.kind === "unknown-type-warning");
50
+ assert.equal(unknownTypeWarn, undefined);
51
+ });
52
+
53
+ test("audit: CREATE with no provenance stamps yields missing-provenance-warning", () => {
54
+ const cypher = "CREATE (n:Person {name: 'Foo'})";
55
+ const warnings = auditCypherWrite({ ...baseInput, cypher, nodesCreated: 1 });
56
+ const w = warnings.find((x) => x.kind === "missing-provenance-warning");
57
+ assert.ok(w, "expected missing-provenance-warning");
58
+ if (w.kind === "missing-provenance-warning") {
59
+ assert.equal(w.created, 1);
60
+ assert.equal(w.stamped, 0);
61
+ }
62
+ });
63
+
64
+ test("audit: MERGE with createdBy* stamps does not trip missing-provenance", () => {
65
+ const cypher =
66
+ "MERGE (n:Person {email: $email}) ON CREATE SET n.createdByAgent = $agent, n.createdBySession = $sid";
67
+ const warnings = auditCypherWrite({ ...baseInput, cypher, nodesCreated: 1 });
68
+ const w = warnings.find((x) => x.kind === "missing-provenance-warning");
69
+ assert.equal(w, undefined);
70
+ });
71
+
72
+ test("audit: provenance check ignores stamps inside string literals", () => {
73
+ const cypher =
74
+ "CREATE (n:Person {bio: 'I have createdByAgent in my bio'})";
75
+ const warnings = auditCypherWrite({ ...baseInput, cypher, nodesCreated: 1 });
76
+ const w = warnings.find((x) => x.kind === "missing-provenance-warning");
77
+ assert.ok(w, "expected missing-provenance-warning despite stamp-like substring in literal");
78
+ });
79
+
80
+ test("audit: idempotent MERGE that matched existing node (nodesCreated=0) emits no missing-provenance-warning", () => {
81
+ // Reviewer-flagged false-positive case. MERGE (n:Person {email}) without
82
+ // ON CREATE SET stamps — static count = 1, stamps = 0. If the node
83
+ // already existed, upstream reports nodesCreated=0 and no provenance was
84
+ // needed; the audit must not warn.
85
+ const cypher = "MERGE (n:Person {email: $email})";
86
+ const warnings = auditCypherWrite({ ...baseInput, cypher, nodesCreated: 0 });
87
+ const w = warnings.find((x) => x.kind === "missing-provenance-warning");
88
+ assert.equal(w, undefined, "must not warn when nodesCreated=0");
89
+ });
90
+
91
+ test("audit: orphanIds populated yields orphan-warning", () => {
92
+ const warnings = auditCypherWrite({
93
+ ...baseInput,
94
+ cypher: "CREATE (n:Person)",
95
+ nodesCreated: 1,
96
+ orphanIds: ["4:abc:1", "4:abc:2"],
97
+ });
98
+ const w = warnings.find((x) => x.kind === "orphan-warning");
99
+ assert.ok(w, "expected orphan-warning");
100
+ if (w.kind === "orphan-warning") {
101
+ assert.deepEqual(w.orphanIds, ["4:abc:1", "4:abc:2"]);
102
+ }
103
+ });
104
+
105
+ test("audit: empty orphanIds emits no orphan-warning", () => {
106
+ const warnings = auditCypherWrite({
107
+ ...baseInput,
108
+ cypher: "MATCH (a:Person), (b:Person) CREATE (a)-[:KNOWS]->(b)",
109
+ relsCreated: 1,
110
+ orphanIds: [],
111
+ });
112
+ const w = warnings.find((x) => x.kind === "orphan-warning");
113
+ assert.equal(w, undefined);
114
+ });
115
+
116
+ test("formatAuditLine: accepted line includes counters + provenance", () => {
117
+ const line = formatAuditLine({
118
+ kind: "accepted",
119
+ cypherPrefix: "MATCH (a:Person) CREATE",
120
+ nodesCreated: 1,
121
+ relsCreated: 4,
122
+ agentName: "database-operator",
123
+ sessionId: "abc-123",
124
+ });
125
+ assert.match(line, /^\[graph-cypher-write\] accepted /);
126
+ assert.match(line, /nodesCreated=1/);
127
+ assert.match(line, /relsCreated=4/);
128
+ assert.match(line, /agentName=database-operator/);
129
+ assert.match(line, /sessionId=abc-123/);
130
+ });
131
+
132
+ test("formatAuditLine: orphan-warning line lists orphanIds", () => {
133
+ const line = formatAuditLine({
134
+ kind: "orphan-warning",
135
+ cypherPrefix: "CREATE (n:Person)",
136
+ orphanIds: ["4:abc:1", "4:abc:2"],
137
+ });
138
+ assert.match(line, /^\[graph-cypher-write\] orphan-warning /);
139
+ assert.match(line, /orphanIds=4:abc:1,4:abc:2/);
140
+ });
141
+
142
+ test("formatAuditLine: unknown-type-warning names the type", () => {
143
+ const line = formatAuditLine({
144
+ kind: "unknown-type-warning",
145
+ cypherPrefix: "CREATE (a)-[:HAS_KNOWN]->(b)",
146
+ type: "HAS_KNOWN",
147
+ });
148
+ assert.match(line, /^\[graph-cypher-write\] unknown-type-warning /);
149
+ assert.match(line, /type=HAS_KNOWN/);
150
+ });
151
+
152
+ test("formatAuditLine: missing-provenance-warning shows created vs stamped counts", () => {
153
+ const line = formatAuditLine({
154
+ kind: "missing-provenance-warning",
155
+ cypherPrefix: "CREATE (n:Person)",
156
+ created: 3,
157
+ stamped: 1,
158
+ });
159
+ assert.match(line, /^\[graph-cypher-write\] missing-provenance-warning /);
160
+ assert.match(line, /created=3/);
161
+ assert.match(line, /stamped=1/);
162
+ });