@rubytech/create-maxy-code 0.1.411 → 0.1.412

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 (147) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/account-scope/dist/index.d.ts +27 -0
  3. package/payload/platform/lib/account-scope/dist/index.d.ts.map +1 -0
  4. package/payload/platform/lib/account-scope/dist/index.js +33 -0
  5. package/payload/platform/lib/account-scope/dist/index.js.map +1 -0
  6. package/payload/platform/lib/account-scope/src/index.ts +43 -0
  7. package/payload/platform/lib/account-scope/tsconfig.json +5 -0
  8. package/payload/platform/package.json +2 -2
  9. package/payload/platform/plugins/.claude-plugin/marketplace.json +5 -0
  10. package/payload/platform/plugins/admin/hooks/lib/maxy-mcp-plugins.txt +1 -0
  11. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +35 -1
  12. package/payload/platform/plugins/business-assistant/skills/e-sign/SKILL.md +2 -0
  13. package/payload/platform/plugins/docs/references/cross-account-authority.md +23 -0
  14. package/payload/platform/plugins/docs/references/memory-guide.md +6 -0
  15. package/payload/platform/plugins/filesystem/.claude-plugin/plugin.json +21 -0
  16. package/payload/platform/plugins/filesystem/PLUGIN.md +63 -0
  17. package/payload/platform/plugins/filesystem/lib/mcp-spawn-tee/index.js +193 -0
  18. package/payload/platform/plugins/filesystem/lib/mcp-spawn-tee/package.json +3 -0
  19. package/payload/platform/plugins/filesystem/mcp/dist/index.d.ts +2 -0
  20. package/payload/platform/plugins/filesystem/mcp/dist/index.d.ts.map +1 -0
  21. package/payload/platform/plugins/filesystem/mcp/dist/index.js +91 -0
  22. package/payload/platform/plugins/filesystem/mcp/dist/index.js.map +1 -0
  23. package/payload/platform/plugins/filesystem/mcp/dist/lib/__tests__/scope.test.d.ts +2 -0
  24. package/payload/platform/plugins/filesystem/mcp/dist/lib/__tests__/scope.test.d.ts.map +1 -0
  25. package/payload/platform/plugins/filesystem/mcp/dist/lib/__tests__/scope.test.js +89 -0
  26. package/payload/platform/plugins/filesystem/mcp/dist/lib/__tests__/scope.test.js.map +1 -0
  27. package/payload/platform/plugins/filesystem/mcp/dist/lib/result.d.ts +12 -0
  28. package/payload/platform/plugins/filesystem/mcp/dist/lib/result.d.ts.map +1 -0
  29. package/payload/platform/plugins/filesystem/mcp/dist/lib/result.js +6 -0
  30. package/payload/platform/plugins/filesystem/mcp/dist/lib/result.js.map +1 -0
  31. package/payload/platform/plugins/filesystem/mcp/dist/lib/scope.d.ts +45 -0
  32. package/payload/platform/plugins/filesystem/mcp/dist/lib/scope.d.ts.map +1 -0
  33. package/payload/platform/plugins/filesystem/mcp/dist/lib/scope.js +86 -0
  34. package/payload/platform/plugins/filesystem/mcp/dist/lib/scope.js.map +1 -0
  35. package/payload/platform/plugins/filesystem/mcp/dist/lib/walk.d.ts +11 -0
  36. package/payload/platform/plugins/filesystem/mcp/dist/lib/walk.d.ts.map +1 -0
  37. package/payload/platform/plugins/filesystem/mcp/dist/lib/walk.js +64 -0
  38. package/payload/platform/plugins/filesystem/mcp/dist/lib/walk.js.map +1 -0
  39. package/payload/platform/plugins/filesystem/mcp/dist/tools/__tests__/handlers.test.d.ts +2 -0
  40. package/payload/platform/plugins/filesystem/mcp/dist/tools/__tests__/handlers.test.d.ts.map +1 -0
  41. package/payload/platform/plugins/filesystem/mcp/dist/tools/__tests__/handlers.test.js +94 -0
  42. package/payload/platform/plugins/filesystem/mcp/dist/tools/__tests__/handlers.test.js.map +1 -0
  43. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-edit.d.ts +8 -0
  44. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-edit.d.ts.map +1 -0
  45. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-edit.js +40 -0
  46. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-edit.js.map +1 -0
  47. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-glob.d.ts +6 -0
  48. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-glob.d.ts.map +1 -0
  49. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-glob.js +13 -0
  50. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-glob.js.map +1 -0
  51. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-grep.d.ts +7 -0
  52. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-grep.d.ts.map +1 -0
  53. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-grep.js +71 -0
  54. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-grep.js.map +1 -0
  55. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-list.d.ts +6 -0
  56. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-list.d.ts.map +1 -0
  57. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-list.js +26 -0
  58. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-list.js.map +1 -0
  59. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-read.d.ts +6 -0
  60. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-read.d.ts.map +1 -0
  61. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-read.js +28 -0
  62. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-read.js.map +1 -0
  63. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-write.d.ts +7 -0
  64. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-write.d.ts.map +1 -0
  65. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-write.js +25 -0
  66. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-write.js.map +1 -0
  67. package/payload/platform/plugins/filesystem/mcp/package.json +21 -0
  68. package/payload/platform/plugins/filesystem/mcp/vitest.config.ts +8 -0
  69. package/payload/platform/plugins/memory/PLUGIN.md +6 -0
  70. package/payload/platform/plugins/memory/mcp/dist/index-testable.d.ts +34 -0
  71. package/payload/platform/plugins/memory/mcp/dist/index-testable.d.ts.map +1 -0
  72. package/payload/platform/plugins/memory/mcp/dist/index-testable.js +60 -0
  73. package/payload/platform/plugins/memory/mcp/dist/index-testable.js.map +1 -0
  74. package/payload/platform/plugins/memory/mcp/dist/index.js +275 -133
  75. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  76. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/cross-account.test.d.ts +2 -0
  77. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/cross-account.test.d.ts.map +1 -0
  78. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/cross-account.test.js +65 -0
  79. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/cross-account.test.js.map +1 -0
  80. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/resolve-tool-account.test.d.ts +2 -0
  81. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/resolve-tool-account.test.d.ts.map +1 -0
  82. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/resolve-tool-account.test.js +54 -0
  83. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/resolve-tool-account.test.js.map +1 -0
  84. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  85. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +7 -0
  86. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  87. package/payload/platform/services/claude-session-manager/dist/claude-host-creds.d.ts.map +1 -1
  88. package/payload/platform/services/claude-session-manager/dist/claude-host-creds.js +6 -1
  89. package/payload/platform/services/claude-session-manager/dist/claude-host-creds.js.map +1 -1
  90. package/payload/platform/services/claude-session-manager/dist/claude-keychain-write.d.ts +7 -1
  91. package/payload/platform/services/claude-session-manager/dist/claude-keychain-write.d.ts.map +1 -1
  92. package/payload/platform/services/claude-session-manager/dist/claude-keychain-write.js +7 -2
  93. package/payload/platform/services/claude-session-manager/dist/claude-keychain-write.js.map +1 -1
  94. package/payload/platform/services/claude-session-manager/dist/config.d.ts +37 -0
  95. package/payload/platform/services/claude-session-manager/dist/config.d.ts.map +1 -1
  96. package/payload/platform/services/claude-session-manager/dist/config.js +34 -1
  97. package/payload/platform/services/claude-session-manager/dist/config.js.map +1 -1
  98. package/payload/platform/services/claude-session-manager/dist/cross-account-census.d.ts +34 -0
  99. package/payload/platform/services/claude-session-manager/dist/cross-account-census.d.ts.map +1 -0
  100. package/payload/platform/services/claude-session-manager/dist/cross-account-census.js +98 -0
  101. package/payload/platform/services/claude-session-manager/dist/cross-account-census.js.map +1 -0
  102. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  103. package/payload/platform/services/claude-session-manager/dist/http-server.js +10 -0
  104. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  105. package/payload/platform/services/claude-session-manager/dist/index.js +17 -0
  106. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  107. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  108. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +25 -0
  109. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  110. package/payload/platform/services/claude-session-manager/dist/session-sidecar.d.ts +7 -0
  111. package/payload/platform/services/claude-session-manager/dist/session-sidecar.d.ts.map +1 -1
  112. package/payload/platform/services/claude-session-manager/dist/session-sidecar.js +8 -0
  113. package/payload/platform/services/claude-session-manager/dist/session-sidecar.js.map +1 -1
  114. package/payload/server/{chunk-ZYS5V5QG.js → chunk-3Z5WE53X.js} +4 -0
  115. package/payload/server/maxy-edge.js +1 -1
  116. package/payload/server/public/assets/{AdminLoginScreens-BRQgI2lW.js → AdminLoginScreens-CYPTMmTk.js} +1 -1
  117. package/payload/server/public/assets/{AdminShell-By9n5icG.js → AdminShell-CZH_EZIy.js} +1 -1
  118. package/payload/server/public/assets/{Checkbox-DjEWgYu1.js → Checkbox-B0pbYJDQ.js} +1 -1
  119. package/payload/server/public/assets/{Transcript-BejWvOaO.js → Transcript-CYaAPBFX.js} +1 -1
  120. package/payload/server/public/assets/{admin-BFEMryQm.js → admin-BKDMSRto.js} +1 -1
  121. package/payload/server/public/assets/{browser-CMJMEUz6.js → browser-CxeMDDZA.js} +1 -1
  122. package/payload/server/public/assets/{calendar-C6ZGlHEP.js → calendar-DiIiTY9-.js} +1 -1
  123. package/payload/server/public/assets/chat-C56Oqvt1.js +1 -0
  124. package/payload/server/public/assets/chevron-left-Da-iZGi_.js +1 -0
  125. package/payload/server/public/assets/data-ZUdT44Ba.js +1 -0
  126. package/payload/server/public/assets/{graph-DIrXtPLv.js → graph-DC3dwnMZ.js} +1 -1
  127. package/payload/server/public/assets/{graph-labels-BtPf1Jom.js → graph-labels-Csnovm_c.js} +1 -1
  128. package/payload/server/public/assets/{maximize-2-DeJ4K8kL.js → maximize-2-WRUJsg5n.js} +1 -1
  129. package/payload/server/public/assets/{operator-B8QhqzLI.js → operator-BpzeHTAw.js} +1 -1
  130. package/payload/server/public/assets/{page-iFOy0dFx.js → page-8jJOxx6y.js} +1 -1
  131. package/payload/server/public/assets/{page-Df6KYkf4.js → page-IQDLdebG.js} +1 -1
  132. package/payload/server/public/assets/{public-BrUIRQZd.js → public-Dqg7jwjq.js} +1 -1
  133. package/payload/server/public/assets/{rotate-ccw-CYfntpwu.js → rotate-ccw-BayMWwfc.js} +1 -1
  134. package/payload/server/public/assets/{useSubAccountSwitcher-Cvnc_fwQ.css → useSubAccountSwitcher-CRZfyJXR.css} +1 -1
  135. package/payload/server/public/browser.html +4 -4
  136. package/payload/server/public/calendar.html +5 -5
  137. package/payload/server/public/chat.html +9 -9
  138. package/payload/server/public/data.html +8 -8
  139. package/payload/server/public/graph.html +9 -9
  140. package/payload/server/public/index.html +11 -11
  141. package/payload/server/public/operator.html +11 -11
  142. package/payload/server/public/public.html +9 -9
  143. package/payload/server/server.js +36 -11
  144. package/payload/server/public/assets/chat-B7s62Nnp.js +0 -1
  145. package/payload/server/public/assets/chevron-left-Dfjcaiyr.js +0 -1
  146. package/payload/server/public/assets/data-741ZbBwR.js +0 -1
  147. /package/payload/server/public/assets/{useSubAccountSwitcher-BK_KfOLC.js → useSubAccountSwitcher-C9aLg8yI.js} +0 -0
