@rubytech/create-maxy 1.0.875 → 1.0.877
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 +51 -37
- package/package.json +1 -1
- package/payload/platform/lib/graph-search/src/__tests__/brochure-threshold.test.ts +136 -0
- package/payload/platform/neo4j/edge-annotations.json +11 -3
- package/payload/platform/plugins/admin/hooks/archive-ingest-surface-gate.sh +11 -5
- package/payload/platform/plugins/admin/mcp/dist/index.js +19 -2
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/skills/onboarding/SKILL.md +6 -2
- package/payload/platform/plugins/cloudflare/scripts/setup-tunnel.sh +88 -9
- package/payload/platform/plugins/cloudflare/skills/setup-tunnel/SKILL.md +1 -1
- package/payload/platform/plugins/docs/references/admin-session.md +80 -0
- package/payload/platform/plugins/docs/references/deployment.md +1 -1
- package/payload/platform/plugins/docs/references/platform.md +1 -1
- package/payload/platform/plugins/docs/references/plugins-guide.md +3 -0
- package/payload/platform/plugins/memory/PLUGIN.md +4 -1
- package/payload/platform/plugins/memory/mcp/dist/index.js +127 -0
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.js +97 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-derive-insights.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-enrich-rejection.test.d.ts +2 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-enrich-rejection.test.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-enrich-rejection.test.js +184 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/conversation-archive-enrich-rejection.test.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.d.ts +89 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.js +542 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-derive-insights.js.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-enrich-rejection.d.ts +41 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-enrich-rejection.d.ts.map +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-enrich-rejection.js +116 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/conversation-archive-enrich-rejection.js.map +1 -0
- package/payload/platform/plugins/memory/skills/conversation-archive-enrich/SKILL.md +159 -0
- package/payload/platform/templates/specialists/agents/database-operator.md +3 -2
- package/payload/server/chunk-GOZP57CX.js +1373 -0
- package/payload/server/chunk-I4AQMEJA.js +11265 -0
- package/payload/server/chunk-LU6TUP3E.js +2169 -0
- package/payload/server/chunk-RRVBWC66.js +667 -0
- package/payload/server/client-pool-VYDOIFG7.js +34 -0
- package/payload/server/cloudflare-task-tracker-M7APAYEF.js +20 -0
- package/payload/server/maxy-edge.js +6 -5
- package/payload/server/public/assets/{Checkbox-CiL1E1ss.js → Checkbox-m3yLBLrp.js} +1 -1
- package/payload/server/public/assets/{admin-DJodbJ_C.js → admin-DEm0CCga.js} +7 -7
- package/payload/server/public/assets/data-BkbjVYwP.js +1 -0
- package/payload/server/public/assets/graph-Cic-rDfg.js +1 -0
- package/payload/server/public/assets/{graph-labels-B3yLO-UT.js → graph-labels-C13OVh5P.js} +1 -1
- package/payload/server/public/assets/{jsx-runtime-CstxPbUG.css → jsx-runtime-DJwgVAMg.css} +1 -1
- package/payload/server/public/assets/page-BLRjaAoU.js +50 -0
- package/payload/server/public/assets/{page-C_FMEZcW.js → page-p-Fj8Guk.js} +1 -1
- package/payload/server/public/assets/{public-nLRQYsaG.js → public-4udeVi_T.js} +1 -1
- package/payload/server/public/assets/{useVoiceRecorder-PQASU6Eq.js → useVoiceRecorder-JwwBC5pd.js} +1 -1
- package/payload/server/public/data.html +5 -5
- package/payload/server/public/graph.html +6 -6
- package/payload/server/public/index.html +8 -8
- package/payload/server/public/public.html +5 -5
- package/payload/server/server.js +54 -24
- package/payload/server/public/assets/data-CXF4YQ99.js +0 -1
- package/payload/server/public/assets/graph-CtSKqC3V.js +0 -1
- package/payload/server/public/assets/page-XR25fnQN.js +0 -50
- /package/payload/server/public/assets/{jsx-runtime-BRxv9PO7.js → jsx-runtime-Bd3TJ8Bg.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -1231,15 +1231,16 @@ function setupDedicatedNeo4j() {
|
|
|
1231
1231
|
const logDir = `/var/log/neo4j-${brandSuffix}`;
|
|
1232
1232
|
const serviceName = `neo4j-${brandSuffix}`;
|
|
1233
1233
|
const httpPort = NEO4J_PORT - 213; // Preserve standard 7687/7474 offset
|
|
1234
|
-
//
|
|
1235
|
-
//
|
|
1236
|
-
//
|
|
1237
|
-
//
|
|
1238
|
-
//
|
|
1234
|
+
// Per-brand state (sed, mkdir, chown, unit-write) is idempotent and runs on
|
|
1235
|
+
// every install. Only the base-config copy and initial-password rotation are
|
|
1236
|
+
// gated to first install — Task 787 established that state remediation must
|
|
1237
|
+
// run every install; Task 979 extended the same principle to the conf and
|
|
1238
|
+
// unit emission so a half-installed host (broken unit missing NEO4J_HOME)
|
|
1239
|
+
// recovers on retry without an out-of-band manual reset.
|
|
1239
1240
|
const confExists = spawnSync("test", ["-f", `${confDir}/neo4j.conf`], { stdio: "pipe" }).status === 0;
|
|
1240
1241
|
if (confExists) {
|
|
1241
|
-
console.log(` Dedicated Neo4j instance for ${BRAND.productName} already configured at ${confDir}`);
|
|
1242
|
-
logFile(` Neo4j dedicated: existing config at ${confDir},
|
|
1242
|
+
console.log(` Dedicated Neo4j instance for ${BRAND.productName} already configured at ${confDir} — re-applying per-brand state`);
|
|
1243
|
+
logFile(` Neo4j dedicated: existing config at ${confDir}, re-applying sed/mkdir/chown/unit on every install`);
|
|
1243
1244
|
}
|
|
1244
1245
|
else {
|
|
1245
1246
|
console.log(` Setting up dedicated Neo4j instance for ${BRAND.productName} on bolt://localhost:${NEO4J_PORT}...`);
|
|
@@ -1252,31 +1253,41 @@ function setupDedicatedNeo4j() {
|
|
|
1252
1253
|
if (!existsSync("/etc/neo4j/neo4j.conf")) {
|
|
1253
1254
|
throw new Error("/etc/neo4j/neo4j.conf not found. Cannot create dedicated instance without base config.");
|
|
1254
1255
|
}
|
|
1255
|
-
//
|
|
1256
|
+
// Copy base config (first install only — sed runs unconditionally below)
|
|
1256
1257
|
console.log(" [privileged] cp -r");
|
|
1257
1258
|
shell("cp", ["-r", "/etc/neo4j", confDir], { sudo: true });
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1259
|
+
}
|
|
1260
|
+
// Idempotent per-brand state — runs on every install (Task 979).
|
|
1261
|
+
// sed `s|^#?key=.*|key=value|` no-ops when the file already has the target
|
|
1262
|
+
// value; mkdir -p, chown -R, and unit-write are idempotent by definition.
|
|
1263
|
+
// NEO4J_HOME=${dataDir} makes server.directories.run resolve per-brand to
|
|
1264
|
+
// ${dataDir}/run, so the launcher's pre-flight does not collide with the
|
|
1265
|
+
// system unit's /var/lib/neo4j/run/neo4j.pid (Task 979 root cause). Plugins
|
|
1266
|
+
// and import are sed-overridden because the apt base conf pins them to
|
|
1267
|
+
// absolute /var/lib/neo4j/plugins and /var/lib/neo4j/import (shared).
|
|
1268
|
+
console.log(" [privileged] sed -i");
|
|
1269
|
+
shell("sed", ["-i", `s/^#\\?server\\.bolt\\.listen_address=.*/server.bolt.listen_address=:${NEO4J_PORT}/`, `${confDir}/neo4j.conf`], { sudo: true });
|
|
1270
|
+
console.log(" [privileged] sed -i");
|
|
1271
|
+
shell("sed", ["-i", `s/^#\\?server\\.http\\.listen_address=.*/server.http.listen_address=:${httpPort}/`, `${confDir}/neo4j.conf`], { sudo: true });
|
|
1272
|
+
console.log(" [privileged] sed -i");
|
|
1273
|
+
shell("sed", ["-i", `s|^#\\?server\\.directories\\.data=.*|server.directories.data=${dataDir}/data|`, `${confDir}/neo4j.conf`], { sudo: true });
|
|
1274
|
+
console.log(" [privileged] sed -i");
|
|
1275
|
+
shell("sed", ["-i", `s|^#\\?server\\.directories\\.logs=.*|server.directories.logs=${logDir}|`, `${confDir}/neo4j.conf`], { sudo: true });
|
|
1276
|
+
console.log(" [privileged] sed -i");
|
|
1277
|
+
shell("sed", ["-i", `s|^#\\?server\\.directories\\.plugins=.*|server.directories.plugins=${dataDir}/plugins|`, `${confDir}/neo4j.conf`], { sudo: true });
|
|
1278
|
+
console.log(" [privileged] sed -i");
|
|
1279
|
+
shell("sed", ["-i", `s|^#\\?server\\.directories\\.import=.*|server.directories.import=${dataDir}/import|`, `${confDir}/neo4j.conf`], { sudo: true });
|
|
1280
|
+
// Verify config was updated — sed silently no-ops if the key format changed
|
|
1281
|
+
const confContent = spawnSync("grep", [`server.bolt.listen_address=:${NEO4J_PORT}`, `${confDir}/neo4j.conf`], { stdio: "pipe" });
|
|
1282
|
+
if (confContent.status !== 0) {
|
|
1283
|
+
console.error(` WARNING: neo4j.conf may not have been updated correctly — bolt port ${NEO4J_PORT} not found in config`);
|
|
1284
|
+
logFile(` WARNING: sed verification failed — bolt port ${NEO4J_PORT} not found in ${confDir}/neo4j.conf`);
|
|
1285
|
+
}
|
|
1286
|
+
console.log(" [privileged] mkdir -p");
|
|
1287
|
+
shell("mkdir", ["-p", `${dataDir}/data`, `${dataDir}/plugins`, `${dataDir}/import`, logDir], { sudo: true });
|
|
1288
|
+
console.log(" [privileged] chown -R");
|
|
1289
|
+
shell("chown", ["-R", "neo4j:neo4j", dataDir, logDir, confDir], { sudo: true });
|
|
1290
|
+
const serviceContent = `[Unit]
|
|
1280
1291
|
Description=Neo4j Graph Database (${BRAND.productName})
|
|
1281
1292
|
After=network-online.target
|
|
1282
1293
|
Wants=network-online.target
|
|
@@ -1286,18 +1297,21 @@ ExecStart=/usr/bin/neo4j console
|
|
|
1286
1297
|
Restart=on-failure
|
|
1287
1298
|
User=neo4j
|
|
1288
1299
|
Group=neo4j
|
|
1289
|
-
Environment=NEO4J_CONF=${confDir}
|
|
1300
|
+
Environment="NEO4J_CONF=${confDir}" "NEO4J_HOME=${dataDir}"
|
|
1290
1301
|
LimitNOFILE=60000
|
|
1291
1302
|
|
|
1292
1303
|
[Install]
|
|
1293
1304
|
WantedBy=multi-user.target
|
|
1294
1305
|
`;
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1306
|
+
const tmpServicePath = `/tmp/${serviceName}.service`;
|
|
1307
|
+
writeFileSync(tmpServicePath, serviceContent);
|
|
1308
|
+
console.log(" [privileged] cp");
|
|
1309
|
+
shell("cp", [tmpServicePath, `/etc/systemd/system/${serviceName}.service`], { sudo: true });
|
|
1310
|
+
spawnSync("rm", ["-f", tmpServicePath]);
|
|
1311
|
+
logFile(` [neo4j] dedicated unit env: NEO4J_CONF=${confDir} NEO4J_HOME=${dataDir}`);
|
|
1312
|
+
if (!confExists) {
|
|
1313
|
+
// Set initial password before first start (first install only — rotation
|
|
1314
|
+
// on retry would brick an existing DB whose password is already stored).
|
|
1301
1315
|
const password = randomBytes(24).toString("base64url");
|
|
1302
1316
|
const persistDir = resolve(process.env.HOME ?? "/root", BRAND.configDir);
|
|
1303
1317
|
mkdirSync(persistDir, { recursive: true });
|
package/package.json
CHANGED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
+
import { hybrid, clearIndexCache } from "../index.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Task 978 — brochure-class calibration fixture.
|
|
6
|
+
*
|
|
7
|
+
* Pre-Task-978 the route's `DEFAULT_VECTOR_THRESHOLD` was 0.72, calibrated
|
|
8
|
+
* against the `savage` query (every score < 0.70, only the relevant row
|
|
9
|
+
* passes the BM25 carve-out). `brochure` is a different shape: keepers
|
|
10
|
+
* cluster in [0.8473, 0.8623], noise in [0.7159, 0.8143]. At 0.72 the
|
|
11
|
+
* floor caught only the lone outlier below 0.72; all 11 in-cluster rows
|
|
12
|
+
* rendered. Task 978 raised the default to 0.82, the midpoint of the
|
|
13
|
+
* 0.033-wide gap between brochure-keepers and brochure-noise.
|
|
14
|
+
*
|
|
15
|
+
* This fixture pins both ends of that calibration so a future drop back
|
|
16
|
+
* toward 0.72 (or a push past 0.85 that would clip the lowest keeper)
|
|
17
|
+
* fails the test instead of silently regressing /graph search quality.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
interface StubRun {
|
|
21
|
+
match: (query: string) => boolean;
|
|
22
|
+
records: Array<Record<string, unknown>>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function record(fields: Record<string, unknown>) {
|
|
26
|
+
return { get: (k: string) => fields[k] };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function makeStubSession(scripted: StubRun[]) {
|
|
30
|
+
return {
|
|
31
|
+
run(query: string) {
|
|
32
|
+
const hit = scripted.find((s) => s.match(query));
|
|
33
|
+
if (!hit) return Promise.resolve({ records: [] });
|
|
34
|
+
return Promise.resolve({ records: hit.records.map(record) });
|
|
35
|
+
},
|
|
36
|
+
} as unknown as import("neo4j-driver").Session;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Real Agent laptop 2026-05-12 snapshot, `brochure` against
|
|
40
|
+
// project_embedding for account b3b638d9. BM25 = 0 on every row.
|
|
41
|
+
const BROCHURE_VECTOR_ROWS = [
|
|
42
|
+
{ nodeId: "k1", name: "Brochure Production — Alex Pelosi Buchanan", score: 0.8623 },
|
|
43
|
+
{ nodeId: "k2", name: "Brochure Production — Ross Margetts", score: 0.8523 },
|
|
44
|
+
{ nodeId: "k3", name: "Brochure Production — Donna Vincent", score: 0.8521 },
|
|
45
|
+
{ nodeId: "k4", name: "Brochure Production — Paul Leslie", score: 0.8509 },
|
|
46
|
+
{ nodeId: "k5", name: "Brochure Production — John Savage", score: 0.8474 },
|
|
47
|
+
{ nodeId: "k6", name: "Brochure Production — Muvin", score: 0.8473 },
|
|
48
|
+
{ nodeId: "n1", name: "Real Agent Product Development", score: 0.8143 },
|
|
49
|
+
{ nodeId: "n2", name: "Dan McLeod Partnership", score: 0.7498 },
|
|
50
|
+
{ nodeId: "n3", name: "Real Agent Lettings — New Vertical", score: 0.7498 },
|
|
51
|
+
{ nodeId: "n4", name: "Real Agent Lettings", score: 0.7487 },
|
|
52
|
+
{ nodeId: "n5", name: "Fundraising & Finance", score: 0.7323 },
|
|
53
|
+
{ nodeId: "n6", name: "Muvin Pilot", score: 0.7240 },
|
|
54
|
+
{ nodeId: "n7", name: "Go-to-Market & Events", score: 0.7159 },
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
function makeBrochureSession() {
|
|
58
|
+
return makeStubSession([
|
|
59
|
+
{
|
|
60
|
+
match: (q) => q.includes("SHOW INDEXES"),
|
|
61
|
+
records: [{ name: "project_embedding", labelsOrTypes: ["Project"] }],
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
match: (q) => q.includes("db.index.vector.queryNodes"),
|
|
65
|
+
records: BROCHURE_VECTOR_ROWS.map((r) => ({
|
|
66
|
+
nodeId: r.nodeId,
|
|
67
|
+
nodeLabels: ["Project"],
|
|
68
|
+
node: { properties: { name: r.name } },
|
|
69
|
+
score: r.score,
|
|
70
|
+
})),
|
|
71
|
+
},
|
|
72
|
+
// BM25 returns nothing — no `brochure` token literal in any name.
|
|
73
|
+
{ match: (q) => q.includes("db.index.fulltext.queryNodes"), records: [] },
|
|
74
|
+
]);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
beforeEach(() => {
|
|
78
|
+
clearIndexCache();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe("brochure-class threshold calibration (Task 978)", () => {
|
|
82
|
+
it("at threshold 0.72 (pre-fix) renders 12 rows — the bug shape", async () => {
|
|
83
|
+
const res = await hybrid(makeBrochureSession(), async () => [0.1], {
|
|
84
|
+
query: "brochure",
|
|
85
|
+
accountId: "acc-1",
|
|
86
|
+
limit: 100,
|
|
87
|
+
labels: ["Project"],
|
|
88
|
+
expandHops: 0,
|
|
89
|
+
vectorThreshold: 0.72,
|
|
90
|
+
});
|
|
91
|
+
// 13 rows merged, lowest (0.7159) below 0.72, 12 rendered.
|
|
92
|
+
expect(res.rawMerged).toBe(13);
|
|
93
|
+
expect(res.results).toHaveLength(12);
|
|
94
|
+
expect(res.suppressed).toBe(1);
|
|
95
|
+
expect(res.bm25Bypass).toBe(0);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it("at threshold 0.82 (post-fix) renders the 6 keepers exactly", async () => {
|
|
99
|
+
const res = await hybrid(makeBrochureSession(), async () => [0.1], {
|
|
100
|
+
query: "brochure",
|
|
101
|
+
accountId: "acc-1",
|
|
102
|
+
limit: 100,
|
|
103
|
+
labels: ["Project"],
|
|
104
|
+
expandHops: 0,
|
|
105
|
+
vectorThreshold: 0.82,
|
|
106
|
+
});
|
|
107
|
+
expect(res.rawMerged).toBe(13);
|
|
108
|
+
expect(res.results).toHaveLength(6);
|
|
109
|
+
expect(res.suppressed).toBe(7);
|
|
110
|
+
expect(res.bm25Bypass).toBe(0);
|
|
111
|
+
expect(res.results.map((r) => r.properties.name as string).sort()).toEqual([
|
|
112
|
+
"Brochure Production — Alex Pelosi Buchanan",
|
|
113
|
+
"Brochure Production — Donna Vincent",
|
|
114
|
+
"Brochure Production — John Savage",
|
|
115
|
+
"Brochure Production — Muvin",
|
|
116
|
+
"Brochure Production — Paul Leslie",
|
|
117
|
+
"Brochure Production — Ross Margetts",
|
|
118
|
+
]);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("at threshold 0.85 (too tight) starts clipping keepers — regression guard", async () => {
|
|
122
|
+
const res = await hybrid(makeBrochureSession(), async () => [0.1], {
|
|
123
|
+
query: "brochure",
|
|
124
|
+
accountId: "acc-1",
|
|
125
|
+
limit: 100,
|
|
126
|
+
labels: ["Project"],
|
|
127
|
+
expandHops: 0,
|
|
128
|
+
vectorThreshold: 0.85,
|
|
129
|
+
});
|
|
130
|
+
// Two keepers (0.8473, 0.8474) fall below 0.85 — proves 0.82 sits in
|
|
131
|
+
// the gap on purpose, not by accident. Don't move the default this
|
|
132
|
+
// high without rechecking.
|
|
133
|
+
expect(res.results).toHaveLength(4);
|
|
134
|
+
expect(res.suppressed).toBe(9);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
@@ -81,8 +81,8 @@
|
|
|
81
81
|
"note": "Flat document-to-chunk (alternative to HAS_SECTION then HAS_CHUNK)."
|
|
82
82
|
},
|
|
83
83
|
"REFERENCES": {
|
|
84
|
-
"direction": "(Message|KnowledgeDocument)-[:REFERENCES]->(*)",
|
|
85
|
-
"note": "Soft reference link."
|
|
84
|
+
"direction": "(Message|KnowledgeDocument|Task)-[:REFERENCES]->(*)",
|
|
85
|
+
"note": "Soft reference link. Task 892 added `Task` as a source: derived-insight tasks created from a `:Section:Conversation` chunk record their provenance via (:Task)-[:REFERENCES]->(:Section:Conversation) with a `contentHash` merge-key for idempotent re-runs."
|
|
86
86
|
},
|
|
87
87
|
"ABOUT": {
|
|
88
88
|
"direction": "(Review|Message)-[:ABOUT]->(*)",
|
|
@@ -102,7 +102,15 @@
|
|
|
102
102
|
},
|
|
103
103
|
"OBSERVED_IN": {
|
|
104
104
|
"direction": "(*)-[:OBSERVED_IN]->(Conversation)",
|
|
105
|
-
"note": "Observation provenance."
|
|
105
|
+
"note": "Observation provenance. Task 892: `:Section:Conversation` chunks (which carry the Conversation label) are valid OBSERVED_IN targets, so (:Preference)-[:OBSERVED_IN]->(:Section:Conversation) pattern-matches this annotation."
|
|
106
|
+
},
|
|
107
|
+
"MENTIONS": {
|
|
108
|
+
"direction": "(Section|Message|KnowledgeDocument)-[:MENTIONS]->(Person|Organization)",
|
|
109
|
+
"note": "Named entity reference. Task 892 added `Section` (typically Section:Conversation) as a source so chunk-anchored insight derivation can record who a transcript chunk mentions. KnowledgeDocument-source MENTIONS is the document-ingest path; Message-source MENTIONS is reserved for future per-message extraction."
|
|
110
|
+
},
|
|
111
|
+
"RELATED_TO": {
|
|
112
|
+
"direction": "(Person|Organization)-[:RELATED_TO]->(Person|Organization)",
|
|
113
|
+
"note": "Operator-confirmed relationship between two named entities derived from a transcript chunk (Task 892). Carries `operatorConfirmed: true` plus `relationshipType` naming the specific bond (`broker`, `colleague`, `referrer`, …). Distinct from typed edges like AUTHORED_BY or PARTICIPANT — RELATED_TO is the generic surface for relationships the operator confirmed at enrich time."
|
|
106
114
|
},
|
|
107
115
|
"HAS_IDENTITY": {
|
|
108
116
|
"direction": "(Agent)-[:HAS_IDENTITY]->(KnowledgeDocument)",
|
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
|
-
# Archive-ingest surface gate (Task 855, updated by Task 891).
|
|
2
|
+
# Archive-ingest surface gate (Task 855, updated by Task 891, Task 892).
|
|
3
3
|
#
|
|
4
4
|
# Five enforcements, one script — phase decided by `hook_event_name` on stdin.
|
|
5
5
|
# Task 855 narrows the database-operator subagent's effective surface during
|
|
6
6
|
# WhatsApp archive ingestion to exactly one Bash entry
|
|
7
7
|
# (`memory/bin/conversation-archive-ingest.sh`) plus read-only neighbours, by
|
|
8
8
|
# blocking the legacy MCP deviation tools mechanically. Task 891 retired the
|
|
9
|
-
# `whatsapp-export-insight-pass` tool
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
9
|
+
# `whatsapp-export-insight-pass` tool; Task 892 reintroduces Phase 2 as
|
|
10
|
+
# `mcp__memory__conversation-archive-derive-insights` — a read-only tool that
|
|
11
|
+
# walks :Section:Conversation chunks of one named :ConversationArchive in
|
|
12
|
+
# pages and emits per-row proposals. The new tool is NOT in any BLOCK list
|
|
13
|
+
# (the gate is allow-by-default for unrecognised tools) — its writes go
|
|
14
|
+
# through the existing graph-cypher-write surface, gated by the operator per
|
|
15
|
+
# row in the conversation-archive-enrich skill. Stale references to the
|
|
16
|
+
# retired Phase 2 name (`whatsapp-export-insight-pass`) remain in the BLOCK
|
|
17
|
+
# list as a loud-denial breadcrumb for any operator-edited skill that still
|
|
18
|
+
# names them.
|
|
13
19
|
#
|
|
14
20
|
# 1. PreToolUse on the four legacy WhatsApp MCP tools — BLOCK unconditionally.
|
|
15
21
|
# The single deterministic Bash entry is the only supported path for
|
|
@@ -536,6 +536,13 @@ server.tool("onboarding-plugin-options", "Return the fully-assembled multi-selec
|
|
|
536
536
|
const meta = PLUGIN_DISPLAY[name] ?? { value: name, label: name, description: "" };
|
|
537
537
|
options.push({ ...meta, group: "Maxy" });
|
|
538
538
|
}
|
|
539
|
+
// Task 976: signal that the onboarding skill will append three Anthropic
|
|
540
|
+
// vertical entries from the claude-for-financial-services and
|
|
541
|
+
// knowledge-work-plugins marketplaces. The count is fixed because the
|
|
542
|
+
// skill prose always appends the same three (kyc-screener,
|
|
543
|
+
// meeting-prep-agent, pdf-viewer). Absence of this line post-install
|
|
544
|
+
// = the group was not surfaced (release blocker per task spec).
|
|
545
|
+
console.error(`[plugin-onboarding] group=anthropic-verticals presented=3`);
|
|
539
546
|
return { content: [{ type: "text", text: JSON.stringify(options, null, 2) }] };
|
|
540
547
|
}
|
|
541
548
|
catch (err) {
|
|
@@ -2237,11 +2244,21 @@ server.tool("onboarding-get", "Read the current onboarding state from the graph.
|
|
|
2237
2244
|
});
|
|
2238
2245
|
server.tool("onboarding-complete-step", "Mark an onboarding step as completed. Sets a completion timestamp on the step " +
|
|
2239
2246
|
"and updates currentStep to the highest completed step. Idempotent — completing " +
|
|
2240
|
-
"an already-completed step preserves its original timestamp. Step must be 1–9."
|
|
2247
|
+
"an already-completed step preserves its original timestamp. Step must be 1–9. " +
|
|
2248
|
+
"When step is 1, optionally pass acceptedAnthropicVerticals + declinedAnthropicVerticals " +
|
|
2249
|
+
"(both required together) to emit the paired counter for the [plugin-onboarding] " +
|
|
2250
|
+
"group=anthropic-verticals prefix; the skill derives both arrays from the closed set " +
|
|
2251
|
+
"{kyc-screener, meeting-prep-agent, pdf-viewer} and the user's submission.", {
|
|
2241
2252
|
step: z.number().int().min(1).max(9).describe("The onboarding step number (1–9) to mark as completed"),
|
|
2242
|
-
|
|
2253
|
+
acceptedAnthropicVerticals: z.array(z.string()).optional().describe("Step 1 only: plugin slugs the user accepted from the Anthropic verticals group. Pass together with declinedAnthropicVerticals."),
|
|
2254
|
+
declinedAnthropicVerticals: z.array(z.string()).optional().describe("Step 1 only: plugin slugs the user declined from the Anthropic verticals group. Pass together with acceptedAnthropicVerticals."),
|
|
2255
|
+
}, async ({ step, acceptedAnthropicVerticals, declinedAnthropicVerticals }) => {
|
|
2243
2256
|
try {
|
|
2244
2257
|
const state = await completeOnboardingStep(getSession, ACCOUNT_ID, step);
|
|
2258
|
+
// Task 977: closed set lives in skill prose, not server validation — a fourth vertical updates the skill.
|
|
2259
|
+
if (step === 1 && acceptedAnthropicVerticals !== undefined && declinedAnthropicVerticals !== undefined) {
|
|
2260
|
+
console.error(`[plugin-onboarding] group=anthropic-verticals accepted=${acceptedAnthropicVerticals.length} declined=${declinedAnthropicVerticals.length}`);
|
|
2261
|
+
}
|
|
2245
2262
|
return {
|
|
2246
2263
|
content: [{
|
|
2247
2264
|
type: "text",
|