@rubytech/create-maxy-code 0.1.458 → 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.
Files changed (218) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/storage-broker/dist/__tests__/cf-exec.test.js +298 -3
  3. package/payload/platform/lib/storage-broker/dist/__tests__/cf-exec.test.js.map +1 -1
  4. package/payload/platform/lib/storage-broker/dist/__tests__/object-limits.test.d.ts +2 -0
  5. package/payload/platform/lib/storage-broker/dist/__tests__/object-limits.test.d.ts.map +1 -0
  6. package/payload/platform/lib/storage-broker/dist/__tests__/object-limits.test.js +79 -0
  7. package/payload/platform/lib/storage-broker/dist/__tests__/object-limits.test.js.map +1 -0
  8. package/payload/platform/lib/storage-broker/dist/__tests__/resource-limits.test.d.ts +2 -0
  9. package/payload/platform/lib/storage-broker/dist/__tests__/resource-limits.test.d.ts.map +1 -0
  10. package/payload/platform/lib/storage-broker/dist/__tests__/resource-limits.test.js +41 -0
  11. package/payload/platform/lib/storage-broker/dist/__tests__/resource-limits.test.js.map +1 -0
  12. package/payload/platform/lib/storage-broker/dist/cf-exec.d.ts +13 -1
  13. package/payload/platform/lib/storage-broker/dist/cf-exec.d.ts.map +1 -1
  14. package/payload/platform/lib/storage-broker/dist/cf-exec.js +165 -0
  15. package/payload/platform/lib/storage-broker/dist/cf-exec.js.map +1 -1
  16. package/payload/platform/lib/storage-broker/dist/house-credential.d.ts.map +1 -1
  17. package/payload/platform/lib/storage-broker/dist/house-credential.js +4 -2
  18. package/payload/platform/lib/storage-broker/dist/house-credential.js.map +1 -1
  19. package/payload/platform/lib/storage-broker/dist/index.d.ts +2 -0
  20. package/payload/platform/lib/storage-broker/dist/index.d.ts.map +1 -1
  21. package/payload/platform/lib/storage-broker/dist/index.js +2 -0
  22. package/payload/platform/lib/storage-broker/dist/index.js.map +1 -1
  23. package/payload/platform/lib/storage-broker/dist/object-limits.d.ts +80 -0
  24. package/payload/platform/lib/storage-broker/dist/object-limits.d.ts.map +1 -0
  25. package/payload/platform/lib/storage-broker/dist/object-limits.js +92 -0
  26. package/payload/platform/lib/storage-broker/dist/object-limits.js.map +1 -0
  27. package/payload/platform/lib/storage-broker/dist/resource-limits.d.ts +102 -0
  28. package/payload/platform/lib/storage-broker/dist/resource-limits.d.ts.map +1 -0
  29. package/payload/platform/lib/storage-broker/dist/resource-limits.js +105 -0
  30. package/payload/platform/lib/storage-broker/dist/resource-limits.js.map +1 -0
  31. package/payload/platform/lib/storage-broker/src/__tests__/cf-exec.test.ts +334 -5
  32. package/payload/platform/lib/storage-broker/src/__tests__/object-limits.test.ts +106 -0
  33. package/payload/platform/lib/storage-broker/src/__tests__/resource-limits.test.ts +49 -0
  34. package/payload/platform/lib/storage-broker/src/cf-exec.ts +221 -6
  35. package/payload/platform/lib/storage-broker/src/house-credential.ts +4 -2
  36. package/payload/platform/lib/storage-broker/src/index.ts +2 -0
  37. package/payload/platform/lib/storage-broker/src/object-limits.ts +95 -0
  38. package/payload/platform/lib/storage-broker/src/resource-limits.ts +105 -0
  39. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +7 -6
  40. package/payload/platform/plugins/cloudflare/PLUGIN.md +3 -1
  41. package/payload/platform/plugins/cloudflare/bin/cf-token.sh +4 -3
  42. package/payload/platform/plugins/cloudflare/mcp/__tests__/auth-route.test.ts +206 -0
  43. package/payload/platform/plugins/cloudflare/mcp/__tests__/authorize.test.ts +201 -0
  44. package/payload/platform/plugins/cloudflare/mcp/__tests__/file-routes.test.ts +178 -0
  45. package/payload/platform/plugins/cloudflare/mcp/__tests__/harness.test.ts +32 -0
  46. package/payload/platform/plugins/cloudflare/mcp/__tests__/passcode.test.ts +74 -0
  47. package/payload/platform/plugins/cloudflare/mcp/__tests__/ratelimit.test.ts +77 -0
  48. package/payload/platform/plugins/cloudflare/mcp/__tests__/skill-contract.test.ts +46 -0
  49. package/payload/platform/plugins/cloudflare/mcp/__tests__/template-config.test.ts +55 -0
  50. package/payload/platform/plugins/cloudflare/mcp/__tests__/upload-route.test.ts +255 -0
  51. package/payload/platform/plugins/cloudflare/mcp/package.json +5 -2
  52. package/payload/platform/plugins/cloudflare/references/api.md +1 -1
  53. package/payload/platform/plugins/cloudflare/references/r2-object-storage.md +46 -0
  54. package/payload/platform/plugins/cloudflare/skills/data-portal/SKILL.md +123 -0
  55. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/authorize.ts +32 -0
  56. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/log.ts +12 -0
  57. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/passcode.ts +73 -0
  58. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/ratelimit.ts +55 -0
  59. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/session.ts +85 -0
  60. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/_lib/types.ts +44 -0
  61. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/auth.ts +113 -0
  62. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/delete.ts +89 -0
  63. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/download.ts +70 -0
  64. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/files.ts +41 -0
  65. package/payload/platform/plugins/cloudflare/skills/data-portal/template/functions/api/upload.ts +151 -0
  66. package/payload/platform/plugins/cloudflare/skills/data-portal/template/public/index.html +42 -0
  67. package/payload/platform/plugins/cloudflare/skills/data-portal/template/public/portal.css +113 -0
  68. package/payload/platform/plugins/cloudflare/skills/data-portal/template/public/portal.js +155 -0
  69. package/payload/platform/plugins/cloudflare/skills/data-portal/template/schema.sql +64 -0
  70. package/payload/platform/plugins/cloudflare/skills/data-portal/template/wrangler.toml +21 -0
  71. package/payload/platform/plugins/cloudflare/skills/site-deploy/SKILL.md +8 -0
  72. package/payload/platform/plugins/docs/references/outlook-guide.md +5 -5
  73. package/payload/platform/plugins/docs/references/voice-mirror-guide.md +1 -0
  74. package/payload/platform/plugins/filesystem/PLUGIN.md +0 -1
  75. package/payload/platform/plugins/filesystem/mcp/dist/lib/scope.d.ts +14 -10
  76. package/payload/platform/plugins/filesystem/mcp/dist/lib/scope.d.ts.map +1 -1
  77. package/payload/platform/plugins/filesystem/mcp/dist/lib/scope.js +14 -10
  78. package/payload/platform/plugins/filesystem/mcp/dist/lib/scope.js.map +1 -1
  79. package/payload/platform/plugins/outlook/.claude-plugin/plugin.json +1 -1
  80. package/payload/platform/plugins/outlook/PLUGIN.md +3 -5
  81. package/payload/platform/plugins/outlook/mcp/dist/__tests__/attach.test.d.ts +2 -0
  82. package/payload/platform/plugins/outlook/mcp/dist/__tests__/attach.test.d.ts.map +1 -0
  83. package/payload/platform/plugins/outlook/mcp/dist/__tests__/attach.test.js +138 -0
  84. package/payload/platform/plugins/outlook/mcp/dist/__tests__/attach.test.js.map +1 -0
  85. package/payload/platform/plugins/outlook/mcp/dist/__tests__/detach.test.d.ts +2 -0
  86. package/payload/platform/plugins/outlook/mcp/dist/__tests__/detach.test.d.ts.map +1 -0
  87. package/payload/platform/plugins/outlook/mcp/dist/__tests__/detach.test.js +75 -0
  88. package/payload/platform/plugins/outlook/mcp/dist/__tests__/detach.test.js.map +1 -0
  89. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-attachments.test.d.ts +2 -0
  90. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-attachments.test.d.ts.map +1 -0
  91. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-attachments.test.js +121 -0
  92. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-attachments.test.js.map +1 -0
  93. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit-attachments.test.d.ts +2 -0
  94. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit-attachments.test.d.ts.map +1 -0
  95. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit-attachments.test.js +102 -0
  96. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit-attachments.test.js.map +1 -0
  97. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit-removal.test.d.ts +2 -0
  98. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit-removal.test.d.ts.map +1 -0
  99. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit-removal.test.js +187 -0
  100. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit-removal.test.js.map +1 -0
  101. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit.test.js +1 -1
  102. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-edit.test.js.map +1 -1
  103. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-send-attachments.test.d.ts +2 -0
  104. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-send-attachments.test.d.ts.map +1 -0
  105. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-send-attachments.test.js +209 -0
  106. package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-send-attachments.test.js.map +1 -0
  107. package/payload/platform/plugins/outlook/mcp/dist/index.js +12 -8
  108. package/payload/platform/plugins/outlook/mcp/dist/index.js.map +1 -1
  109. package/payload/platform/plugins/outlook/mcp/dist/lib/attach.d.ts +37 -0
  110. package/payload/platform/plugins/outlook/mcp/dist/lib/attach.d.ts.map +1 -0
  111. package/payload/platform/plugins/outlook/mcp/dist/lib/attach.js +79 -0
  112. package/payload/platform/plugins/outlook/mcp/dist/lib/attach.js.map +1 -0
  113. package/payload/platform/plugins/outlook/mcp/dist/lib/attachment-list.d.ts +43 -0
  114. package/payload/platform/plugins/outlook/mcp/dist/lib/attachment-list.d.ts.map +1 -0
  115. package/payload/platform/plugins/outlook/mcp/dist/lib/attachment-list.js +65 -0
  116. package/payload/platform/plugins/outlook/mcp/dist/lib/attachment-list.js.map +1 -0
  117. package/payload/platform/plugins/outlook/mcp/dist/lib/detach.d.ts +65 -0
  118. package/payload/platform/plugins/outlook/mcp/dist/lib/detach.d.ts.map +1 -0
  119. package/payload/platform/plugins/outlook/mcp/dist/lib/detach.js +99 -0
  120. package/payload/platform/plugins/outlook/mcp/dist/lib/detach.js.map +1 -0
  121. package/payload/platform/plugins/outlook/mcp/dist/lib/message.d.ts +8 -0
  122. package/payload/platform/plugins/outlook/mcp/dist/lib/message.d.ts.map +1 -1
  123. package/payload/platform/plugins/outlook/mcp/dist/lib/message.js +4 -0
  124. package/payload/platform/plugins/outlook/mcp/dist/lib/message.js.map +1 -1
  125. package/payload/platform/plugins/outlook/mcp/dist/lib/outbound-attachments.d.ts +36 -8
  126. package/payload/platform/plugins/outlook/mcp/dist/lib/outbound-attachments.d.ts.map +1 -1
  127. package/payload/platform/plugins/outlook/mcp/dist/lib/outbound-attachments.js +40 -13
  128. package/payload/platform/plugins/outlook/mcp/dist/lib/outbound-attachments.js.map +1 -1
  129. package/payload/platform/plugins/outlook/mcp/dist/lib/upload-session.d.ts +9 -5
  130. package/payload/platform/plugins/outlook/mcp/dist/lib/upload-session.d.ts.map +1 -1
  131. package/payload/platform/plugins/outlook/mcp/dist/lib/upload-session.js +12 -8
  132. package/payload/platform/plugins/outlook/mcp/dist/lib/upload-session.js.map +1 -1
  133. package/payload/platform/plugins/outlook/mcp/dist/tools/draft-edit.d.ts +49 -4
  134. package/payload/platform/plugins/outlook/mcp/dist/tools/draft-edit.d.ts.map +1 -1
  135. package/payload/platform/plugins/outlook/mcp/dist/tools/draft-edit.js +82 -13
  136. package/payload/platform/plugins/outlook/mcp/dist/tools/draft-edit.js.map +1 -1
  137. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.d.ts +8 -2
  138. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.d.ts.map +1 -1
  139. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.js +13 -4
  140. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.js.map +1 -1
  141. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-attachment.d.ts +15 -16
  142. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-attachment.d.ts.map +1 -1
  143. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-attachment.js +8 -41
  144. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-attachment.js.map +1 -1
  145. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-reply.d.ts.map +1 -1
  146. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-reply.js +3 -20
  147. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-reply.js.map +1 -1
  148. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-send.d.ts +27 -3
  149. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-send.d.ts.map +1 -1
  150. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-send.js +53 -8
  151. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-send.js.map +1 -1
  152. package/payload/platform/plugins/outlook/references/auth.md +1 -1
  153. package/payload/platform/plugins/outlook/references/graph-surfaces.md +33 -6
  154. package/payload/platform/plugins/outlook/skills/outlook/SKILL.md +22 -5
  155. package/payload/platform/plugins/storage-broker/PLUGIN.md +41 -2
  156. package/payload/platform/plugins/storage-broker/mcp/dist/__tests__/confine-path.test.d.ts +2 -0
  157. package/payload/platform/plugins/storage-broker/mcp/dist/__tests__/confine-path.test.d.ts.map +1 -0
  158. package/payload/platform/plugins/storage-broker/mcp/dist/__tests__/confine-path.test.js +85 -0
  159. package/payload/platform/plugins/storage-broker/mcp/dist/__tests__/confine-path.test.js.map +1 -0
  160. package/payload/platform/plugins/storage-broker/mcp/dist/__tests__/put-source.test.d.ts +2 -0
  161. package/payload/platform/plugins/storage-broker/mcp/dist/__tests__/put-source.test.d.ts.map +1 -0
  162. package/payload/platform/plugins/storage-broker/mcp/dist/__tests__/put-source.test.js +77 -0
  163. package/payload/platform/plugins/storage-broker/mcp/dist/__tests__/put-source.test.js.map +1 -0
  164. package/payload/platform/plugins/storage-broker/mcp/dist/confine-path.d.ts +12 -0
  165. package/payload/platform/plugins/storage-broker/mcp/dist/confine-path.d.ts.map +1 -0
  166. package/payload/platform/plugins/storage-broker/mcp/dist/confine-path.js +107 -0
  167. package/payload/platform/plugins/storage-broker/mcp/dist/confine-path.js.map +1 -0
  168. package/payload/platform/plugins/storage-broker/mcp/dist/index.js +86 -0
  169. package/payload/platform/plugins/storage-broker/mcp/dist/index.js.map +1 -1
  170. package/payload/platform/plugins/storage-broker/mcp/dist/put-source.d.ts +11 -0
  171. package/payload/platform/plugins/storage-broker/mcp/dist/put-source.d.ts.map +1 -0
  172. package/payload/platform/plugins/storage-broker/mcp/dist/put-source.js +35 -0
  173. package/payload/platform/plugins/storage-broker/mcp/dist/put-source.js.map +1 -0
  174. package/payload/platform/plugins/voice-mirror/PLUGIN.md +19 -7
  175. package/payload/platform/plugins/voice-mirror/mcp/dist/index.d.ts.map +1 -1
  176. package/payload/platform/plugins/voice-mirror/mcp/dist/index.js +35 -16
  177. package/payload/platform/plugins/voice-mirror/mcp/dist/index.js.map +1 -1
  178. package/payload/platform/plugins/voice-mirror/mcp/dist/lib/admin-user.d.ts +31 -0
  179. package/payload/platform/plugins/voice-mirror/mcp/dist/lib/admin-user.d.ts.map +1 -0
  180. package/payload/platform/plugins/voice-mirror/mcp/dist/lib/admin-user.js +87 -0
  181. package/payload/platform/plugins/voice-mirror/mcp/dist/lib/admin-user.js.map +1 -0
  182. package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-distil-profile.d.ts.map +1 -1
  183. package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-distil-profile.js +13 -3
  184. package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-distil-profile.js.map +1 -1
  185. package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-ingest-session-text.d.ts.map +1 -1
  186. package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-ingest-session-text.js +2 -1
  187. package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-ingest-session-text.js.map +1 -1
  188. package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-record-feedback.d.ts.map +1 -1
  189. package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-record-feedback.js +2 -1
  190. package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-record-feedback.js.map +1 -1
  191. package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-tag-content.d.ts +2 -2
  192. package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-tag-content.d.ts.map +1 -1
  193. package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-tag-content.js +12 -3
  194. package/payload/platform/plugins/voice-mirror/mcp/dist/tools/voice-tag-content.js.map +1 -1
  195. package/payload/platform/plugins/voice-mirror/mcp/scripts/smoke.mjs +334 -0
  196. package/payload/platform/plugins/voice-mirror/skills/voice-mirror/SKILL.md +13 -0
  197. package/payload/platform/scripts/logs-read-jsonl.test.sh +118 -0
  198. package/payload/platform/scripts/logs-read.sh +63 -9
  199. package/payload/platform/scripts/voice-author-key-audit.sh +155 -0
  200. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  201. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +4 -0
  202. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  203. package/payload/platform/services/claude-session-manager/dist/mcp-descriptor-resolve.d.ts +17 -0
  204. package/payload/platform/services/claude-session-manager/dist/mcp-descriptor-resolve.d.ts.map +1 -0
  205. package/payload/platform/services/claude-session-manager/dist/mcp-descriptor-resolve.js +88 -0
  206. package/payload/platform/services/claude-session-manager/dist/mcp-descriptor-resolve.js.map +1 -0
  207. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +0 -1
  208. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  209. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +34 -23
  210. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  211. package/payload/server/{chunk-64FGYKNZ.js → chunk-JXWFVE5X.js} +148 -0
  212. package/payload/server/{chunk-F4D35LKL.js → chunk-POBXIHOF.js} +34 -0
  213. package/payload/server/maxy-edge.js +1 -1
  214. package/payload/server/server.js +412 -37
  215. package/payload/server/{src-S7C4P6TT.js → src-4F37OHLK.js} +19 -1
  216. package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.d.ts +0 -2
  217. package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.js +0 -215
  218. package/payload/platform/plugins/outlook/mcp/dist/__tests__/pkce-flow.test.js.map +0 -1