@@ -0,0 +1,40 @@
1
+ import { readFileSync, writeFileSync } from "node:fs";
2
+ import { resolveRoot, containedPath } from "../lib/scope.js";
3
+ import { ok, err } from "../lib/result.js";
4
+ export function fileEdit(args, ctx) {
5
+ const r = resolveRoot(ctx, args.targetAccountId, "file-edit");
6
+ if ("reject" in r)
7
+ return err(`file-edit denied: ${r.reject}`);
8
+ // An empty oldString has no unique match semantics — "ab".split("") yields a
9
+ // spurious single "match" that would silently splice newString into the file.
10
+ if (args.oldString === "")
11
+ return err("file-edit: oldString must not be empty");
12
+ let abs;
13
+ try {
14
+ abs = containedPath(r.root, args.path);
15
+ }
16
+ catch {
17
+ return err("file-edit rejected: path escapes the account directory");
18
+ }
19
+ let content;
20
+ try {
21
+ content = readFileSync(abs, "utf-8");
22
+ }
23
+ catch (e) {
24
+ return err(`file-edit: ${e.message}`);
25
+ }
26
+ const parts = content.split(args.oldString);
27
+ const count = parts.length - 1;
28
+ if (count === 0)
29
+ return err(`file-edit: oldString not found in ${args.path}`);
30
+ if (count > 1)
31
+ return err(`file-edit: oldString is not unique in ${args.path} (${count} matches)`);
32
+ try {
33
+ writeFileSync(abs, parts.join(args.newString), "utf-8");
34
+ return ok(`edited ${args.path}`);
35
+ }
36
+ catch (e) {
37
+ return err(`file-edit: ${e.message}`);
38
+ }
39
+ }
40
+ //# sourceMappingURL=file-edit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-edit.js","sourceRoot":"","sources":["../../src/tools/file-edit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAY,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAE3C,MAAM,UAAU,QAAQ,CACtB,IAAsF,EACtF,GAAQ;IAER,MAAM,CAAC,GAAG,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,WAAW,CAAC,CAAC;IAC9D,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC,qBAAqB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/D,6EAA6E;IAC7E,8EAA8E;IAC9E,IAAI,IAAI,CAAC,SAAS,KAAK,EAAE;QAAE,OAAO,GAAG,CAAC,wCAAwC,CAAC,CAAC;IAChF,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC,wDAAwD,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,OAAe,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IACvC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,GAAG,CAAC,cAAe,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;IACnD,CAAC;IACD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IAC/B,IAAI,KAAK,KAAK,CAAC;QAAE,OAAO,GAAG,CAAC,qCAAqC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9E,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,GAAG,CAAC,yCAAyC,IAAI,CAAC,IAAI,KAAK,KAAK,WAAW,CAAC,CAAC;IACnG,IAAI,CAAC;QACH,aAAa,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,CAAC;QACxD,OAAO,EAAE,CAAC,UAAU,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,GAAG,CAAC,cAAe,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;IACnD,CAAC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { type Ctx } from "../lib/scope.js";
2
+ export declare function fileGlob(args: {
3
+ pattern: string;
4
+ targetAccountId?: string;
5
+ }, ctx: Ctx): import("../lib/result.js").ToolResult;
6
+ //# sourceMappingURL=file-glob.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-glob.d.ts","sourceRoot":"","sources":["../../src/tools/file-glob.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAMxD,wBAAgB,QAAQ,CAAC,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,EAAE,GAAG,yCAMrF"}
@@ -0,0 +1,13 @@
1
+ import { resolveRoot } from "../lib/scope.js";
2
+ import { walkFiles, globToRegExp } from "../lib/walk.js";
3
+ import { ok, err } from "../lib/result.js";
4
+ const GLOB_CAP = 1000;
5
+ export function fileGlob(args, ctx) {
6
+ const r = resolveRoot(ctx, args.targetAccountId, "file-glob");
7
+ if ("reject" in r)
8
+ return err(`file-glob denied: ${r.reject}`);
9
+ const re = globToRegExp(args.pattern);
10
+ const hits = walkFiles(r.root, GLOB_CAP).filter((rel) => re.test(rel));
11
+ return ok(hits.length ? hits.join("\n") : "(no matches)");
12
+ }
13
+ //# sourceMappingURL=file-glob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-glob.js","sourceRoot":"","sources":["../../src/tools/file-glob.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAY,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAE3C,MAAM,QAAQ,GAAG,IAAI,CAAC;AAEtB,MAAM,UAAU,QAAQ,CAAC,IAAmD,EAAE,GAAQ;IACpF,MAAM,CAAC,GAAG,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,WAAW,CAAC,CAAC;IAC9D,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC,qBAAqB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/D,MAAM,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IACvE,OAAO,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC;AAC5D,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { type Ctx } from "../lib/scope.js";
2
+ export declare function fileGrep(args: {
3
+ pattern: string;
4
+ path?: string;
5
+ targetAccountId?: string;
6
+ }, ctx: Ctx): import("../lib/result.js").ToolResult;
7
+ //# sourceMappingURL=file-grep.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-grep.d.ts","sourceRoot":"","sources":["../../src/tools/file-grep.ts"],"names":[],"mappings":"AAEA,OAAO,EAA8B,KAAK,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAQvE,wBAAgB,QAAQ,CACtB,IAAI,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,EAClE,GAAG,EAAE,GAAG,yCAmDT"}
@@ -0,0 +1,71 @@
1
+ import { statSync, readFileSync } from "node:fs";
2
+ import { join, relative, resolve, sep } from "node:path";
3
+ import { resolveRoot, containedPath } from "../lib/scope.js";
4
+ import { walkFiles } from "../lib/walk.js";
5
+ import { ok, err } from "../lib/result.js";
6
+ const GREP_CAP = 500; // max match lines returned
7
+ const WALK_CAP = 20000; // max files scanned
8
+ const MAX_FILE = 5 * 1024 * 1024;
9
+ export function fileGrep(args, ctx) {
10
+ const r = resolveRoot(ctx, args.targetAccountId, "file-grep");
11
+ if ("reject" in r)
12
+ return err(`file-grep denied: ${r.reject}`);
13
+ let base;
14
+ // `prefix` is the search subdir expressed relative to the account root, so
15
+ // every reported path stays root-relative and round-trips into file-read /
16
+ // file-glob regardless of which subdir was searched.
17
+ let prefix = "";
18
+ try {
19
+ if (args.path) {
20
+ base = containedPath(r.root, args.path);
21
+ prefix = relative(r.root, resolve(r.root, args.path)).split(sep).join("/");
22
+ }
23
+ else {
24
+ base = r.root;
25
+ }
26
+ }
27
+ catch {
28
+ return err("file-grep rejected: path escapes the account directory");
29
+ }
30
+ let re;
31
+ try {
32
+ re = new RegExp(args.pattern);
33
+ }
34
+ catch (e) {
35
+ return err(`file-grep: invalid pattern: ${e.message}`);
36
+ }
37
+ const hits = [];
38
+ for (const rel of walkFiles(base, WALK_CAP)) {
39
+ if (hits.length >= GREP_CAP)
40
+ break;
41
+ const abs = join(base, rel);
42
+ const rootRel = prefix ? `${prefix}/${rel}` : rel;
43
+ let st;
44
+ try {
45
+ st = statSync(abs);
46
+ }
47
+ catch {
48
+ continue;
49
+ }
50
+ if (st.size > MAX_FILE)
51
+ continue;
52
+ let text;
53
+ try {
54
+ text = readFileSync(abs, "utf-8");
55
+ }
56
+ catch {
57
+ continue;
58
+ }
59
+ if (text.includes("\u0000"))
60
+ continue; // skip binary
61
+ const lines = text.split("\n");
62
+ for (let i = 0; i < lines.length; i++) {
63
+ if (hits.length >= GREP_CAP)
64
+ break;
65
+ if (re.test(lines[i]))
66
+ hits.push(`${rootRel}:${i + 1}:${lines[i].slice(0, 200)}`);
67
+ }
68
+ }
69
+ return ok(hits.length ? hits.join("\n") : "(no matches)");
70
+ }
71
+ //# sourceMappingURL=file-grep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-grep.js","sourceRoot":"","sources":["../../src/tools/file-grep.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import { type Ctx } from "../lib/scope.js";
2
+ export declare function fileList(args: {
3
+ path?: string;
4
+ targetAccountId?: string;
5
+ }, ctx: Ctx): import("../lib/result.js").ToolResult;
6
+ //# sourceMappingURL=file-list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-list.d.ts","sourceRoot":"","sources":["../../src/tools/file-list.ts"],"names":[],"mappings":"AACA,OAAO,EAA8B,KAAK,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAGvE,wBAAgB,QAAQ,CAAC,IAAI,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,EAAE,GAAG,yCAkBnF"}
@@ -0,0 +1,26 @@
1
+ import { readdirSync } from "node:fs";
2
+ import { resolveRoot, containedPath } from "../lib/scope.js";
3
+ import { ok, err } from "../lib/result.js";
4
+ export function fileList(args, ctx) {
5
+ const r = resolveRoot(ctx, args.targetAccountId, "file-list");
6
+ if ("reject" in r)
7
+ return err(`file-list denied: ${r.reject}`);
8
+ let dir;
9
+ try {
10
+ dir = args.path ? containedPath(r.root, args.path) : r.root;
11
+ }
12
+ catch {
13
+ return err("file-list rejected: path escapes the account directory");
14
+ }
15
+ try {
16
+ const rows = readdirSync(dir, { withFileTypes: true }).map((e) => {
17
+ const type = e.isDirectory() ? "dir" : e.isSymbolicLink() ? "symlink" : "file";
18
+ return `${type}\t${e.name}`;
19
+ });
20
+ return ok(rows.length ? rows.join("\n") : "(empty)");
21
+ }
22
+ catch (e) {
23
+ return err(`file-list: ${e.message}`);
24
+ }
25
+ }
26
+ //# sourceMappingURL=file-list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-list.js","sourceRoot":"","sources":["../../src/tools/file-list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAY,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAE3C,MAAM,UAAU,QAAQ,CAAC,IAAiD,EAAE,GAAQ;IAClF,MAAM,CAAC,GAAG,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,WAAW,CAAC,CAAC;IAC9D,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC,qBAAqB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/D,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC,wDAAwD,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YAC/D,MAAM,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC;YAC/E,OAAO,GAAG,IAAI,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9B,CAAC,CAAC,CAAC;QACH,OAAO,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACvD,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,GAAG,CAAC,cAAe,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;IACnD,CAAC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { type Ctx } from "../lib/scope.js";
2
+ export declare function fileRead(args: {
3
+ path: string;
4
+ targetAccountId?: string;
5
+ }, ctx: Ctx): import("../lib/result.js").ToolResult;
6
+ //# sourceMappingURL=file-read.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-read.d.ts","sourceRoot":"","sources":["../../src/tools/file-read.ts"],"names":[],"mappings":"AACA,OAAO,EAA8B,KAAK,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAKvE,wBAAgB,QAAQ,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,EAAE,GAAG,yCAiBlF"}
@@ -0,0 +1,28 @@
1
+ import { statSync, readFileSync } from "node:fs";
2
+ import { resolveRoot, containedPath } from "../lib/scope.js";
3
+ import { ok, err } from "../lib/result.js";
4
+ const MAX_FILE = 5 * 1024 * 1024; // 5 MB
5
+ export function fileRead(args, ctx) {
6
+ const r = resolveRoot(ctx, args.targetAccountId, "file-read");
7
+ if ("reject" in r)
8
+ return err(`file-read denied: ${r.reject}`);
9
+ let abs;
10
+ try {
11
+ abs = containedPath(r.root, args.path);
12
+ }
13
+ catch {
14
+ return err("file-read rejected: path escapes the account directory");
15
+ }
16
+ try {
17
+ const st = statSync(abs);
18
+ if (!st.isFile())
19
+ return err(`file-read: not a file: ${args.path}`);
20
+ if (st.size > MAX_FILE)
21
+ return err(`file-read: file too large (${st.size} bytes, max ${MAX_FILE})`);
22
+ return ok(readFileSync(abs, "utf-8"));
23
+ }
24
+ catch (e) {
25
+ return err(`file-read: ${e.message}`);
26
+ }
27
+ }
28
+ //# sourceMappingURL=file-read.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-read.js","sourceRoot":"","sources":["../../src/tools/file-read.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAY,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAE3C,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO;AAEzC,MAAM,UAAU,QAAQ,CAAC,IAAgD,EAAE,GAAQ;IACjF,MAAM,CAAC,GAAG,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,WAAW,CAAC,CAAC;IAC9D,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC,qBAAqB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/D,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC,wDAAwD,CAAC,CAAC;IACvE,CAAC;IACD,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QACzB,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE;YAAE,OAAO,GAAG,CAAC,0BAA0B,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACpE,IAAI,EAAE,CAAC,IAAI,GAAG,QAAQ;YAAE,OAAO,GAAG,CAAC,8BAA8B,EAAE,CAAC,IAAI,eAAe,QAAQ,GAAG,CAAC,CAAC;QACpG,OAAO,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC;IACxC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,GAAG,CAAC,cAAe,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;IACnD,CAAC;AACH,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { type Ctx } from "../lib/scope.js";
2
+ export declare function fileWrite(args: {
3
+ path: string;
4
+ content: string;
5
+ targetAccountId?: string;
6
+ }, ctx: Ctx): import("../lib/result.js").ToolResult;
7
+ //# sourceMappingURL=file-write.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-write.d.ts","sourceRoot":"","sources":["../../src/tools/file-write.ts"],"names":[],"mappings":"AAEA,OAAO,EAA8B,KAAK,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAGvE,wBAAgB,SAAS,CACvB,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,EACjE,GAAG,EAAE,GAAG,yCAiBT"}
@@ -0,0 +1,25 @@
1
+ import { mkdirSync, writeFileSync } from "node:fs";
2
+ import { dirname } from "node:path";
3
+ import { resolveRoot, containedPath } from "../lib/scope.js";
4
+ import { ok, err } from "../lib/result.js";
5
+ export function fileWrite(args, ctx) {
6
+ const r = resolveRoot(ctx, args.targetAccountId, "file-write");
7
+ if ("reject" in r)
8
+ return err(`file-write denied: ${r.reject}`);
9
+ let abs;
10
+ try {
11
+ abs = containedPath(r.root, args.path);
12
+ }
13
+ catch {
14
+ return err("file-write rejected: path escapes the account directory");
15
+ }
16
+ try {
17
+ mkdirSync(dirname(abs), { recursive: true });
18
+ writeFileSync(abs, args.content, "utf-8");
19
+ return ok(`wrote ${Buffer.byteLength(args.content)} bytes to ${args.path}`);
20
+ }
21
+ catch (e) {
22
+ return err(`file-write: ${e.message}`);
23
+ }
24
+ }
25
+ //# sourceMappingURL=file-write.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-write.js","sourceRoot":"","sources":["../../src/tools/file-write.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAY,MAAM,iBAAiB,CAAC;AACvE,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAE3C,MAAM,UAAU,SAAS,CACvB,IAAiE,EACjE,GAAQ;IAER,MAAM,CAAC,GAAG,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,YAAY,CAAC,CAAC;IAC/D,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,GAAG,CAAC,sBAAsB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAChE,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,GAAG,CAAC,yDAAyD,CAAC,CAAC;IACxE,CAAC;IACD,IAAI,CAAC;QACH,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7C,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QAC1C,OAAO,EAAE,CAAC,SAAS,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9E,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,OAAO,GAAG,CAAC,eAAgB,CAAW,CAAC,OAAO,EAAE,CAAC,CAAC;IACpD,CAAC;AACH,CAAC"}
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "@maxy/filesystem",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "start": "node dist/index.js",
10
+ "test": "vitest run"
11
+ },
12
+ "dependencies": {
13
+ "@modelcontextprotocol/sdk": "^1.12.1",
14
+ "zod": "^3.24.0"
15
+ },
16
+ "devDependencies": {
17
+ "typescript": "^5.7.0",
18
+ "@types/node": "^22.0.0",
19
+ "vitest": "^2.0.0"
20
+ }
21
+ }
@@ -0,0 +1,8 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ include: ["src/**/__tests__/**/*.test.ts"],
6
+ environment: "node",
7
+ },
8
+ });
@@ -185,6 +185,12 @@ Writes targeting `:Person`, `:UserProfile`, `:AdminUser`, `:Organization`, `:Loc
185
185
 
186
186
  - **Direct-ask path — `SESSION_NODE_ID` env-stamp.** When `producedByTaskId` is not provided (typical of direct admin asks like "add Anneke as person"), `memory-write` falls back to the `SESSION_NODE_ID` env var. The admin server stamps it at PTY spawn time on both admin sessions and on the specialist subagent spawns the admin dispatches (listing-curator, content-producer, database-operator and friends inherit the same anchor). The value is the `sessionId` UUID of the active `:AdminConversation`; the wrapper's `injectConversationProvenance` MATCHes `(c:Conversation {sessionId, accountId})` so account isolation is enforced inside the natural key — a cross-account stamp returns zero rows and never injects. Autonomous spawns with no parent conversation (cron, scheduled-task) legitimately have no env-stamp; those must thread `producedByTaskId` via `work-create` instead. The env-stamp is invisible to the agent (no schema field changes); the LLM never decides whether to use it.
187
187
 
188
+ ## Cross-account access (house admin) — `targetAccountId`
189
+
190
+ The memory tools whose Neo4j read/write is scoped **solely** by `accountId` accept an optional `targetAccountId`. A house-scoped `role:admin` session sets it to read or write a **sub-account's** subgraph instead of its own; omitting it is byte-identical to the pre-existing own-account behaviour. Only a house-scoped session may set it: a sub-account or public session that passes `targetAccountId` is rejected `cross-account-denied`, and a house session naming an account that does not exist on this install is rejected `cross-account-invalid-target`. Resolution runs through the shared `resolveEffectiveAccount` primitive — there is no house fallback and no local re-implementation. Each call that sets `targetAccountId` emits one `[xacct] op=call tool=<t> caller=<own8> target=<tgt8> houseScope=<bool> decision=<allow|deny> reason=<…>` line before the graph call, and the tool's own `[memory-<t>] [<accountId>]` line prints the resolved (target) id as a wrong-subgraph cross-check.
191
+
192
+ Tools that do **not** take `targetAccountId` fall into three groups. **Not account-scoped:** `image-fetch` fetches image bytes by URL; `graph-prune-denylist-add/remove/list` mutate a brand-global deny-list; `memory-ingest-extract` stages a temp file — none read or write a sub-account subgraph. **Bound to a caller constant the parameter cannot re-scope:** `profile-read/update/delete` key on the operator's `userId`, and `session-compact` keys on the caller's live session — cross-account support for these is deferred (it needs `userId`/session re-scoping, a separate design question). **Scheduled, not interactive:** the dream-cycle **cron** (`lib/dream-cycle/*`) takes its account from the scheduler context, not a tool argument; only the interactive `memory-dream-run` tool is house-targetable. These tools keep their own-account no-account guard.
193
+
188
194
  ## Graph Hygiene
189
195
 
190
196
  Graph hygiene is **agent-directed, case by case** — no autonomous rule engine, no cron. The Neo4j graph degrades into a landfill if pollution from prior agent errors (wrong-account hostnames, fabricated identifiers, raw monologue dumps) is allowed to compound. Cleanup happens when the admin agent observes a match against the operator-curated deny-list (or the user directly asks) and applies discretion using `memory-update`, `memory-delete`, or `conversation-memory-expunge`.
@@ -0,0 +1,34 @@
1
+ type RejectContent = {
2
+ content: {
3
+ type: "text";
4
+ text: string;
5
+ }[];
6
+ isError: true;
7
+ };
8
+ export type ToolAccount = {
9
+ accountId: string;
10
+ } | {
11
+ reject: RejectContent;
12
+ };
13
+ /**
14
+ * Build the per-tool account resolver. `accountId` / `houseScope` are the boot
15
+ * constants; `isValid` is injected (from `enumerateValidAccountIds(accountsDir)`
16
+ * in production, a stub in tests). Returns a closure the tool handlers call with
17
+ * their optional `targetAccountId`.
18
+ *
19
+ * Contract:
20
+ * - null boot `accountId` → refuse (no-account guard, unchanged behaviour).
21
+ * - no `targetAccountId` → own account, no `[xacct]` line (byte-identical).
22
+ * - `targetAccountId` set → resolve via the shared 1440 resolver, emit one
23
+ * `[xacct]` line, and on reject return the MCP error content.
24
+ * `houseScope=false decision=allow` cannot occur: a non-house session with a
25
+ * target is denied by the resolver before any allow path.
26
+ */
27
+ export declare function makeResolveToolAccount(deps: {
28
+ accountId: string | null;
29
+ houseScope: string | null;
30
+ isValid: (id: string) => boolean;
31
+ log?: (line: string) => void;
32
+ }): (toolName: string, targetAccountId?: string | null) => ToolAccount;
33
+ export {};
34
+ //# sourceMappingURL=index-testable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-testable.d.ts","sourceRoot":"","sources":["../src/index-testable.ts"],"names":[],"mappings":"AAQA,KAAK,aAAa,GAAG;IAAE,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAAC,OAAO,EAAE,IAAI,CAAA;CAAE,CAAC;AAClF,MAAM,MAAM,WAAW,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,aAAa,CAAA;CAAE,CAAC;AAuB5E;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE;IAC3C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IACjC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9B,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,KAAK,WAAW,CA0BrE"}
@@ -0,0 +1,60 @@
1
+ // Pure, side-effect-free cross-account seam for the memory MCP plugin.
2
+ //
3
+ // Split out of index.ts so unit tests can exercise the resolution + [xacct]
4
+ // logging without importing index.ts, which has stdio side effects at module
5
+ // load (stderr tee, MCP server construction, schema load that throws on a
6
+ // broken sidecar). Task 1455.
7
+ import { resolveEffectiveAccountCore } from "../../../../lib/account-scope/dist/index.js";
8
+ function errContent(text) {
9
+ return { content: [{ type: "text", text }], isError: true };
10
+ }
11
+ /** Preserve the exact wording of index.ts's `refuseNoAccount` so a metadata-only
12
+ * spawn (no ACCOUNT_ID) returns the same message it always has. */
13
+ function noAccountText(toolName) {
14
+ return (`${toolName} cannot execute: this MCP instance was launched without ACCOUNT_ID ` +
15
+ `(plugin-system metadata-only mode). Per-account graph access requires ACCOUNT_ID at spawn.`);
16
+ }
17
+ /** Operator-facing reject messages. Each CONTAINS its reject code so the
18
+ * reason is greppable and the agent sees why the call was refused. */
19
+ function rejectText(reason, target8) {
20
+ return reason === "cross-account-denied"
21
+ ? `cross-account-denied: only a house-scoped admin session may set targetAccountId.`
22
+ : `cross-account-invalid-target: "${target8}…" is not a valid account on this install.`;
23
+ }
24
+ /**
25
+ * Build the per-tool account resolver. `accountId` / `houseScope` are the boot
26
+ * constants; `isValid` is injected (from `enumerateValidAccountIds(accountsDir)`
27
+ * in production, a stub in tests). Returns a closure the tool handlers call with
28
+ * their optional `targetAccountId`.
29
+ *
30
+ * Contract:
31
+ * - null boot `accountId` → refuse (no-account guard, unchanged behaviour).
32
+ * - no `targetAccountId` → own account, no `[xacct]` line (byte-identical).
33
+ * - `targetAccountId` set → resolve via the shared 1440 resolver, emit one
34
+ * `[xacct]` line, and on reject return the MCP error content.
35
+ * `houseScope=false decision=allow` cannot occur: a non-house session with a
36
+ * target is denied by the resolver before any allow path.
37
+ */
38
+ export function makeResolveToolAccount(deps) {
39
+ const write = deps.log ?? ((l) => void process.stderr.write(l));
40
+ return (toolName, targetAccountId) => {
41
+ if (!deps.accountId) {
42
+ // Preserve the greppable no-account diagnostic the old refuseNoAccount wrote.
43
+ write(`[memory] tool=${toolName} refuse reason=no-account-context\n`);
44
+ return { reject: errContent(noAccountText(toolName)) };
45
+ }
46
+ const eff = resolveEffectiveAccountCore({ ownAccountId: deps.accountId, houseScope: deps.houseScope, targetAccountId }, deps.isValid);
47
+ if (targetAccountId) {
48
+ const decision = "reject" in eff ? "deny" : "allow";
49
+ const reason = "reject" in eff ? eff.reject : "ok";
50
+ write(`[xacct] op=call tool=${toolName} caller=${deps.accountId.slice(0, 8)} ` +
51
+ `target=${targetAccountId.slice(0, 8)} houseScope=${deps.houseScope ? "true" : "false"} ` +
52
+ `decision=${decision} reason=${reason}\n`);
53
+ }
54
+ if ("reject" in eff) {
55
+ return { reject: errContent(rejectText(eff.reject, (targetAccountId ?? "").slice(0, 8))) };
56
+ }
57
+ return { accountId: eff.accountId };
58
+ };
59
+ }
60
+ //# sourceMappingURL=index-testable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-testable.js","sourceRoot":"","sources":["../src/index-testable.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,EAAE;AACF,4EAA4E;AAC5E,6EAA6E;AAC7E,0EAA0E;AAC1E,8BAA8B;AAC9B,OAAO,EAAE,2BAA2B,EAAE,MAAM,6CAA6C,CAAC;AAK1F,SAAS,UAAU,CAAC,IAAY;IAC9B,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,IAAa,EAAE,CAAC;AAChF,CAAC;AAED;oEACoE;AACpE,SAAS,aAAa,CAAC,QAAgB;IACrC,OAAO,CACL,GAAG,QAAQ,qEAAqE;QAChF,4FAA4F,CAC7F,CAAC;AACJ,CAAC;AAED;uEACuE;AACvE,SAAS,UAAU,CAAC,MAA+D,EAAE,OAAe;IAClG,OAAO,MAAM,KAAK,sBAAsB;QACtC,CAAC,CAAC,kFAAkF;QACpF,CAAC,CAAC,kCAAkC,OAAO,4CAA4C,CAAC;AAC5F,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAKtC;IACC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACxE,OAAO,CAAC,QAAQ,EAAE,eAAe,EAAE,EAAE;QACnC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,8EAA8E;YAC9E,KAAK,CAAC,iBAAiB,QAAQ,qCAAqC,CAAC,CAAC;YACtE,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;QACzD,CAAC;QACD,MAAM,GAAG,GAAG,2BAA2B,CACrC,EAAE,YAAY,EAAE,IAAI,CAAC,SAAS,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,eAAe,EAAE,EAC9E,IAAI,CAAC,OAAO,CACb,CAAC;QACF,IAAI,eAAe,EAAE,CAAC;YACpB,MAAM,QAAQ,GAAG,QAAQ,IAAI,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;YACpD,MAAM,MAAM,GAAG,QAAQ,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;YACnD,KAAK,CACH,wBAAwB,QAAQ,WAAW,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG;gBACtE,UAAU,eAAe,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,eAAe,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,GAAG;gBACzF,YAAY,QAAQ,WAAW,MAAM,IAAI,CAC5C,CAAC;QACJ,CAAC;QACD,IAAI,QAAQ,IAAI,GAAG,EAAE,CAAC;YACpB,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7F,CAAC;QACD,OAAO,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE,CAAC;IACtC,CAAC,CAAC;AACJ,CAAC"}