@rubytech/create-maxy-code 0.1.459 → 0.1.460
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/package.json +1 -1
- package/payload/platform/lib/storage-broker/dist/__tests__/cf-exec.test.js +116 -6
- package/payload/platform/lib/storage-broker/dist/__tests__/cf-exec.test.js.map +1 -1
- package/payload/platform/lib/storage-broker/dist/__tests__/object-limits.test.js +20 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/object-limits.test.js.map +1 -1
- package/payload/platform/lib/storage-broker/dist/__tests__/resource-limits.test.d.ts +2 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/resource-limits.test.d.ts.map +1 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/resource-limits.test.js +41 -0
- package/payload/platform/lib/storage-broker/dist/__tests__/resource-limits.test.js.map +1 -0
- package/payload/platform/lib/storage-broker/dist/cf-exec.d.ts +1 -0
- package/payload/platform/lib/storage-broker/dist/cf-exec.d.ts.map +1 -1
- package/payload/platform/lib/storage-broker/dist/cf-exec.js +116 -48
- package/payload/platform/lib/storage-broker/dist/cf-exec.js.map +1 -1
- package/payload/platform/lib/storage-broker/dist/index.d.ts +1 -0
- package/payload/platform/lib/storage-broker/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/storage-broker/dist/index.js +1 -0
- package/payload/platform/lib/storage-broker/dist/index.js.map +1 -1
- package/payload/platform/lib/storage-broker/dist/object-limits.d.ts +27 -0
- package/payload/platform/lib/storage-broker/dist/object-limits.d.ts.map +1 -1
- package/payload/platform/lib/storage-broker/dist/object-limits.js +27 -7
- package/payload/platform/lib/storage-broker/dist/object-limits.js.map +1 -1
- package/payload/platform/lib/storage-broker/dist/resource-limits.d.ts +102 -0
- package/payload/platform/lib/storage-broker/dist/resource-limits.d.ts.map +1 -0
- package/payload/platform/lib/storage-broker/dist/resource-limits.js +105 -0
- package/payload/platform/lib/storage-broker/dist/resource-limits.js.map +1 -0
- package/payload/platform/lib/storage-broker/src/__tests__/cf-exec.test.ts +130 -7
- package/payload/platform/lib/storage-broker/src/__tests__/object-limits.test.ts +30 -0
- package/payload/platform/lib/storage-broker/src/__tests__/resource-limits.test.ts +49 -0
- package/payload/platform/lib/storage-broker/src/cf-exec.ts +129 -56
- package/payload/platform/lib/storage-broker/src/index.ts +1 -0
- package/payload/platform/lib/storage-broker/src/object-limits.ts +26 -6
- package/payload/platform/lib/storage-broker/src/resource-limits.ts +105 -0
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +5 -4
- package/payload/platform/plugins/cloudflare/PLUGIN.md +3 -1
- package/payload/platform/plugins/cloudflare/mcp/__tests__/auth-route.test.ts +206 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/authorize.test.ts +201 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/file-routes.test.ts +178 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/harness.test.ts +32 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/passcode.test.ts +74 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/ratelimit.test.ts +77 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/skill-contract.test.ts +46 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/template-config.test.ts +55 -0
- package/payload/platform/plugins/cloudflare/mcp/__tests__/upload-route.test.ts +255 -0
- package/payload/platform/plugins/cloudflare/mcp/package.json +5 -2
- package/payload/platform/plugins/cloudflare/references/r2-object-storage.md +46 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/SKILL.md +123 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/authorize.ts +32 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/log.ts +12 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/passcode.ts +73 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/ratelimit.ts +55 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/session.ts +85 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/types.ts +44 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/auth.ts +113 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/delete.ts +89 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/download.ts +70 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/files.ts +41 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/upload.ts +151 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/public/index.html +42 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/public/portal.css +113 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/public/portal.js +155 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/schema.sql +64 -0
- package/payload/platform/plugins/cloudflare/skills/data-portal/template/wrangler.toml +21 -0
- package/payload/platform/plugins/cloudflare/skills/site-deploy/SKILL.md +8 -0
- package/payload/platform/plugins/docs/references/outlook-guide.md +3 -3
- package/payload/platform/plugins/docs/references/voice-mirror-guide.md +1 -0
- package/payload/platform/plugins/filesystem/PLUGIN.md +0 -1
- package/payload/platform/plugins/filesystem/mcp/dist/lib/scope.d.ts +14 -10
- package/payload/platform/plugins/filesystem/mcp/dist/lib/scope.d.ts.map +1 -1
- package/payload/platform/plugins/filesystem/mcp/dist/lib/scope.js +14 -10
- package/payload/platform/plugins/filesystem/mcp/dist/lib/scope.js.map +1 -1
- package/payload/platform/plugins/outlook/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/outlook/PLUGIN.md +3 -5
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/detach.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/detach.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/detach.test.js +75 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/detach.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit-removal.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit-removal.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit-removal.test.js +187 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit-removal.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit.test.js +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit.test.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/index.js +3 -2
- package/payload/platform/plugins/outlook/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/lib/attachment-list.d.ts +43 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/attachment-list.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/attachment-list.js +65 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/attachment-list.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/detach.d.ts +65 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/detach.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/detach.js +99 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/detach.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/lib/outbound-attachments.d.ts +6 -1
- package/payload/platform/plugins/outlook/mcp/dist/lib/outbound-attachments.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/lib/outbound-attachments.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/draft-edit.d.ts +42 -22
- package/payload/platform/plugins/outlook/mcp/dist/tools/draft-edit.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/draft-edit.js +59 -23
- package/payload/platform/plugins/outlook/mcp/dist/tools/draft-edit.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-attachment.d.ts +15 -16
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-attachment.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-attachment.js +8 -41
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-attachment.js.map +1 -1
- package/payload/platform/plugins/outlook/references/auth.md +1 -1
- package/payload/platform/plugins/outlook/references/graph-surfaces.md +7 -3
- package/payload/platform/plugins/outlook/skills/outlook/SKILL.md +1 -1
- package/payload/platform/plugins/storage-broker/PLUGIN.md +1 -1
- package/payload/platform/plugins/voice-mirror/PLUGIN.md +19 -7
- package/payload/platform/plugins/voice-mirror/mcp/dist/index.d.ts.map +1 -1
- package/payload/platform/plugins/voice-mirror/mcp/dist/index.js +35 -16
- package/payload/platform/plugins/voice-mirror/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/voice-mirror/mcp/dist/lib/admin-user.d.ts +31 -0
- package/payload/platform/plugins/voice-mirror/mcp/dist/lib/admin-user.d.ts.map +1 -0
- package/payload/platform/plugins/voice-mirror/mcp/dist/lib/admin-user.js +87 -0
- package/payload/platform/plugins/voice-mirror/mcp/dist/lib/admin-user.js.map +1 -0
- package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-distil-profile.d.ts.map +1 -1
- package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-distil-profile.js +13 -3
- package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-distil-profile.js.map +1 -1
- package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-ingest-session-text.d.ts.map +1 -1
- package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-ingest-session-text.js +2 -1
- package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-ingest-session-text.js.map +1 -1
- package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-record-feedback.d.ts.map +1 -1
- package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-record-feedback.js +2 -1
- package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-record-feedback.js.map +1 -1
- package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-tag-content.d.ts +2 -2
- package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-tag-content.d.ts.map +1 -1
- package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-tag-content.js +12 -3
- package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-tag-content.js.map +1 -1
- package/payload/platform/plugins/voice-mirror/mcp/scripts/smoke.mjs +334 -0
- package/payload/platform/plugins/voice-mirror/skills/voice-mirror/SKILL.md +13 -0
- package/payload/platform/scripts/voice-author-key-audit.sh +155 -0
- package/payload/platform/services/claude-session-manager/dist/mcp-descriptor-resolve.d.ts +17 -0
- package/payload/platform/services/claude-session-manager/dist/mcp-descriptor-resolve.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/mcp-descriptor-resolve.js +88 -0
- package/payload/platform/services/claude-session-manager/dist/mcp-descriptor-resolve.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +0 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +34 -23
- package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
- package/payload/server/{chunk-Q6W4U6HL.js → chunk-JXWFVE5X.js} +83 -39
- package/payload/server/server.js +213 -38
- package/payload/server/{src-3I2RYZFB.js → src-4F37OHLK.js} +11 -1
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.d.ts +0 -2
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.js +0 -215
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.js.map +0 -1
package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-ingest-session-text.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"voice-ingest-session-text.js","sourceRoot":"","sources":["../../src/tools/voice-ingest-session-text.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAe/D;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAa;IAC9C,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC1C,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC5B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC,CAAC,gBAAgB;IAC1D,IAAI,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC;QAAE,OAAO,IAAI,CAAC,CAAC,oBAAoB;IAC5E,IAAI,OAAO,CAAC,MAAM,IAAI,IAAI;QAAE,OAAO,IAAI,CAAC,CAAC,cAAc;IACvD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,MAAoC;IAEpC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,GAAG,IAAI,EAAE,GAAG,MAAM,CAAC;IAE9D,IAAI,CAAC,SAAS,IAAI,CAAC,MAAM,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,0DAA0D,CAAC,CAAC;IAC9E,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IAED,2DAA2D;IAC3D,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,IAAI,KAAK,IAAI;YAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,yCAAyC,SAAS,IAAI,SAAS,cAAc,MAAM,oBAAoB,CACxG,CAAC;QACF,OAAO,EAAE,aAAa,EAAE,CAAC,EAAE,YAAY,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC;IAC1D,CAAC;IAED,iFAAiF;IACjF,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,IAAI,UAAU,GAAG,CAAC,CAAC;IAEnB,IAAI,CAAC;QACH,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC5B,MAAM,WAAW,GAAG,UAAU,CAAC,QAAQ,CAAC;iBACrC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC;iBACrB,MAAM,CAAC,KAAK,CAAC,CAAC;YAEjB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAC9B;;;;;;;;;;;;;4CAaoC,EACpC;gBACE,SAAS;gBACT,MAAM;gBACN,WAAW;gBACX,IAAI;gBACJ,GAAG;gBACH,SAAS;aACV,CACF,CAAC;YAEF,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAChC,yDAAyD;gBACzD,MAAM,IAAI,KAAK,CACb,
|
|
1
|
+
{"version":3,"file":"voice-ingest-session-text.js","sourceRoot":"","sources":["../../src/tools/voice-ingest-session-text.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"voice-record-feedback.d.ts","sourceRoot":"","sources":["../../src/tools/voice-record-feedback.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"voice-record-feedback.d.ts","sourceRoot":"","sources":["../../src/tools/voice-record-feedback.ts"],"names":[],"mappings":"AAoBA,OAAO,EAEL,KAAK,WAAW,EAChB,KAAK,UAAU,EAChB,MAAM,wBAAwB,CAAC;AAEhC,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,MAAM,EAAE,WAAW,CAAC;IACpB;;;;OAIG;IACH,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB;;;;;;OAMG;IACH,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,yBAAyB,GAChC,OAAO,CAAC,yBAAyB,CAAC,CAmFpC"}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import { randomUUID } from "node:crypto";
|
|
19
19
|
import { getSession } from "../lib/neo4j.js";
|
|
20
|
+
import { unknownAdminUserMessage } from "../lib/admin-user.js";
|
|
20
21
|
import { profileUserIdForScope, } from "../lib/voice-corpus.js";
|
|
21
22
|
export async function voiceRecordFeedback(params) {
|
|
22
23
|
const { accountId, userId, originalText, editedText, format, intent: rawIntent, context } = params;
|
|
@@ -80,7 +81,7 @@ export async function voiceRecordFeedback(params) {
|
|
|
80
81
|
brief: context?.brief ?? null,
|
|
81
82
|
});
|
|
82
83
|
if (result.records.length === 0) {
|
|
83
|
-
throw new Error(
|
|
84
|
+
throw new Error(unknownAdminUserMessage("voice-record-feedback", userId, accountId));
|
|
84
85
|
}
|
|
85
86
|
const hadProfile = result.records[0].get("hadProfile") === true;
|
|
86
87
|
process.stderr.write(`[voice-record-feedback] scope=${scope} userId=${profileUserId} format=${format} intent="${cappedIntent.replace(/"/g, "'")}" diffBytes=${editedText.length - originalText.length}\n`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"voice-record-feedback.js","sourceRoot":"","sources":["../../src/tools/voice-record-feedback.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EACL,qBAAqB,GAGtB,MAAM,wBAAwB,CAAC;AAuChC,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAiC;IAEjC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC;IACnG,MAAM,KAAK,GAAe,MAAM,CAAC,KAAK,IAAI,UAAU,CAAC;IACrD,MAAM,aAAa,GAAG,qBAAqB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAC3D,IAAI,CAAC,SAAS,IAAI,CAAC,MAAM,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;IAC1E,CAAC;IACD,IAAI,CAAC,YAAY,IAAI,CAAC,UAAU,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,6DAA6D,CAAC,CAAC;IACjF,CAAC;IACD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;IAC/D,CAAC;IACD,MAAM,MAAM,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACxC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,mHAAmH,CACpH,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,MAAM,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC5C,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAE1C,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;IAC7B,IAAI,CAAC;QACH,sEAAsE;QACtE,6EAA6E;QAC7E,4EAA4E;QAC5E,wEAAwE;QACxE,wEAAwE;QACxE,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAC9B;;;;;;;;;;;;;;;;;;;;oEAoB8D,EAC9D;YACE,SAAS;YACT,MAAM;YACN,aAAa;YACb,KAAK;YACL,MAAM;YACN,MAAM;YACN,YAAY;YACZ,UAAU;YACV,MAAM,EAAE,YAAY;YACpB,UAAU;YACV,KAAK,EAAE,OAAO,EAAE,KAAK,IAAI,IAAI;YAC7B,KAAK,EAAE,OAAO,EAAE,KAAK,IAAI,IAAI;SAC9B,CACF,CAAC;QACF,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CACb,
|
|
1
|
+
{"version":3,"file":"voice-record-feedback.js","sourceRoot":"","sources":["../../src/tools/voice-record-feedback.ts"],"names":[],"mappings":"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"}
|
|
@@ -4,8 +4,8 @@ export declare const AUTHORSHIP_MODES: ReadonlySet<AuthorshipMode>;
|
|
|
4
4
|
/**
|
|
5
5
|
* Resolve the corpus author for a tag write (Task 676). An explicit, non-blank
|
|
6
6
|
* `author` wins; otherwise the tagging operator's `userId`. Throws when neither
|
|
7
|
-
* is available (no `author` argument and no `
|
|
8
|
-
*
|
|
7
|
+
* is available (no `author` argument and no `USER_ID` in spawn env), so a tag
|
|
8
|
+
* can never silently land with a null author.
|
|
9
9
|
*
|
|
10
10
|
* This is why single-operator accounts get personal attribution for free: with
|
|
11
11
|
* `author` omitted, the sole operator is the implicit author, so their personal
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"voice-tag-content.d.ts","sourceRoot":"","sources":["../../src/tools/voice-tag-content.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"voice-tag-content.d.ts","sourceRoot":"","sources":["../../src/tools/voice-tag-content.ts"],"names":[],"mappings":"AAgBA,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,wBAAwB,CAAC;AAEhC,MAAM,MAAM,cAAc,GACtB,YAAY,GACZ,0BAA0B,GAC1B,0BAA0B,GAC1B,YAAY,GACZ,SAAS,CAAC;AAEd,eAAO,MAAM,gBAAgB,EAAE,WAAW,CAAC,cAAc,CAMvD,CAAC;AAEH;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,cAAc,EAAE,MAAM,GAAG,IAAI,GAC5B,MAAM,CAOR;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,cAAc,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,MAAM,EAAE,WAAW,CAAC;IACpB;;;;;OAKG;IACH,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACxC;AAED,wBAAsB,eAAe,CACnC,MAAM,EAAE,qBAAqB,GAC5B,OAAO,CAAC,qBAAqB,CAAC,CA2GhC"}
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* `:Message` rows where the operator is not in the loop (Task 471).
|
|
14
14
|
*/
|
|
15
15
|
import { getSession } from "../lib/neo4j.js";
|
|
16
|
+
import { assertAdminUserOnAccount } from "../lib/admin-user.js";
|
|
16
17
|
import { TAGGABLE_PRIMARY_LABELS, FORMAT_VALUES, } from "../lib/voice-corpus.js";
|
|
17
18
|
export const AUTHORSHIP_MODES = new Set([
|
|
18
19
|
"human-only",
|
|
@@ -24,8 +25,8 @@ export const AUTHORSHIP_MODES = new Set([
|
|
|
24
25
|
/**
|
|
25
26
|
* Resolve the corpus author for a tag write (Task 676). An explicit, non-blank
|
|
26
27
|
* `author` wins; otherwise the tagging operator's `userId`. Throws when neither
|
|
27
|
-
* is available (no `author` argument and no `
|
|
28
|
-
*
|
|
28
|
+
* is available (no `author` argument and no `USER_ID` in spawn env), so a tag
|
|
29
|
+
* can never silently land with a null author.
|
|
29
30
|
*
|
|
30
31
|
* This is why single-operator accounts get personal attribution for free: with
|
|
31
32
|
* `author` omitted, the sole operator is the implicit author, so their personal
|
|
@@ -37,7 +38,7 @@ export function resolveVoiceAuthor(author, operatorUserId) {
|
|
|
37
38
|
return explicit;
|
|
38
39
|
if (operatorUserId)
|
|
39
40
|
return operatorUserId;
|
|
40
|
-
throw new Error("voice-tag-content: author required — no
|
|
41
|
+
throw new Error("voice-tag-content: author required — no USER_ID in spawn env and no author given.");
|
|
41
42
|
}
|
|
42
43
|
export async function voiceTagContent(params) {
|
|
43
44
|
const { nodeIds, mode, accountId, format, voiceAuthor } = params;
|
|
@@ -64,6 +65,14 @@ export async function voiceTagContent(params) {
|
|
|
64
65
|
if (!voiceAuthor) {
|
|
65
66
|
throw new Error("voice-tag-content: voiceAuthor is required (resolve via resolveVoiceAuthor before calling).");
|
|
66
67
|
}
|
|
68
|
+
// Task 1701 — the stamped author must name a real :AdminUser on this account.
|
|
69
|
+
// An unvalidated stamp is dead data on arrival: personal distillation anchors
|
|
70
|
+
// on (:AdminUser {accountId, userId}) and refuses to read it back, so the
|
|
71
|
+
// corpus ends up attributed to an identity that does not exist and no profile
|
|
72
|
+
// can ever be built from it. Gating the resolved value covers the `author`
|
|
73
|
+
// argument, the `userId` argument, and the USER_ID env path in one place,
|
|
74
|
+
// because the resolved value is what actually lands on the node.
|
|
75
|
+
await assertAdminUserOnAccount("voice-tag-content", voiceAuthor, accountId);
|
|
67
76
|
const session = getSession();
|
|
68
77
|
const now = new Date().toISOString();
|
|
69
78
|
const skippedReasons = {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"voice-tag-content.js","sourceRoot":"","sources":["../../src/tools/voice-tag-content.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EACL,uBAAuB,EACvB,aAAa,GAEd,MAAM,wBAAwB,CAAC;AAShC,MAAM,CAAC,MAAM,gBAAgB,GAAgC,IAAI,GAAG,CAAC;IACnE,YAAY;IACZ,0BAA0B;IAC1B,0BAA0B;IAC1B,YAAY;IACZ,SAAS;CACV,CAAC,CAAC;AAEH;;;;;;;;;GASG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAA0B,EAC1B,cAA6B;IAE7B,MAAM,QAAQ,GAAG,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACvC,IAAI,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC9B,IAAI,cAAc;QAAE,OAAO,cAAc,CAAC;IAC1C,MAAM,IAAI,KAAK,CACb,
|
|
1
|
+
{"version":3,"file":"voice-tag-content.js","sourceRoot":"","sources":["../../src/tools/voice-tag-content.ts"],"names":[],"mappings":"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"}
|
|
@@ -33,6 +33,7 @@ import { readFileSync } from "node:fs";
|
|
|
33
33
|
import { join, dirname } from "node:path";
|
|
34
34
|
import { fileURLToPath } from "node:url";
|
|
35
35
|
import { readdirSync } from "node:fs";
|
|
36
|
+
import { spawn } from "node:child_process";
|
|
36
37
|
|
|
37
38
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
38
39
|
const TOOLS_SRC = join(__dirname, "../src/tools");
|
|
@@ -410,6 +411,339 @@ await check(
|
|
|
410
411
|
},
|
|
411
412
|
);
|
|
412
413
|
|
|
414
|
+
// ---------------------------------------------------------------------------
|
|
415
|
+
// 12. Boot identity resolution (Task 1699). The server resolves the operator
|
|
416
|
+
// from USER_ID — the variable the PTY spawn stamps and MCP children
|
|
417
|
+
// inherit. ADMIN_USER_ID was never emitted by the platform, so every boot
|
|
418
|
+
// logged userId=null and four of five tools refused.
|
|
419
|
+
//
|
|
420
|
+
// Spawning the built server is the only faithful test: userId is read at
|
|
421
|
+
// module scope, and the boot line is the contract the operator diagnoses
|
|
422
|
+
// from. An empty USER_ID (silent-compaction / recorder / public spawns
|
|
423
|
+
// legitimately stamp empty) must resolve to null, not "" — an empty string
|
|
424
|
+
// is truthy enough to reach voiceRetrieveConditioning's `userId` and would
|
|
425
|
+
// become a profile key.
|
|
426
|
+
// ---------------------------------------------------------------------------
|
|
427
|
+
console.log("\n-- boot identity --");
|
|
428
|
+
|
|
429
|
+
const DIST_ENTRY = join(__dirname, "../dist/index.js");
|
|
430
|
+
|
|
431
|
+
function bootUserId(env) {
|
|
432
|
+
return new Promise((resolve, reject) => {
|
|
433
|
+
const child = spawn(process.execPath, [DIST_ENTRY], {
|
|
434
|
+
env: { PATH: process.env.PATH, ACCOUNT_ID: "acct-smoke", ...env },
|
|
435
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
436
|
+
});
|
|
437
|
+
let stderr = "";
|
|
438
|
+
const timer = setTimeout(() => {
|
|
439
|
+
child.kill("SIGKILL");
|
|
440
|
+
reject(new Error(`no boot line within 10s; stderr=${stderr.slice(0, 300)}`));
|
|
441
|
+
}, 10_000);
|
|
442
|
+
child.stderr.on("data", (chunk) => {
|
|
443
|
+
stderr += chunk.toString();
|
|
444
|
+
const m = stderr.match(/\[voice-mirror\] boot accountId=(\S+) userId=(\S+)/);
|
|
445
|
+
if (m) {
|
|
446
|
+
clearTimeout(timer);
|
|
447
|
+
child.kill("SIGKILL");
|
|
448
|
+
resolve({ accountId: m[1], userId: m[2] });
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
child.on("error", (err) => {
|
|
452
|
+
clearTimeout(timer);
|
|
453
|
+
reject(err);
|
|
454
|
+
});
|
|
455
|
+
// A child that dies before printing the boot line would otherwise sit here
|
|
456
|
+
// until the timeout and report "no boot line", hiding the real cause.
|
|
457
|
+
child.on("exit", (code, signal) => {
|
|
458
|
+
if (signal === "SIGKILL") return; // our own kill, after a successful match
|
|
459
|
+
clearTimeout(timer);
|
|
460
|
+
reject(new Error(`exited code=${code} before boot line; stderr=${stderr.slice(0, 300)}`));
|
|
461
|
+
});
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
await check("boot resolves userId from USER_ID (the var the platform emits)", async () => {
|
|
466
|
+
const { accountId, userId } = await bootUserId({ USER_ID: "436fdf2d-cfa7-4836-9ffa-77d91cef15f9" });
|
|
467
|
+
assert.equal(accountId, "acct-smoke");
|
|
468
|
+
assert.equal(userId, "436fdf2d-cfa7-4836-9ffa-77d91cef15f9");
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
await check("boot reports userId=null when USER_ID is absent", async () => {
|
|
472
|
+
const { userId } = await bootUserId({});
|
|
473
|
+
assert.equal(userId, "null");
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
await check("boot reports userId=null when USER_ID is empty (not \"\")", async () => {
|
|
477
|
+
const { userId } = await bootUserId({ USER_ID: "" });
|
|
478
|
+
assert.equal(userId, "null");
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
await check("ADMIN_USER_ID is inert — it is not an identity source", async () => {
|
|
482
|
+
const { userId } = await bootUserId({ ADMIN_USER_ID: "should-be-ignored" });
|
|
483
|
+
assert.equal(userId, "null");
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
// ---------------------------------------------------------------------------
|
|
487
|
+
// 12. Identity gate (Task 1701).
|
|
488
|
+
// A userId / voiceAuthor that names no :AdminUser on the calling account
|
|
489
|
+
// must never become a profile key or an author stamp. The reserved org
|
|
490
|
+
// sentinel legitimately names no :AdminUser and must bypass (Tasks 676, 975).
|
|
491
|
+
// ---------------------------------------------------------------------------
|
|
492
|
+
console.log("\n-- identity gate --");
|
|
493
|
+
|
|
494
|
+
const adminUserMod = await import("../dist/lib/admin-user.js");
|
|
495
|
+
|
|
496
|
+
check("unknownAdminUserMessage names the tool, the rejected key and the account", () => {
|
|
497
|
+
const msg = adminUserMod.unknownAdminUserMessage(
|
|
498
|
+
"voice-tag-content",
|
|
499
|
+
"rob-mead",
|
|
500
|
+
"68b50910",
|
|
501
|
+
);
|
|
502
|
+
assert.ok(msg.includes("rob-mead"), `names the rejected key: ${msg}`);
|
|
503
|
+
assert.ok(msg.includes("68b50910"), `names the account: ${msg}`);
|
|
504
|
+
assert.ok(msg.includes("voice-tag-content"), `names the tool: ${msg}`);
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
check("refusal wording does not invite the caller to guess another userId", () => {
|
|
508
|
+
// The prior text told callers to supply userId explicitly. An agent with no
|
|
509
|
+
// way to learn a real id supplied a plausible-looking name instead, which is
|
|
510
|
+
// how `rob-mead` reached a live client account.
|
|
511
|
+
const msg = adminUserMod.unknownAdminUserMessage(
|
|
512
|
+
"voice-tag-content",
|
|
513
|
+
"rob-mead",
|
|
514
|
+
"68b50910",
|
|
515
|
+
);
|
|
516
|
+
assert.ok(
|
|
517
|
+
!/supply\s+\S*\s*userId|pass\s+\S*\s*userId\s+explicitly|provide\s+a\s+userId/i.test(
|
|
518
|
+
msg,
|
|
519
|
+
),
|
|
520
|
+
`must not invite the caller to supply a userId: ${msg}`,
|
|
521
|
+
);
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
check("VoiceIdentityError carries the rejected key and account", () => {
|
|
525
|
+
const err = new adminUserMod.VoiceIdentityError(
|
|
526
|
+
"voice-tag-content",
|
|
527
|
+
"rob-mead",
|
|
528
|
+
"68b50910",
|
|
529
|
+
);
|
|
530
|
+
assert.ok(err instanceof Error);
|
|
531
|
+
assert.equal(err.name, "VoiceIdentityError");
|
|
532
|
+
assert.equal(err.userId, "rob-mead");
|
|
533
|
+
assert.equal(err.accountId, "68b50910");
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
await check("ORG_USER_ID bypasses the gate without opening a session", async () => {
|
|
537
|
+
// With NEO4J_URI unset, getDriver() throws. Returning normally proves the
|
|
538
|
+
// org sentinel short-circuits before any session is opened, so an org
|
|
539
|
+
// distil/retrieve still works on a spawn with no personal identity.
|
|
540
|
+
const saved = process.env.NEO4J_URI;
|
|
541
|
+
delete process.env.NEO4J_URI;
|
|
542
|
+
try {
|
|
543
|
+
await adminUserMod.assertAdminUserOnAccount(
|
|
544
|
+
"voice-distil-profile",
|
|
545
|
+
corpusMod.ORG_USER_ID,
|
|
546
|
+
"acct-1",
|
|
547
|
+
);
|
|
548
|
+
} finally {
|
|
549
|
+
if (saved !== undefined) process.env.NEO4J_URI = saved;
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
await check("a non-org key is looked up, never passed through (fail-closed)", async () => {
|
|
554
|
+
// The converse of the bypass: a real key must reach the lookup. With
|
|
555
|
+
// NEO4J_URI unset that surfaces as a driver throw. A gate that returned
|
|
556
|
+
// normally here would be failing open on every unverified key.
|
|
557
|
+
const saved = process.env.NEO4J_URI;
|
|
558
|
+
delete process.env.NEO4J_URI;
|
|
559
|
+
try {
|
|
560
|
+
await assert.rejects(
|
|
561
|
+
() =>
|
|
562
|
+
adminUserMod.assertAdminUserOnAccount(
|
|
563
|
+
"voice-tag-content",
|
|
564
|
+
"rob-mead",
|
|
565
|
+
"68b50910",
|
|
566
|
+
),
|
|
567
|
+
/NEO4J_URI unset/,
|
|
568
|
+
"must attempt the lookup rather than silently accepting the key",
|
|
569
|
+
);
|
|
570
|
+
} finally {
|
|
571
|
+
if (saved !== undefined) process.env.NEO4J_URI = saved;
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
// ---------------------------------------------------------------------------
|
|
576
|
+
// 13. Identity-gate wiring (Task 1701).
|
|
577
|
+
// The gate's own behaviour is covered above against the helper. These are
|
|
578
|
+
// structural invariants on the call sites: a live Neo4j is needed to tell
|
|
579
|
+
// "gate refused" from "write failed" at runtime (both surface as the same
|
|
580
|
+
// driver error), so ordering and presence are asserted on the source —
|
|
581
|
+
// the same idiom as the Cypher forbidden-pattern gate above.
|
|
582
|
+
// ---------------------------------------------------------------------------
|
|
583
|
+
console.log("\n-- identity-gate wiring --");
|
|
584
|
+
|
|
585
|
+
const tagSrc = readFileSync(join(TOOLS_SRC, "voice-tag-content.ts"), "utf-8");
|
|
586
|
+
|
|
587
|
+
check("voice-tag-content gates the resolved voiceAuthor", () => {
|
|
588
|
+
assert.ok(
|
|
589
|
+
/assertAdminUserOnAccount\(/.test(tagSrc),
|
|
590
|
+
"voice-tag-content must gate voiceAuthor via assertAdminUserOnAccount",
|
|
591
|
+
);
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
check("voice-tag-content gates before it stamps voiceAuthor", () => {
|
|
595
|
+
// The stamp is the write this task exists to stop. A gate ordered after it
|
|
596
|
+
// would refuse only once the dead data was already on the node.
|
|
597
|
+
const gateAt = tagSrc.indexOf("assertAdminUserOnAccount(");
|
|
598
|
+
const writeAt = tagSrc.indexOf("n.voiceAuthor = $voiceAuthor");
|
|
599
|
+
assert.ok(gateAt > -1, "gate present");
|
|
600
|
+
assert.ok(writeAt > -1, "voiceAuthor write present");
|
|
601
|
+
assert.ok(gateAt < writeAt, "identity gate must run before the voiceAuthor write");
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
await check("voice-tag-content: empty nodeIds stays a no-op (no identity lookup)", async () => {
|
|
605
|
+
// An empty tag has nothing to attribute, so it must not reach the driver.
|
|
606
|
+
// Guards the ordering of the early return against the new gate.
|
|
607
|
+
const saved = process.env.NEO4J_URI;
|
|
608
|
+
delete process.env.NEO4J_URI;
|
|
609
|
+
try {
|
|
610
|
+
const result = await tagMod.voiceTagContent({
|
|
611
|
+
nodeIds: [],
|
|
612
|
+
mode: "human-only",
|
|
613
|
+
accountId: "acct-1",
|
|
614
|
+
format: "text",
|
|
615
|
+
voiceAuthor: "rob-mead",
|
|
616
|
+
});
|
|
617
|
+
assert.equal(result.updated, 0);
|
|
618
|
+
assert.equal(result.skipped, 0);
|
|
619
|
+
} finally {
|
|
620
|
+
if (saved !== undefined) process.env.NEO4J_URI = saved;
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
|
|
624
|
+
const distilSrc = readFileSync(join(TOOLS_SRC, "voice-distil-profile.ts"), "utf-8");
|
|
625
|
+
|
|
626
|
+
check("voice-distil-profile gates the profile key at its single entry point", () => {
|
|
627
|
+
// Gating the exported entry rather than distilForFormat covers sample, amend,
|
|
628
|
+
// amend-write and multi-format enumeration with one lookup, instead of one
|
|
629
|
+
// per format.
|
|
630
|
+
const entryAt = distilSrc.indexOf("export async function voiceDistilProfile");
|
|
631
|
+
assert.ok(entryAt > -1, "exported entry present");
|
|
632
|
+
assert.ok(
|
|
633
|
+
/assertAdminUserOnAccount\(/.test(distilSrc.slice(entryAt)),
|
|
634
|
+
"the exported entry must gate the profile key",
|
|
635
|
+
);
|
|
636
|
+
});
|
|
637
|
+
|
|
638
|
+
check("voice-distil-profile keeps both structural write anchors", () => {
|
|
639
|
+
// The anchor MATCH binds `anchor` for MERGE (anchor)-[:HAS_VOICE_PROFILE]->(p).
|
|
640
|
+
// The entry gate is additive; removing these would drop the edge.
|
|
641
|
+
const anchors = distilSrc.match(
|
|
642
|
+
/MATCH \(anchor:AdminUser \{accountId: \$accountId, userId: \$profileUserId\}\)/g,
|
|
643
|
+
);
|
|
644
|
+
assert.equal(anchors?.length, 2, "both write-path anchors retained");
|
|
645
|
+
});
|
|
646
|
+
|
|
647
|
+
const retrieveSrc = readFileSync(
|
|
648
|
+
join(TOOLS_SRC, "voice-retrieve-conditioning.ts"),
|
|
649
|
+
"utf-8",
|
|
650
|
+
);
|
|
651
|
+
|
|
652
|
+
check("voice-retrieve-conditioning does NOT gate identity (it writes nothing)", () => {
|
|
653
|
+
// Deliberately ungated. Retrieve mutates nothing, so it sits outside the
|
|
654
|
+
// "validate before any write keyed on it" rule, and gating it broke the
|
|
655
|
+
// personal->org fallback below. Invented keys are stopped at
|
|
656
|
+
// voice-tag-content, which is the only path that puts one in the graph.
|
|
657
|
+
assert.ok(
|
|
658
|
+
!/assertAdminUserOnAccount\(/.test(retrieveSrc),
|
|
659
|
+
"retrieve must not gate identity: it regresses the personal->org fallback",
|
|
660
|
+
);
|
|
661
|
+
});
|
|
662
|
+
|
|
663
|
+
check("voice-retrieve-conditioning keeps the personal->org fallback", () => {
|
|
664
|
+
// The regression guard. A personal lookup with no data must borrow the org
|
|
665
|
+
// (house) voice, not degrade to the default register. A house operator
|
|
666
|
+
// scoped into a client sub-account has no :AdminUser on that account, so
|
|
667
|
+
// this fallback is the only thing that puts the client's house card into
|
|
668
|
+
// their drafts.
|
|
669
|
+
assert.ok(
|
|
670
|
+
/const org = await orgLookup\(\);/.test(retrieveSrc),
|
|
671
|
+
"personal miss must fall back to orgLookup()",
|
|
672
|
+
);
|
|
673
|
+
assert.ok(
|
|
674
|
+
/status = hasData\(org\) \? "fallback-org" : "no-data";/.test(retrieveSrc),
|
|
675
|
+
"personal miss must report fallback-org when the org voice resolves",
|
|
676
|
+
);
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
check("voice-retrieve-conditioning never sets isError", () => {
|
|
680
|
+
// Match the property assignment, not the word: prose about isError is fine,
|
|
681
|
+
// setting it is not.
|
|
682
|
+
assert.ok(
|
|
683
|
+
!/isError\s*[:=]/.test(retrieveSrc),
|
|
684
|
+
"retrieve degrades via status, never isError",
|
|
685
|
+
);
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
// ---------------------------------------------------------------------------
|
|
689
|
+
// 14. One refusal wording across every gate (Task 1701).
|
|
690
|
+
// record-feedback and ingest already enforced the rule via an inline
|
|
691
|
+
// MATCH (a:AdminUser {accountId, userId}). That match is structural — it
|
|
692
|
+
// binds `a` for the AUTHORED edge — so it stays exactly where it is and
|
|
693
|
+
// only the wording is shared.
|
|
694
|
+
// ---------------------------------------------------------------------------
|
|
695
|
+
console.log("\n-- shared refusal wording --");
|
|
696
|
+
|
|
697
|
+
for (const file of [
|
|
698
|
+
"voice-record-feedback.ts",
|
|
699
|
+
"voice-ingest-session-text.ts",
|
|
700
|
+
"voice-distil-profile.ts",
|
|
701
|
+
]) {
|
|
702
|
+
const src = readFileSync(join(TOOLS_SRC, file), "utf-8");
|
|
703
|
+
check(`${file} refuses with the shared wording`, () => {
|
|
704
|
+
assert.ok(
|
|
705
|
+
/unknownAdminUserMessage\(/.test(src),
|
|
706
|
+
`${file} must use the shared refusal wording`,
|
|
707
|
+
);
|
|
708
|
+
});
|
|
709
|
+
check(`${file} drops the un-actionable onboarding wording`, () => {
|
|
710
|
+
// "Onboarding must promote the operator to :AdminUser" names a remedy the
|
|
711
|
+
// caller cannot perform and never names the account.
|
|
712
|
+
assert.ok(
|
|
713
|
+
!/Onboarding must promote the operator/.test(src),
|
|
714
|
+
`${file} still carries the old onboarding wording`,
|
|
715
|
+
);
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
check("record-feedback keeps its structural AdminUser match (binds the AUTHORED edge)", () => {
|
|
720
|
+
const fb = readFileSync(join(TOOLS_SRC, "voice-record-feedback.ts"), "utf-8");
|
|
721
|
+
assert.ok(
|
|
722
|
+
/MATCH \(a:AdminUser \{accountId: \$accountId, userId: \$userId\}\)/.test(fb),
|
|
723
|
+
"structural match removed — the AUTHORED edge would be dropped",
|
|
724
|
+
);
|
|
725
|
+
assert.ok(/CREATE \(a\)-\[:AUTHORED\]->\(e\)/.test(fb), "AUTHORED edge retained");
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
check("ingest keeps its structural AdminUser match (binds the AUTHORED edge)", () => {
|
|
729
|
+
const ing = readFileSync(join(TOOLS_SRC, "voice-ingest-session-text.ts"), "utf-8");
|
|
730
|
+
assert.ok(
|
|
731
|
+
/MATCH \(a:AdminUser \{accountId: \$accountId, userId: \$userId\}\)/.test(ing),
|
|
732
|
+
"structural match removed — the AUTHORED edge would be dropped",
|
|
733
|
+
);
|
|
734
|
+
assert.ok(/MERGE \(a\)-\[:AUTHORED\]->\(n\)/.test(ing), "AUTHORED edge retained");
|
|
735
|
+
});
|
|
736
|
+
|
|
737
|
+
check("distil keeps the :LocalBusiness wording for the org branch", () => {
|
|
738
|
+
// An org distil failing on a missing business node is a different failure
|
|
739
|
+
// from an unknown personal key; it must not collapse into the shared wording.
|
|
740
|
+
const src = readFileSync(join(TOOLS_SRC, "voice-distil-profile.ts"), "utf-8");
|
|
741
|
+
assert.ok(
|
|
742
|
+
/:LocalBusiness \{accountId='\$\{accountId\}'\} not found/.test(src),
|
|
743
|
+
"org branch must keep its own :LocalBusiness message",
|
|
744
|
+
);
|
|
745
|
+
});
|
|
746
|
+
|
|
413
747
|
// ---------------------------------------------------------------------------
|
|
414
748
|
// Final summary.
|
|
415
749
|
// ---------------------------------------------------------------------------
|
|
@@ -201,6 +201,18 @@ Which voice a draft uses is the `scope` on the retrieval brief: `"my voice"` →
|
|
|
201
201
|
|
|
202
202
|
**Single-operator accounts are unchanged.** With author omitted at tag time, the sole operator is the implicit author, so their personal corpus equals the account-wide corpus and personal distillation reproduces the prior single profile. Legacy content carries no `voiceAuthor`; it appears only in the account-wide org walk until re-tagged, and personal drafts fall back to the org voice in the meantime.
|
|
203
203
|
|
|
204
|
+
## Identity is never guessed
|
|
205
|
+
|
|
206
|
+
Every `userId` and every `author` is an `:AdminUser` id that already exists on the account being operated. It is never a person's name, and never a slug built from one. `rob-mead` is not an identity.
|
|
207
|
+
|
|
208
|
+
A value naming no `:AdminUser` on the account is refused, and nothing is written. This is not a formality. A profile keyed on an admin who does not exist cannot be read back by any seat that resolves identity properly, so it becomes dead data that looks like a real operator profile. A `voiceAuthor` stamped the same way attributes writing to somebody who does not exist.
|
|
209
|
+
|
|
210
|
+
The tools resolve identity from the spawn environment on their own. Pass `author:` only to attribute content to a different operator on a multi-operator account, and only with that operator's real `:AdminUser` id.
|
|
211
|
+
|
|
212
|
+
If a tool refuses, the id is wrong. Do not retry with a different guess. The account's real admin identity is the operator's to supply.
|
|
213
|
+
|
|
214
|
+
`__org__` is exempt, because the house voice anchors on the business rather than on a person.
|
|
215
|
+
|
|
204
216
|
## Observability
|
|
205
217
|
|
|
206
218
|
| Tag | When |
|
|
@@ -213,6 +225,7 @@ Which voice a draft uses is the `scope` on the retrieval brief: `"my voice"` →
|
|
|
213
225
|
| `[voice-retrieve] scope=<scope> format=<format> status=<ok\|fallback-org\|no-data\|error> styleCardBytes=<n> exemplarCount=<k> tokenBudget=<n>` | Each retrieval. `status=fallback-org` proves the personal→org fallback fired. |
|
|
214
226
|
| `[voice-record-feedback] scope=<scope> userId=<id\|__org__> format=<format> intent="<haiku-summary>" diffBytes=<n>` | Each feedback write. |
|
|
215
227
|
| `[voice-ingest-session-text] sessionId=<id> adminUser=<id> turns=<n> bytes=<n> skipped=<m>` | On-demand session-text capture. |
|
|
228
|
+
| `[voice-identity] refuse tool=<tool> reason=unknown-admin-user userId=<key> accountId=<account>` | A key naming no `:AdminUser` on the account was refused. Names the rejected key and the account, so an invented key is visible in the logs instead of only in the graph. |
|
|
216
229
|
|
|
217
230
|
**Confirms working:** `[voice-retrieve] exemplarCount≥1` precedes every drafting-skill prompt assembly when the calling skill has not opted out. `[voice-distil]` appears at least once per 30-day window per active format per operator. `[voice-ingest-session-text] turns≥1` appears when the operator asks to capture a session's voice.
|
|
218
231
|
|