@@ -0,0 +1,85 @@
1
+ import { describe, it, expect, beforeAll, afterAll } from "vitest";
2
+ import { mkdtempSync, mkdirSync, writeFileSync, symlinkSync, rmSync, realpathSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { confineToAccountDir } from "../confine-path.js";
6
+ let root;
7
+ let accountDir;
8
+ let outside;
9
+ beforeAll(() => {
10
+ // realpath the tmp root: on macOS /var is a symlink to /private/var, so an
11
+ // un-resolved base would never prefix-match the resolved candidates.
12
+ root = realpathSync(mkdtempSync(join(tmpdir(), "confine-")));
13
+ accountDir = join(root, "accounts", "acc-a");
14
+ outside = join(root, "elsewhere");
15
+ mkdirSync(accountDir, { recursive: true });
16
+ mkdirSync(outside, { recursive: true });
17
+ writeFileSync(join(accountDir, "ok.txt"), "hi");
18
+ writeFileSync(join(outside, "secret.env"), "TOKEN=x");
19
+ mkdirSync(join(accountDir, "sub"), { recursive: true });
20
+ // The escape a plain string prefix check would miss.
21
+ symlinkSync(join(outside, "secret.env"), join(accountDir, "escape.txt"));
22
+ // A symlinked *directory* inside the account dir, pointing out.
23
+ symlinkSync(outside, join(accountDir, "outlink"));
24
+ process.env.ACCOUNT_DIR = accountDir;
25
+ });
26
+ afterAll(() => {
27
+ delete process.env.ACCOUNT_DIR;
28
+ rmSync(root, { recursive: true, force: true });
29
+ });
30
+ describe("confineToAccountDir", () => {
31
+ it("accepts a regular file inside the account dir", () => {
32
+ expect(confineToAccountDir(join(accountDir, "ok.txt"), true)).toBe(join(accountDir, "ok.txt"));
33
+ });
34
+ it("rejects a .. traversal out of the account dir", () => {
35
+ expect(() => confineToAccountDir(join(accountDir, "..", "..", "elsewhere", "secret.env"), true)).toThrow(/outside the account directory/);
36
+ });
37
+ it("rejects a symlink resolving outside the account dir", () => {
38
+ expect(() => confineToAccountDir(join(accountDir, "escape.txt"), true)).toThrow(/outside the account directory/);
39
+ });
40
+ it("rejects a directory passed where a file is required", () => {
41
+ expect(() => confineToAccountDir(join(accountDir, "sub"), true)).toThrow(/not a regular file/);
42
+ });
43
+ it("reports a missing source file as not found, not as an escape", () => {
44
+ expect(() => confineToAccountDir(join(accountDir, "nope.txt"), true)).toThrow(/File not found/);
45
+ });
46
+ it("rejects a not-yet-existing destination whose parent is outside", () => {
47
+ expect(() => confineToAccountDir(join(outside, "new.bin"), false)).toThrow(/outside the account directory/);
48
+ });
49
+ // The destination branch cannot realpath a path that does not exist, so it
50
+ // resolves the parent. When the final component *does* already exist and is a
51
+ // symlink out, resolving only the parent would pass it and the caller's write
52
+ // would follow the link out of the account directory — the same escape the
53
+ // source branch rejects.
54
+ it("rejects a destination whose existing final component is a symlink out", () => {
55
+ expect(() => confineToAccountDir(join(accountDir, "escape.txt"), false)).toThrow(/outside the account directory/);
56
+ });
57
+ it("rejects a destination reached through a symlinked parent directory", () => {
58
+ expect(() => confineToAccountDir(join(accountDir, "outlink", "new.bin"), false)).toThrow(/outside the account directory/);
59
+ });
60
+ it("accepts a not-yet-existing destination inside the account dir", () => {
61
+ expect(confineToAccountDir(join(accountDir, "new.bin"), false)).toBe(join(accountDir, "new.bin"));
62
+ });
63
+ it("accepts a not-yet-existing destination in a subdirectory", () => {
64
+ expect(confineToAccountDir(join(accountDir, "sub", "new.bin"), false)).toBe(join(accountDir, "sub", "new.bin"));
65
+ });
66
+ it("throws when no account context is resolvable, rather than defaulting open", () => {
67
+ const saved = process.env.ACCOUNT_DIR;
68
+ const savedRoot = process.env.PLATFORM_ROOT;
69
+ const savedId = process.env.ACCOUNT_ID;
70
+ delete process.env.ACCOUNT_DIR;
71
+ delete process.env.PLATFORM_ROOT;
72
+ delete process.env.ACCOUNT_ID;
73
+ try {
74
+ expect(() => confineToAccountDir(join(accountDir, "ok.txt"), true)).toThrow(/No account context/);
75
+ }
76
+ finally {
77
+ process.env.ACCOUNT_DIR = saved;
78
+ if (savedRoot !== undefined)
79
+ process.env.PLATFORM_ROOT = savedRoot;
80
+ if (savedId !== undefined)
81
+ process.env.ACCOUNT_ID = savedId;
82
+ }
83
+ });
84
+ });
85
+ //# sourceMappingURL=confine-path.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"confine-path.test.js","sourceRoot":"","sources":["../../src/__tests__/confine-path.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=put-source.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"put-source.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/put-source.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,77 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { mkdtempSync, realpathSync, rmSync, writeFileSync, truncateSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { R2_OBJECT_MAX_BYTES } from "../../../../../lib/storage-broker/dist/object-limits.js";
6
+ // Task 1695 — the property under test is an ordering: the cap must fire BEFORE
7
+ // the file is read. Spying on readFileSync is what makes that observable. An
8
+ // assertion that only checked the error message would pass just as happily on an
9
+ // implementation that slurps a 3 GB file into memory and then measures its
10
+ // length, which is the bug this exists to prevent.
11
+ //
12
+ // node:fs is mocked partially so confine-path's realpathSync/statSync stay real
13
+ // and the confinement predicate is still exercised for what it is.
14
+ const readFileSpy = vi.fn();
15
+ vi.mock("node:fs", async (importOriginal) => {
16
+ const actual = await importOriginal();
17
+ return {
18
+ ...actual,
19
+ readFileSync: (...args) => readFileSpy(...args),
20
+ };
21
+ });
22
+ const { readPutSource } = await import("../put-source.js");
23
+ let dir;
24
+ beforeEach(() => {
25
+ // realpath'd at creation: on macOS mkdtemp hands back /var/folders/… which
26
+ // canonicalises to /private/var/folders/…, and confineToAccountDir realpaths
27
+ // both sides. Without this the paths under test differ from the paths asserted
28
+ // for a reason that has nothing to do with the cap.
29
+ dir = realpathSync(mkdtempSync(join(tmpdir(), "put-source-")));
30
+ process.env.ACCOUNT_DIR = dir;
31
+ readFileSpy.mockReset();
32
+ readFileSpy.mockReturnValue(Buffer.from([1, 2, 3]));
33
+ });
34
+ afterEach(() => {
35
+ rmSync(dir, { recursive: true, force: true });
36
+ delete process.env.ACCOUNT_DIR;
37
+ });
38
+ /** A sparse file of `size` bytes: statSync reports the size, no disk is used and
39
+ * nothing is ever written. A real 100 MiB fixture would make this suite the
40
+ * thing that thrashes the machine. */
41
+ function sparseFile(name, size) {
42
+ const p = join(dir, name);
43
+ writeFileSync(p, "");
44
+ truncateSync(p, size);
45
+ return p;
46
+ }
47
+ describe("readPutSource", () => {
48
+ it("rejects an over-cap file before reading it", () => {
49
+ const p = sparseFile("huge.bin", R2_OBJECT_MAX_BYTES + 1);
50
+ expect(() => readPutSource(p)).toThrow(/huge\.bin/);
51
+ // The assertion that matters. Without it this test would pass on an
52
+ // implementation that reads the whole file and then checks its length.
53
+ expect(readFileSpy).not.toHaveBeenCalled();
54
+ });
55
+ it("names the file and the limit when it rejects", () => {
56
+ const p = sparseFile("huge.bin", 150 * 1024 * 1024);
57
+ expect(() => readPutSource(p)).toThrow(/huge\.bin is 150\.0 MiB.*100\.0 MiB/);
58
+ });
59
+ it("reads a file at exactly the cap — the cap is a bound, not a wall", () => {
60
+ const p = sparseFile("atcap.bin", R2_OBJECT_MAX_BYTES);
61
+ const out = readPutSource(p);
62
+ expect(readFileSpy).toHaveBeenCalledWith(p);
63
+ expect(out.path).toBe(p);
64
+ });
65
+ it("reads an ordinary file", () => {
66
+ const p = join(dir, "small.bin");
67
+ writeFileSync(p, Buffer.from([1, 2, 3]));
68
+ const out = readPutSource(p);
69
+ expect(out.bytes).toEqual(Buffer.from([1, 2, 3]));
70
+ expect(readFileSpy).toHaveBeenCalledWith(p);
71
+ });
72
+ it("still rejects a path outside the account directory, before any size check", () => {
73
+ expect(() => readPutSource("/etc/hosts")).toThrow(/outside the account directory/);
74
+ expect(readFileSpy).not.toHaveBeenCalled();
75
+ });
76
+ });
77
+ //# sourceMappingURL=put-source.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"put-source.test.js","sourceRoot":"","sources":["../../src/__tests__/put-source.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Resolve `p` and require it inside the caller's account directory.
3
+ *
4
+ * `mustExist` true (a source to read): the path itself is realpathed and must
5
+ * be a regular file. False (a destination to write): the path does not exist
6
+ * yet and cannot be realpathed, so its *parent* is — which is exactly what a
7
+ * symlinked parent directory would otherwise subvert.
8
+ *
9
+ * Returns the resolved absolute path. Throws naming the offending path.
10
+ */
11
+ export declare function confineToAccountDir(p: string, mustExist: boolean): string;
12
+ //# sourceMappingURL=confine-path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"confine-path.d.ts","sourceRoot":"","sources":["../src/confine-path.ts"],"names":[],"mappings":"AAyCA;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,MAAM,CA4DzE"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Confine an agent-supplied file path to the caller's own account directory.
3
+ *
4
+ * Mirrors the predicate proven in the email plugin's resolveOutboundAttachments
5
+ * (platform/plugins/email/mcp/src/lib/attachment-resolve.ts): realpath the
6
+ * candidate and require the result under the realpathed account directory, so a
7
+ * symlink cannot escape a plain string prefix check. Copied rather than
8
+ * imported because platform/lib/* is the only cross-plugin-importable surface
9
+ * and this predicate does not live there; consolidating the duplicates is its
10
+ * own concern, not this tool's.
11
+ *
12
+ * This is a correctness guardrail, not a security boundary, and the difference
13
+ * matters. Every account shares one unix user and the agent holds Bash, so it
14
+ * can already read anything this process can (Task 1690). What actually
15
+ * contains the blast radius is that the house-level broker route touches no
16
+ * filesystem at all: file I/O stays here, in a process running at the agent's
17
+ * own privilege, so it grants nothing the agent did not already have. Moving
18
+ * this check server-side would not harden it — it would create the very
19
+ * house-privileged arbitrary read/write it appears to prevent.
20
+ */
21
+ import { realpathSync, statSync } from "node:fs";
22
+ import { basename, dirname, join, resolve } from "node:path";
23
+ /** The caller's account directory. ACCOUNT_DIR is the canonical spawn-set var;
24
+ * fall back to PLATFORM_ROOT + ACCOUNT_ID exactly as the email and url-get
25
+ * tools do. Throws rather than returning a default: with no account context
26
+ * there is nothing to validate against, and failing closed is the only safe
27
+ * answer. */
28
+ function accountDir() {
29
+ const direct = process.env.ACCOUNT_DIR;
30
+ if (direct && direct.trim())
31
+ return direct;
32
+ const platformRoot = process.env.PLATFORM_ROOT;
33
+ const accountId = process.env.ACCOUNT_ID;
34
+ if (platformRoot && accountId && !accountId.includes("/") && !accountId.includes("..")) {
35
+ return join(platformRoot, "..", "data", "accounts", accountId);
36
+ }
37
+ throw new Error("No account context (ACCOUNT_DIR unset and PLATFORM_ROOT+ACCOUNT_ID unavailable) to validate paths against.");
38
+ }
39
+ /**
40
+ * Resolve `p` and require it inside the caller's account directory.
41
+ *
42
+ * `mustExist` true (a source to read): the path itself is realpathed and must
43
+ * be a regular file. False (a destination to write): the path does not exist
44
+ * yet and cannot be realpathed, so its *parent* is — which is exactly what a
45
+ * symlinked parent directory would otherwise subvert.
46
+ *
47
+ * Returns the resolved absolute path. Throws naming the offending path.
48
+ */
49
+ export function confineToAccountDir(p, mustExist) {
50
+ let base;
51
+ try {
52
+ base = realpathSync(accountDir());
53
+ }
54
+ catch (err) {
55
+ throw new Error(`Account directory is not accessible: ${err instanceof Error ? err.message : String(err)}`);
56
+ }
57
+ let resolved;
58
+ if (mustExist) {
59
+ try {
60
+ resolved = realpathSync(p);
61
+ }
62
+ catch (err) {
63
+ if (err.code === "ENOENT") {
64
+ throw new Error(`File not found: ${p}`);
65
+ }
66
+ throw new Error(`Cannot read ${p}: ${err instanceof Error ? err.message : String(err)}`);
67
+ }
68
+ if (!statSync(resolved).isFile()) {
69
+ throw new Error(`${p} is not a regular file.`);
70
+ }
71
+ }
72
+ else {
73
+ const absolute = resolve(p);
74
+ // A destination that already exists must be realpathed like any source: if
75
+ // the final component is a symlink pointing out of the account directory, a
76
+ // write to it follows the link out, and resolving only the parent would let
77
+ // that through. The source branch rejects exactly this escape, so the two
78
+ // branches would otherwise disagree about what "confined" means.
79
+ try {
80
+ resolved = realpathSync(absolute);
81
+ }
82
+ catch (err) {
83
+ if (err.code !== "ENOENT") {
84
+ throw new Error(`Cannot resolve destination ${p}: ${err instanceof Error ? err.message : String(err)}`);
85
+ }
86
+ // It does not exist yet, so it cannot be realpathed. Resolve its parent —
87
+ // which is what a symlinked parent directory would otherwise subvert —
88
+ // and re-attach the final name.
89
+ let parent;
90
+ try {
91
+ parent = realpathSync(dirname(absolute));
92
+ }
93
+ catch (perr) {
94
+ if (perr.code === "ENOENT") {
95
+ throw new Error(`Destination directory does not exist: ${dirname(absolute)}`);
96
+ }
97
+ throw new Error(`Cannot resolve destination ${p}: ${perr instanceof Error ? perr.message : String(perr)}`);
98
+ }
99
+ resolved = join(parent, basename(absolute));
100
+ }
101
+ }
102
+ if (resolved !== base && !resolved.startsWith(base + "/")) {
103
+ throw new Error(`${p} is outside the account directory and cannot be used.`);
104
+ }
105
+ return resolved;
106
+ }
107
+ //# sourceMappingURL=confine-path.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"confine-path.js","sourceRoot":"","sources":["../src/confine-path.ts"],"names":[],"mappings":"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"}
@@ -6,10 +6,14 @@
6
6
  */
7
7
  import { initStderrTee } from "../../../../lib/mcp-stderr-tee/dist/index.js";
8
8
  initStderrTee("storage-broker");
9
+ import { writeFileSync } from "node:fs";
9
10
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
10
11
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
11
12
  import { z } from "zod";
12
13
  import { callApi as rawCallApi } from "./call-api.js";
14
+ import { confineToAccountDir } from "./confine-path.js";
15
+ import { readPutSource } from "./put-source.js";
16
+ import { R2_OBJECT_MAX_BYTES } from "../../../../lib/storage-broker/dist/object-limits.js";
13
17
  import { callerScopeHeaders } from "./caller-scope-headers.js";
14
18
  import { requirePortEnv } from "../../../../lib/require-port-env/dist/index.js";
15
19
  import { lifelineTool } from "../../../../lib/mcp-lifeline/dist/index.js";
@@ -87,6 +91,88 @@ lifelineTool(server, "storage-r2-bucket-create", "Create a Cloudflare R2 bucket
87
91
  return textResult(`R2 create failed: ${err}`, true);
88
92
  return textResult(`Created R2 bucket ${name}.`);
89
93
  });
94
+ // ─── R2 objects (Task 1691) ───────────────────────────────────────────────
95
+ //
96
+ // get/put move bytes through a file rather than the tool result: these objects
97
+ // are photos and scans, and base64 in a tool result would push megabytes into
98
+ // the agent's context window. The file I/O lives here, in this account-scoped
99
+ // process, and never in the house-level route that holds the credential — see
100
+ // confine-path.ts for why that split is the part that matters.
101
+ // ─── storage-r2-object-list ───────────────────────────────────────────────
102
+ lifelineTool(server, "storage-r2-object-list", "List the objects in an R2 bucket your account owns.", {
103
+ bucket: z.string().describe("Bucket name"),
104
+ prefix: z.string().optional().describe("Only list keys starting with this prefix"),
105
+ }, async ({ bucket, prefix }) => {
106
+ const result = await callApi("storage-r2-object-list", "/api/storage/r2/object/list", "POST", { bucket, prefix });
107
+ const err = errorOf(result);
108
+ if (err)
109
+ return textResult(`R2 object list failed: ${err}`, true);
110
+ const objects = result.objects ?? [];
111
+ return textResult(objects.length ? objects.map((o) => `${o.key}\t${o.size}`).join("\n") : "No objects.");
112
+ });
113
+ // ─── storage-r2-object-get ────────────────────────────────────────────────
114
+ lifelineTool(server, "storage-r2-object-get", `Download an object from an R2 bucket your account owns to a file in your account directory. Objects larger than ${R2_OBJECT_MAX_BYTES / 1024 / 1024} MiB are rejected.`, {
115
+ bucket: z.string().describe("Bucket name"),
116
+ key: z.string().describe("Object key"),
117
+ destPath: z.string().describe("Destination file path, inside your account directory"),
118
+ }, async ({ bucket, key, destPath }) => {
119
+ // Confine before the fetch: a rejected destination should cost no download.
120
+ let dest;
121
+ try {
122
+ dest = confineToAccountDir(destPath, false);
123
+ }
124
+ catch (e) {
125
+ return textResult(`R2 object get failed: ${e instanceof Error ? e.message : String(e)}`, true);
126
+ }
127
+ const result = await callApi("storage-r2-object-get", "/api/storage/r2/object/get", "POST", { bucket, key });
128
+ const err = errorOf(result);
129
+ if (err)
130
+ return textResult(`R2 object get failed: ${err}`, true);
131
+ const b64 = result.contentBase64;
132
+ if (typeof b64 !== "string")
133
+ return textResult("R2 object get failed: no content in response", true);
134
+ const bytes = Buffer.from(b64, "base64");
135
+ try {
136
+ writeFileSync(dest, bytes);
137
+ }
138
+ catch (e) {
139
+ return textResult(`R2 object get failed: ${e instanceof Error ? e.message : String(e)}`, true);
140
+ }
141
+ return textResult(`Wrote ${bytes.length} bytes to ${dest}`);
142
+ });
143
+ // ─── storage-r2-object-put ────────────────────────────────────────────────
144
+ lifelineTool(server, "storage-r2-object-put", `Upload a file from your account directory to an R2 bucket your account owns. Files larger than ${R2_OBJECT_MAX_BYTES / 1024 / 1024} MiB are rejected.`, {
145
+ bucket: z.string().describe("Bucket name"),
146
+ key: z.string().describe("Object key"),
147
+ srcPath: z.string().describe("Source file path, inside your account directory"),
148
+ }, async ({ bucket, key, srcPath }) => {
149
+ let bytes;
150
+ try {
151
+ // Confined and size-checked before the read: an over-cap file costs no
152
+ // buffer here and never reaches the house process.
153
+ ({ bytes } = readPutSource(srcPath));
154
+ }
155
+ catch (e) {
156
+ return textResult(`R2 object put failed: ${e instanceof Error ? e.message : String(e)}`, true);
157
+ }
158
+ const result = await callApi("storage-r2-object-put", "/api/storage/r2/object/put", "POST", {
159
+ bucket,
160
+ key,
161
+ contentBase64: bytes.toString("base64"),
162
+ });
163
+ const err = errorOf(result);
164
+ if (err)
165
+ return textResult(`R2 object put failed: ${err}`, true);
166
+ return textResult(`Uploaded ${bytes.length} bytes to ${bucket}/${key}.`);
167
+ });
168
+ // ─── storage-r2-object-delete ─────────────────────────────────────────────
169
+ lifelineTool(server, "storage-r2-object-delete", "Delete an object from an R2 bucket your account owns.", { bucket: z.string().describe("Bucket name"), key: z.string().describe("Object key") }, async ({ bucket, key }) => {
170
+ const result = await callApi("storage-r2-object-delete", "/api/storage/r2/object/delete", "POST", { bucket, key });
171
+ const err = errorOf(result);
172
+ if (err)
173
+ return textResult(`R2 object delete failed: ${err}`, true);
174
+ return textResult(`Deleted ${bucket}/${key}.`);
175
+ });
90
176
  async function main() {
91
177
  const transport = new StdioServerTransport();
92
178
  await server.connect(transport);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Returns the confined absolute path and the file's bytes.
3
+ *
4
+ * Throws naming the offending file and the limit when the file is over the cap,
5
+ * and naming the path when it is outside the account directory or unreadable.
6
+ */
7
+ export declare function readPutSource(srcPath: string): {
8
+ path: string;
9
+ bytes: Buffer;
10
+ };
11
+ //# sourceMappingURL=put-source.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"put-source.d.ts","sourceRoot":"","sources":["../src/put-source.ts"],"names":[],"mappings":"AAuBA;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAQ9E"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Resolve a put's source file: confine it to the caller's account directory,
3
+ * size it, reject it if it is over the cap, and only then read it.
4
+ *
5
+ * The order is the point (Task 1695). An over-cap file must cost no buffer, in
6
+ * this process or the house one, so the size is checked before the read rather
7
+ * than after — the same pre-read statSync rule the email plugin's
8
+ * resolveOutboundAttachments already proves for the same class of payload.
9
+ * Checking after the read would bound the upload while leaving the local OOM
10
+ * exactly where it was.
11
+ *
12
+ * This lives in its own module rather than inline in the put tool because the
13
+ * ordering is the behaviour worth testing, and index.ts constructs an MCP server
14
+ * on import.
15
+ */
16
+ import { readFileSync, statSync } from "node:fs";
17
+ import { basename } from "node:path";
18
+ import { confineToAccountDir } from "./confine-path.js";
19
+ import { R2_OBJECT_MAX_BYTES, tooLargeMessage, } from "../../../../lib/storage-broker/dist/object-limits.js";
20
+ /**
21
+ * Returns the confined absolute path and the file's bytes.
22
+ *
23
+ * Throws naming the offending file and the limit when the file is over the cap,
24
+ * and naming the path when it is outside the account directory or unreadable.
25
+ */
26
+ export function readPutSource(srcPath) {
27
+ const path = confineToAccountDir(srcPath, true);
28
+ // confineToAccountDir has already proven this is a regular file.
29
+ const { size } = statSync(path);
30
+ if (size > R2_OBJECT_MAX_BYTES) {
31
+ throw new Error(tooLargeMessage(basename(path), size));
32
+ }
33
+ return { path, bytes: readFileSync(path) };
34
+ }
35
+ //# sourceMappingURL=put-source.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"put-source.js","sourceRoot":"","sources":["../src/put-source.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EACL,mBAAmB,EACnB,eAAe,GAChB,MAAM,sDAAsD,CAAC;AAE9D;;;;;GAKG;AACH,MAAM,UAAU,aAAa,CAAC,OAAe;IAC3C,MAAM,IAAI,GAAG,mBAAmB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAChD,iEAAiE;IACjE,MAAM,EAAE,IAAI,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,IAAI,GAAG,mBAAmB,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;AAC7C,CAAC"}
@@ -29,11 +29,7 @@ mcp:
29
29
  LOG_DIR: ${LOG_DIR}
30
30
  PLATFORM_ROOT: ${PLATFORM_ROOT}
31
31
  ACCOUNT_ID: ${ACCOUNT_ID}
32
- ADMIN_USER_ID: ${ADMIN_USER_ID}
33
32
  SESSION_ID: ${SESSION_ID}
34
- NEO4J_URI: ${NEO4J_URI}
35
- NEO4J_USER: ${NEO4J_USER}
36
- NEO4J_PASSWORD: ${NEO4J_PASSWORD}
37
33
  ---
38
34
 
39
35
  # Voice Mirror
@@ -57,16 +53,32 @@ The operator is:
57
53
 
58
54
  ## Tools
59
55
 
60
- Voice mirror introduces five deterministic plugin tools. They route writes through `memory-write` so the graph-write gate stamps provenance on every node.
56
+ Voice mirror introduces five deterministic plugin tools. They write to the graph directly over an account-scoped Cypher session, not through `memory-write`: an authorship stamp does not need the re-embedding and re-validation `memory-write` performs on every call. Account scoping is enforced per query, and identity per write (see [Identity](#identity)).
61
57
 
62
58
  | Tool | Purpose |
63
59
  |------|---------|
64
- | `voice-tag-content` | Stamp `authorshipMode ∈ {human-only, human-led-agent-assisted, agent-led-human-reviewed, agent-only, unknown}`, `format ∈ {text, email, social-post, article, note, marketing-copy}`, and `voiceAuthor` on one or many `:KnowledgeDocument | :Message | :SocialPost` nodes (email threads live as `:KnowledgeDocument {source:'email'}`). Bulk-mode for backfill batches. `format` is required. `author` is optional — omit to attribute to the tagging operator. |
60
+ | `voice-tag-content` | Stamp `authorshipMode ∈ {human-only, human-led-agent-assisted, agent-led-human-reviewed, agent-only, unknown}`, `format ∈ {text, email, social-post, article, note, marketing-copy}`, and `voiceAuthor` on one or many `:KnowledgeDocument | :Message | :SocialPost` nodes (email threads live as `:KnowledgeDocument {source:'email'}`). Bulk-mode for backfill batches. `format` is required. `author` is optional — omit to attribute to the tagging operator. The resolved `voiceAuthor` must name an `:AdminUser` on the calling account or the tag is refused and nothing is written (see [Identity](#identity)). |
65
61
  | `voice-distil-profile` | Three modes, scope-aware (`scope='personal'` default walks one author; `scope='org'` walks the whole account onto the business). `mode='sample'` (default) walks the `human-only` corpus for the given `format`/`scope` and returns exemplars + recent edit intents for the agent to compose a style card; cadence-guarded (≥20% growth or ≥30 days). `mode='amend'` reads only `nodeIds` plus the existing `:VoiceProfile.styleCard` so the agent can decide whether named documents move the profile — operator-initiated, no cadence guard. `mode='write'` persists the YAML card; supply `amendedFromNodeIds` to attribute the write to specific documents (bypasses cadence + corpus walk). Trashed nodes are excluded from every walk. Omit `format` on `'sample'` to enumerate all formats in that scope's corpus and distil each. |
66
- | `voice-retrieve-conditioning` | Return `{styleCard, exemplars[], status}` for a drafting brief. Requires `brief.format` (one of the six corpus formats). `brief.scope` (default `personal`) picks personal vs org voice; a personal request with no personal profile falls back to the org profile (`status='fallback-org'`). K=5 short-form (`brief.length:'short'`), K=15 long-form (`brief.length:'long'`). Token-budget bounded. `status ∈ {ok, fallback-org, no-data, error}`. |
62
+ | `voice-retrieve-conditioning` | Return `{styleCard, exemplars[], status}` for a drafting brief. Requires `brief.format` (one of the six corpus formats). `brief.scope` (default `personal`) picks personal vs org voice; a personal request with no personal profile falls back to the org profile (`status='fallback-org'`). K=5 short-form (`brief.length:'short'`), K=15 long-form (`brief.length:'long'`). Token-budget bounded. `status ∈ {ok, fallback-org, no-data, error}`. Identity is not gated here: retrieval writes nothing, and a personal request that resolves no personal voice falls back to the org voice by design. |
67
63
  | `voice-record-feedback` | Capture an operator edit on an agent draft as a `:VoiceEdit {format, scope}` with a Haiku-summarised `intent`, linked back to the scoped per-format `:VoiceProfile` via `:FEEDBACK_FOR` (org edits → org profile; editor preserved via `:AUTHORED`). Requires `format`. |
68
64
  | `voice-ingest-session-text` | Write operator turns from the current session as `:Message {format:'text', authorshipMode:'human-only'}` corpus nodes. The agent reads its own operator turns from context and passes them as `turns`. Deduplicates via SHA256 `contentHash`. Invoked on demand by the voice-mirror skill when the operator asks to capture this session's voice. |
69
65
 
66
+ ## Identity
67
+
68
+ Every `userId` / `author` a tool accepts is an `:AdminUser` id that already exists on the calling account. It is never a person's name, nor a slug derived from one. A value naming no `:AdminUser` on the account is refused and nothing is written: a profile keyed on a non-existent admin is unreachable by any seat that resolves identity properly, and a `voiceAuthor` stamped with one attributes a corpus to an identity that does not exist.
69
+
70
+ The reserved org key `__org__` is exempt. It names no `:AdminUser` by design and anchors on `:LocalBusiness`. That is how the account-wide house voice works, including on a spawn that carries `ACCOUNT_ID` but no personal identity.
71
+
72
+ Each tool resolves its identity from `USER_ID` in the spawn env. A tool argument is only needed when attributing to someone other than the caller, on a multi-operator account.
73
+
74
+ A refusal names the rejected key and the account, and emits one line:
75
+
76
+ ```
77
+ [voice-identity] refuse tool=<tool> reason=unknown-admin-user userId=<key> accountId=<account>
78
+ ```
79
+
80
+ `platform/scripts/voice-author-key-audit.sh` is the standing per-install check for keys already in the graph. It is read-only and reports two distinct findings: `:VoiceProfile` keys and `voiceAuthor` stamps matching no `:AdminUser` on their account. Disposition of anything it finds is an operator decision.
81
+
70
82
  ## References
71
83
 
72
84
  No external references; the skill body defines the five tool contracts. Developer notes in `maxy-code/.docs/voice-mirror.md`.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAuC,gBAAgB,EAAuB,MAAM,8BAA8B,CAAC;AAic1H,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAuC,gBAAgB,EAAuB,MAAM,8BAA8B,CAAC;AAsd1H,OAAO,EAAE,gBAAgB,EAAE,CAAC"}