@rubytech/create-maxy-code 0.1.329 → 0.1.331

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 (130) hide show
  1. package/dist/index.js +19 -2
  2. package/package.json +1 -1
  3. package/payload/platform/config/brand.json +1 -0
  4. package/payload/platform/lib/account-enumeration/dist/index.d.ts +33 -0
  5. package/payload/platform/lib/account-enumeration/dist/index.d.ts.map +1 -1
  6. package/payload/platform/lib/account-enumeration/dist/index.js +76 -0
  7. package/payload/platform/lib/account-enumeration/dist/index.js.map +1 -1
  8. package/payload/platform/lib/account-enumeration/src/index.ts +78 -0
  9. package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
  10. package/payload/platform/plugins/admin/PLUGIN.md +13 -1
  11. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-authoring-skill-gate.test.js +1 -2
  12. package/payload/platform/plugins/admin/mcp/dist/__tests__/admin-authoring-skill-gate.test.js.map +1 -1
  13. package/payload/platform/plugins/admin/mcp/dist/content-producer-authoring-skills.d.ts +1 -1
  14. package/payload/platform/plugins/admin/mcp/dist/content-producer-authoring-skills.d.ts.map +1 -1
  15. package/payload/platform/plugins/admin/mcp/dist/content-producer-authoring-skills.js +18 -13
  16. package/payload/platform/plugins/admin/mcp/dist/content-producer-authoring-skills.js.map +1 -1
  17. package/payload/platform/plugins/admin/mcp/dist/index.js +102 -0
  18. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  19. package/payload/platform/plugins/admin/mcp/dist/tools/__tests__/account-lifecycle.test.d.ts +2 -0
  20. package/payload/platform/plugins/admin/mcp/dist/tools/__tests__/account-lifecycle.test.d.ts.map +1 -0
  21. package/payload/platform/plugins/admin/mcp/dist/tools/__tests__/account-lifecycle.test.js +57 -0
  22. package/payload/platform/plugins/admin/mcp/dist/tools/__tests__/account-lifecycle.test.js.map +1 -0
  23. package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.d.ts +34 -0
  24. package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.d.ts.map +1 -0
  25. package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.js +78 -0
  26. package/payload/platform/plugins/admin/mcp/dist/tools/account-lifecycle.js.map +1 -0
  27. package/payload/platform/plugins/admin/skills/plainly/SKILL.md +7 -0
  28. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +2 -2
  29. package/payload/platform/plugins/contacts/mcp/dist/index.js +1 -1
  30. package/payload/platform/plugins/contacts/mcp/dist/index.js.map +1 -1
  31. package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-lookup.test.d.ts +2 -0
  32. package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-lookup.test.d.ts.map +1 -0
  33. package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-lookup.test.js +71 -0
  34. package/payload/platform/plugins/contacts/mcp/dist/tools/__tests__/contact-lookup.test.js.map +1 -0
  35. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.d.ts +1 -0
  36. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.d.ts.map +1 -1
  37. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.js +10 -3
  38. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.js.map +1 -1
  39. package/payload/platform/plugins/contacts/mcp/package.json +4 -2
  40. package/payload/platform/plugins/docs/references/admin-ui.md +1 -1
  41. package/payload/platform/plugins/memory/.claude-plugin/plugin.json +1 -1
  42. package/payload/platform/plugins/memory/PLUGIN.md +1 -1
  43. package/payload/platform/plugins/memory/mcp/dist/index.js +3 -1
  44. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  45. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-lookup-by-name.test.js +39 -0
  46. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-lookup-by-name.test.js.map +1 -1
  47. package/payload/platform/plugins/memory/mcp/dist/tools/memory-lookup-by-name.d.ts.map +1 -1
  48. package/payload/platform/plugins/memory/mcp/dist/tools/memory-lookup-by-name.js +13 -1
  49. package/payload/platform/plugins/memory/mcp/dist/tools/memory-lookup-by-name.js.map +1 -1
  50. package/payload/platform/plugins/slides/skills/deck-system/SKILL.md +1 -1
  51. package/payload/platform/scripts/__tests__/provision-role-stamp.test.sh +40 -0
  52. package/payload/platform/scripts/__tests__/sweep-durability.test.sh +103 -0
  53. package/payload/platform/scripts/lib/provision-account-dir.sh +319 -0
  54. package/payload/platform/scripts/lib/resolve-account-dir.sh +79 -132
  55. package/payload/platform/scripts/setup-account.sh +28 -361
  56. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  57. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +4 -0
  58. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  59. package/payload/platform/services/claude-session-manager/dist/config.d.ts +18 -0
  60. package/payload/platform/services/claude-session-manager/dist/config.d.ts.map +1 -1
  61. package/payload/platform/services/claude-session-manager/dist/config.js +63 -7
  62. package/payload/platform/services/claude-session-manager/dist/config.js.map +1 -1
  63. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  64. package/payload/platform/services/claude-session-manager/dist/http-server.js +44 -2
  65. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  66. package/payload/platform/services/claude-session-manager/dist/index.js +50 -2
  67. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  68. package/payload/platform/services/claude-session-manager/dist/rc-child-log.d.ts +13 -0
  69. package/payload/platform/services/claude-session-manager/dist/rc-child-log.d.ts.map +1 -0
  70. package/payload/platform/services/claude-session-manager/dist/rc-child-log.js +52 -0
  71. package/payload/platform/services/claude-session-manager/dist/rc-child-log.js.map +1 -0
  72. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
  73. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +7 -0
  74. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
  75. package/payload/platform/services/claude-session-manager/dist/session-sidecar.d.ts +5 -0
  76. package/payload/platform/services/claude-session-manager/dist/session-sidecar.d.ts.map +1 -1
  77. package/payload/platform/services/claude-session-manager/dist/session-sidecar.js +8 -0
  78. package/payload/platform/services/claude-session-manager/dist/session-sidecar.js.map +1 -1
  79. package/payload/platform/services/claude-session-manager/dist/stuck-turn.d.ts +70 -0
  80. package/payload/platform/services/claude-session-manager/dist/stuck-turn.d.ts.map +1 -0
  81. package/payload/platform/services/claude-session-manager/dist/stuck-turn.js +213 -0
  82. package/payload/platform/services/claude-session-manager/dist/stuck-turn.js.map +1 -0
  83. package/payload/platform/services/webchat-channel/dist/instructions.d.ts.map +1 -1
  84. package/payload/platform/services/webchat-channel/dist/instructions.js +25 -1
  85. package/payload/platform/services/webchat-channel/dist/instructions.js.map +1 -1
  86. package/payload/platform/services/webchat-channel/dist/notification.d.ts +17 -0
  87. package/payload/platform/services/webchat-channel/dist/notification.d.ts.map +1 -1
  88. package/payload/platform/services/webchat-channel/dist/notification.js +14 -0
  89. package/payload/platform/services/webchat-channel/dist/notification.js.map +1 -1
  90. package/payload/platform/services/webchat-channel/dist/server.d.ts.map +1 -1
  91. package/payload/platform/services/webchat-channel/dist/server.js +66 -2
  92. package/payload/platform/services/webchat-channel/dist/server.js.map +1 -1
  93. package/payload/platform/services/webchat-channel/dist/turn-follow.d.ts +28 -0
  94. package/payload/platform/services/webchat-channel/dist/turn-follow.d.ts.map +1 -0
  95. package/payload/platform/services/webchat-channel/dist/turn-follow.js +188 -0
  96. package/payload/platform/services/webchat-channel/dist/turn-follow.js.map +1 -0
  97. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
  98. package/payload/platform/services/whatsapp-channel/dist/notification.js +24 -1
  99. package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
  100. package/payload/platform/templates/account.json +2 -0
  101. package/payload/premium-plugins/management-consulting/skills/pitch-deck/SKILL.md +44 -37
  102. package/payload/server/public/assets/AdminLoginScreens-DexSvtjD.js +1 -0
  103. package/payload/server/public/assets/AdminShell-Bf9tndNg.js +1 -0
  104. package/payload/server/public/assets/{Checkbox-DWGpkJD5.js → Checkbox-D58GsKoQ.js} +1 -1
  105. package/payload/server/public/assets/{OperatorConversations-BWu6GMQM.css → OperatorConversations-CDdp2nVn.css} +1 -1
  106. package/payload/server/public/assets/OperatorConversations-RmqANYz8.js +9 -0
  107. package/payload/server/public/assets/{admin-CnIlm4Yi.js → admin-CH5Vkkt4.js} +1 -1
  108. package/payload/server/public/assets/{browser-ePr5LxPd.js → browser-BwDtzSlr.js} +1 -1
  109. package/payload/server/public/assets/chat-3GY0LmYH.js +1 -0
  110. package/payload/server/public/assets/data-CttrzhfL.js +1 -0
  111. package/payload/server/public/assets/{graph-B5uxt5YB.js → graph-CZSdF2jv.js} +3 -3
  112. package/payload/server/public/assets/{graph-labels-fx-bv3Y-.js → graph-labels-Bi0fu8Ns.js} +1 -1
  113. package/payload/server/public/assets/operator-BEivxmRy.js +1 -0
  114. package/payload/server/public/assets/page-DUvT07SB.js +30 -0
  115. package/payload/server/public/assets/{public-DXAdRKJe.js → public-B3eIlVbd.js} +1 -1
  116. package/payload/server/public/browser.html +4 -4
  117. package/payload/server/public/chat.html +5 -5
  118. package/payload/server/public/data.html +4 -4
  119. package/payload/server/public/graph.html +6 -6
  120. package/payload/server/public/index.html +6 -6
  121. package/payload/server/public/operator.html +7 -7
  122. package/payload/server/public/public.html +5 -5
  123. package/payload/server/server.js +314 -222
  124. package/payload/server/public/assets/AdminLoginScreens-DstNf4M-.js +0 -1
  125. package/payload/server/public/assets/AdminShell-B5EcXCYR.js +0 -1
  126. package/payload/server/public/assets/OperatorConversations-CSQnX7Dx.js +0 -9
  127. package/payload/server/public/assets/chat-XjXSornk.js +0 -1
  128. package/payload/server/public/assets/data-DuObS80U.js +0 -1
  129. package/payload/server/public/assets/operator-CIGXegWd.js +0 -1
  130. package/payload/server/public/assets/page-BVOAwj-e.js +0 -30
package/dist/index.js CHANGED
@@ -2848,14 +2848,31 @@ function registerSpecialistAgents() {
2848
2848
  // time for the post-install banner.
2849
2849
  // ---------------------------------------------------------------------------
2850
2850
  function resolveInstallAccountId() {
2851
+ // Managed-service model: prefer the single `role:"house"` account; fall back
2852
+ // to the sole account in the pre-migration window. Returns "" on no accounts
2853
+ // or genuine drift (zero-house with >1, or multiple houses).
2851
2854
  const accountsDir = join(INSTALL_DIR, "data/accounts");
2852
2855
  if (!existsSync(accountsDir))
2853
2856
  return "";
2854
2857
  try {
2858
+ const candidates = [];
2859
+ const houses = [];
2855
2860
  for (const d of readdirSync(accountsDir)) {
2856
- if (existsSync(join(accountsDir, d, "account.json")))
2857
- return d;
2861
+ const configPath = join(accountsDir, d, "account.json");
2862
+ if (!existsSync(configPath))
2863
+ continue;
2864
+ candidates.push(d);
2865
+ try {
2866
+ const cfg = JSON.parse(readFileSync(configPath, "utf-8"));
2867
+ if (cfg.role === "house")
2868
+ houses.push(d);
2869
+ }
2870
+ catch { /* unparseable — not a house candidate */ }
2858
2871
  }
2872
+ if (houses.length === 1)
2873
+ return houses[0];
2874
+ if (houses.length === 0 && candidates.length === 1)
2875
+ return candidates[0];
2859
2876
  }
2860
2877
  catch { /* directory unreadable */ }
2861
2878
  return "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy-code",
3
- "version": "0.1.329",
3
+ "version": "0.1.331",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy-code": "./dist/index.js"
@@ -17,6 +17,7 @@
17
17
  "commercialMode": false,
18
18
  "vertical": "schema-knowledge-work",
19
19
  "shipsPremiumBundles": ["venture-studio", "writer-craft", "teaching", "management-consulting"],
20
+ "primaryContainer": { "label": "Project", "term": "Projects" },
20
21
 
21
22
  "defaultColors": {
22
23
  "primary": "#7C8C72",
@@ -6,6 +6,39 @@
6
6
  * be hiding).
7
7
  */
8
8
  export declare function enumerateValidAccountIds(accountsDir: string): string[];
9
+ /**
10
+ * Returns the single `role:"house"` account UUID under `accountsDir`.
11
+ *
12
+ * The registry is the enumerated set of valid account dirs (see
13
+ * `enumerateValidAccountIds`); among them, exactly one must carry
14
+ * `role:"house"`. Throws on zero-house or multiple-house — both are drift the
15
+ * managed-service invariant forbids (`houses=<n>` in the message is the failure
16
+ * signature operators grep for).
17
+ *
18
+ * Deliberately NOT cached: house designation can change at runtime when the
19
+ * operator provisions/archives accounts via the lifecycle tools, so the role
20
+ * lookup reads `account.json` fresh each call. (`enumerateValidAccountIds` is
21
+ * still cached for the dir-validity scan; that set is stable for the process.)
22
+ *
23
+ * Legacy tolerance is intentionally NOT here: a pre-migration account with no
24
+ * `role` does not count as house. `setup-account.sh` stamps `role:"house"` on a
25
+ * sole unlabelled account at seed time, so by manager boot the invariant holds.
26
+ */
27
+ export declare function resolveHouseAccountId(accountsDir: string): string;
28
+ /**
29
+ * Like `resolveHouseAccountId`, but tolerant of the pre-migration window: when
30
+ * no account carries `role:"house"` AND exactly one valid account exists, that
31
+ * sole account is returned (it is about to be stamped `role:"house"` by
32
+ * `setup-account.sh`). This is the resolver every "the install's account" call
33
+ * site uses (boot account dir, installer account id, channel persist), so a
34
+ * legacy single-account install never throws while waiting for the seed-time
35
+ * role stamp — the WhatsApp persist path in particular must not drop a customer
36
+ * message in that window.
37
+ *
38
+ * Still loud-fails on genuine drift: zero accounts, multiple houses, or
39
+ * zero-house with more than one account (`houses=<n> total=<k>` signature).
40
+ */
41
+ export declare function resolveHouseOrSoleAccountId(accountsDir: string): string;
9
42
  /**
10
43
  * Resolves the canonical accounts dir from `MAXY_PLATFORM_ROOT`. Mirrors the
11
44
  * pattern at platform/ui/app/lib/claude-agent/account.ts:9
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA6BA;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAgCtE;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAS9C;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,mCAAmC,GAC3C,SAAS,GACT,oBAAoB,GACpB,UAAU,CAAC;AAEf,MAAM,MAAM,sBAAsB,GAC9B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,GAC9C;IACE,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,mCAAmC,CAAC;IAC5C,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEN,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,OAAO,EAAE,MAAM,EAAE,GAChB,sBAAsB,CAWxB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA6BA;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAgCtE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAsBjE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,2BAA2B,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAqBvE;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,CAS9C;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,mCAAmC,GAC3C,SAAS,GACT,oBAAoB,GACpB,UAAU,CAAC;AAEf,MAAM,MAAM,sBAAsB,GAC9B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,GAC9C;IACE,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,mCAAmC,CAAC;IAC5C,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEN,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,OAAO,EAAE,MAAM,EAAE,GAChB,sBAAsB,CAWxB"}
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.enumerateValidAccountIds = enumerateValidAccountIds;
4
+ exports.resolveHouseAccountId = resolveHouseAccountId;
5
+ exports.resolveHouseOrSoleAccountId = resolveHouseOrSoleAccountId;
4
6
  exports.getAccountsDirFromEnv = getAccountsDirFromEnv;
5
7
  exports._resetEnumerationCache = _resetEnumerationCache;
6
8
  exports.validateAccountIdEnv = validateAccountIdEnv;
@@ -71,6 +73,80 @@ function enumerateValidAccountIds(accountsDir) {
71
73
  cache.set(accountsDir, valid);
72
74
  return valid;
73
75
  }
76
+ /**
77
+ * Returns the single `role:"house"` account UUID under `accountsDir`.
78
+ *
79
+ * The registry is the enumerated set of valid account dirs (see
80
+ * `enumerateValidAccountIds`); among them, exactly one must carry
81
+ * `role:"house"`. Throws on zero-house or multiple-house — both are drift the
82
+ * managed-service invariant forbids (`houses=<n>` in the message is the failure
83
+ * signature operators grep for).
84
+ *
85
+ * Deliberately NOT cached: house designation can change at runtime when the
86
+ * operator provisions/archives accounts via the lifecycle tools, so the role
87
+ * lookup reads `account.json` fresh each call. (`enumerateValidAccountIds` is
88
+ * still cached for the dir-validity scan; that set is stable for the process.)
89
+ *
90
+ * Legacy tolerance is intentionally NOT here: a pre-migration account with no
91
+ * `role` does not count as house. `setup-account.sh` stamps `role:"house"` on a
92
+ * sole unlabelled account at seed time, so by manager boot the invariant holds.
93
+ */
94
+ function resolveHouseAccountId(accountsDir) {
95
+ const valid = enumerateValidAccountIds(accountsDir);
96
+ const houses = [];
97
+ for (const id of valid) {
98
+ const configPath = (0, node_path_1.resolve)(accountsDir, id, "account.json");
99
+ try {
100
+ const cfg = JSON.parse((0, node_fs_1.readFileSync)(configPath, "utf-8"));
101
+ if (cfg.role === "house")
102
+ houses.push(id);
103
+ }
104
+ catch {
105
+ // already excluded from `valid` if unparseable; a race here just skips it
106
+ }
107
+ }
108
+ if (houses.length !== 1) {
109
+ throw new Error(`[account-registry] resolveHouseAccountId: houses=${houses.length} under ${accountsDir} ` +
110
+ `(${houses.join(", ") || "none"}) — exactly one role:"house" account is required. ` +
111
+ `Loud-fail rather than picking one silently.`);
112
+ }
113
+ return houses[0];
114
+ }
115
+ /**
116
+ * Like `resolveHouseAccountId`, but tolerant of the pre-migration window: when
117
+ * no account carries `role:"house"` AND exactly one valid account exists, that
118
+ * sole account is returned (it is about to be stamped `role:"house"` by
119
+ * `setup-account.sh`). This is the resolver every "the install's account" call
120
+ * site uses (boot account dir, installer account id, channel persist), so a
121
+ * legacy single-account install never throws while waiting for the seed-time
122
+ * role stamp — the WhatsApp persist path in particular must not drop a customer
123
+ * message in that window.
124
+ *
125
+ * Still loud-fails on genuine drift: zero accounts, multiple houses, or
126
+ * zero-house with more than one account (`houses=<n> total=<k>` signature).
127
+ */
128
+ function resolveHouseOrSoleAccountId(accountsDir) {
129
+ const valid = enumerateValidAccountIds(accountsDir);
130
+ const houses = [];
131
+ for (const id of valid) {
132
+ const configPath = (0, node_path_1.resolve)(accountsDir, id, "account.json");
133
+ try {
134
+ const cfg = JSON.parse((0, node_fs_1.readFileSync)(configPath, "utf-8"));
135
+ if (cfg.role === "house")
136
+ houses.push(id);
137
+ }
138
+ catch {
139
+ // already excluded from `valid` if unparseable
140
+ }
141
+ }
142
+ if (houses.length === 1)
143
+ return houses[0];
144
+ if (houses.length === 0 && valid.length === 1)
145
+ return valid[0];
146
+ throw new Error(`[account-registry] resolveHouseOrSoleAccountId: houses=${houses.length} total=${valid.length} ` +
147
+ `under ${accountsDir} — expected exactly one role:"house" (or a single unlabelled account). ` +
148
+ `Loud-fail rather than picking one silently.`);
149
+ }
74
150
  /**
75
151
  * Resolves the canonical accounts dir from `MAXY_PLATFORM_ROOT`. Mirrors the
76
152
  * pattern at platform/ui/app/lib/claude-agent/account.ts:9
@@ -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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"}
@@ -68,6 +68,84 @@ export function enumerateValidAccountIds(accountsDir: string): string[] {
68
68
  return valid;
69
69
  }
70
70
 
71
+ /**
72
+ * Returns the single `role:"house"` account UUID under `accountsDir`.
73
+ *
74
+ * The registry is the enumerated set of valid account dirs (see
75
+ * `enumerateValidAccountIds`); among them, exactly one must carry
76
+ * `role:"house"`. Throws on zero-house or multiple-house — both are drift the
77
+ * managed-service invariant forbids (`houses=<n>` in the message is the failure
78
+ * signature operators grep for).
79
+ *
80
+ * Deliberately NOT cached: house designation can change at runtime when the
81
+ * operator provisions/archives accounts via the lifecycle tools, so the role
82
+ * lookup reads `account.json` fresh each call. (`enumerateValidAccountIds` is
83
+ * still cached for the dir-validity scan; that set is stable for the process.)
84
+ *
85
+ * Legacy tolerance is intentionally NOT here: a pre-migration account with no
86
+ * `role` does not count as house. `setup-account.sh` stamps `role:"house"` on a
87
+ * sole unlabelled account at seed time, so by manager boot the invariant holds.
88
+ */
89
+ export function resolveHouseAccountId(accountsDir: string): string {
90
+ const valid = enumerateValidAccountIds(accountsDir);
91
+ const houses: string[] = [];
92
+ for (const id of valid) {
93
+ const configPath = resolve(accountsDir, id, "account.json");
94
+ try {
95
+ const cfg = JSON.parse(readFileSync(configPath, "utf-8")) as {
96
+ role?: unknown;
97
+ };
98
+ if (cfg.role === "house") houses.push(id);
99
+ } catch {
100
+ // already excluded from `valid` if unparseable; a race here just skips it
101
+ }
102
+ }
103
+ if (houses.length !== 1) {
104
+ throw new Error(
105
+ `[account-registry] resolveHouseAccountId: houses=${houses.length} under ${accountsDir} ` +
106
+ `(${houses.join(", ") || "none"}) — exactly one role:"house" account is required. ` +
107
+ `Loud-fail rather than picking one silently.`,
108
+ );
109
+ }
110
+ return houses[0];
111
+ }
112
+
113
+ /**
114
+ * Like `resolveHouseAccountId`, but tolerant of the pre-migration window: when
115
+ * no account carries `role:"house"` AND exactly one valid account exists, that
116
+ * sole account is returned (it is about to be stamped `role:"house"` by
117
+ * `setup-account.sh`). This is the resolver every "the install's account" call
118
+ * site uses (boot account dir, installer account id, channel persist), so a
119
+ * legacy single-account install never throws while waiting for the seed-time
120
+ * role stamp — the WhatsApp persist path in particular must not drop a customer
121
+ * message in that window.
122
+ *
123
+ * Still loud-fails on genuine drift: zero accounts, multiple houses, or
124
+ * zero-house with more than one account (`houses=<n> total=<k>` signature).
125
+ */
126
+ export function resolveHouseOrSoleAccountId(accountsDir: string): string {
127
+ const valid = enumerateValidAccountIds(accountsDir);
128
+ const houses: string[] = [];
129
+ for (const id of valid) {
130
+ const configPath = resolve(accountsDir, id, "account.json");
131
+ try {
132
+ const cfg = JSON.parse(readFileSync(configPath, "utf-8")) as {
133
+ role?: unknown;
134
+ };
135
+ if (cfg.role === "house") houses.push(id);
136
+ } catch {
137
+ // already excluded from `valid` if unparseable
138
+ }
139
+ }
140
+ if (houses.length === 1) return houses[0];
141
+ if (houses.length === 0 && valid.length === 1) return valid[0];
142
+ throw new Error(
143
+ `[account-registry] resolveHouseOrSoleAccountId: houses=${houses.length} total=${valid.length} ` +
144
+ `under ${accountsDir} — expected exactly one role:"house" (or a single unlabelled account). ` +
145
+ `Loud-fail rather than picking one silently.`,
146
+ );
147
+ }
148
+
71
149
  /**
72
150
  * Resolves the canonical accounts dir from `MAXY_PLATFORM_ROOT`. Mirrors the
73
151
  * pattern at platform/ui/app/lib/claude-agent/account.ts:9
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "admin",
3
- "description": "Platform administration plugin. Provides system-status, public-hostname (deterministic Cloudflare public-URL resolver, single call returning the operator's canonical hostname so agents never guess property names on :CloudflareHostname nodes), publish-site (move an extracted static-site tree under <accountDir>/sites/<slug>/, emit one canonical path slug, and refresh llms.txt at the site root — typed refusal taxonomy replaces the prior skill-prose contract), brand-settings, account-manage, capabilities-here (deterministic install-introspection — returns enabledPlugins, tier, models, installed platform + premium specialists, registered Claude Code agent types, and the brand block as a single JSON object; the canonical tool for grounding any claim about what is enabled on the current account), account-update, admin-add, admin-remove, admin-list, admin-update-pin, agent-list, agent-config-read, logs-read, plugin-read, skill-load (one-call resolve+read for SKILL.md by skill name, the canonical primitive for loading a named skill; plugin-read remains the reader for references/* and PLUGIN.md), skill-search (free-text intent → skill resolver: ranks every plugin skill's SKILL.md name+description against a query and returns the top owners with their canonical skills/<slug>/ path, so intent→skill mapping survives the available-skills menu leaving context after a compaction), store-skill (deterministic write counterpart to plugin-read; persists operator-authored skills as plugin files under the active account), session-reset, session-resume, wifi, and action-approval tools (action-pending, action-approve, action-reject, action-edit) for managing the Maxy platform.",
3
+ "description": "Platform administration plugin. Provides system-status, public-hostname (deterministic Cloudflare public-URL resolver, single call returning the operator's canonical hostname so agents never guess property names on :CloudflareHostname nodes), publish-site (move an extracted static-site tree under <accountDir>/sites/<slug>/, emit one canonical path slug, and refresh llms.txt at the site root — typed refusal taxonomy replaces the prior skill-prose contract), brand-settings, account-manage, capabilities-here (deterministic install-introspection — returns enabledPlugins, tier, models, installed platform + premium specialists, registered Claude Code agent types, and the brand block as a single JSON object; the canonical tool for grounding any claim about what is enabled on the current account), account-update, the multi-account managed-service lifecycle tools (account_list, account_create, account_delete, account_purge — one Rubytech operator hosting N client accounts on one house install; create/delete/purge are operator-only and purge is two-step with a typed confirmation), admin-add, admin-remove, admin-list, admin-update-pin, agent-list, agent-config-read, logs-read, plugin-read, skill-load (one-call resolve+read for SKILL.md by skill name, the canonical primitive for loading a named skill; plugin-read remains the reader for references/* and PLUGIN.md), skill-search (free-text intent → skill resolver: ranks every plugin skill's SKILL.md name+description against a query and returns the top owners with their canonical skills/<slug>/ path, so intent→skill mapping survives the available-skills menu leaving context after a compaction), store-skill (deterministic write counterpart to plugin-read; persists operator-authored skills as plugin files under the active account), session-reset, session-resume, wifi, and action-approval tools (action-pending, action-approve, action-reject, action-edit) for managing the Maxy platform.",
4
4
  "version": "0.1.0",
5
5
  "author": {
6
6
  "name": "Rubytech LLC"
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: admin
3
3
  surface: platform
4
- description: "Platform administration plugin. Provides system-status, public-hostname (deterministic Cloudflare public-URL resolver, single call returning the operator's canonical hostname so agents never guess property names on :CloudflareHostname nodes), publish-site (move an extracted static-site tree under <accountDir>/sites/<slug>/, emit one canonical path slug, and refresh llms.txt at the site root — typed refusal taxonomy replaces the prior skill-prose contract), brand-settings, account-manage, capabilities-here (deterministic install-introspection — returns enabledPlugins, tier, models, installed platform + premium specialists, registered Claude Code agent types, and the brand block as a single JSON object; the canonical tool for grounding any claim about what is enabled on the current account), account-update, admin-add, admin-remove, admin-list, admin-update-pin, agent-list, agent-config-read, logs-read, plugin-read, skill-load (one-call resolve+read for SKILL.md by skill name, the canonical primitive for loading a named skill; plugin-read remains the reader for references/* and PLUGIN.md), skill-search (free-text intent → skill resolver: ranks every plugin skill's SKILL.md name+description against a query and returns the top owners with their canonical skills/<slug>/ path, so intent→skill mapping survives the available-skills menu leaving context after a compaction), store-skill (deterministic write counterpart to plugin-read; persists operator-authored skills as plugin files under the active account), session-reset, session-resume, wifi, and action-approval tools (action-pending, action-approve, action-reject, action-edit) for managing the Maxy platform."
4
+ description: "Platform administration plugin. Provides system-status, public-hostname (deterministic Cloudflare public-URL resolver, single call returning the operator's canonical hostname so agents never guess property names on :CloudflareHostname nodes), publish-site (move an extracted static-site tree under <accountDir>/sites/<slug>/, emit one canonical path slug, and refresh llms.txt at the site root — typed refusal taxonomy replaces the prior skill-prose contract), brand-settings, account-manage, capabilities-here (deterministic install-introspection — returns enabledPlugins, tier, models, installed platform + premium specialists, registered Claude Code agent types, and the brand block as a single JSON object; the canonical tool for grounding any claim about what is enabled on the current account), account-update, the multi-account managed-service lifecycle tools (account_list, account_create, account_delete, account_purge — one Rubytech operator hosting N client accounts on one house install; create/delete/purge are operator-only and purge is two-step with a typed confirmation), admin-add, admin-remove, admin-list, admin-update-pin, agent-list, agent-config-read, logs-read, plugin-read, skill-load (one-call resolve+read for SKILL.md by skill name, the canonical primitive for loading a named skill; plugin-read remains the reader for references/* and PLUGIN.md), skill-search (free-text intent → skill resolver: ranks every plugin skill's SKILL.md name+description against a query and returns the top owners with their canonical skills/<slug>/ path, so intent→skill mapping survives the available-skills menu leaving context after a compaction), store-skill (deterministic write counterpart to plugin-read; persists operator-authored skills as plugin files under the active account), session-reset, session-resume, wifi, and action-approval tools (action-pending, action-approve, action-reject, action-edit) for managing the Maxy platform."
5
5
  tools:
6
6
  - name: system-status
7
7
  publicAllowlist: false
@@ -24,6 +24,18 @@ tools:
24
24
  - name: account-update
25
25
  publicAllowlist: false
26
26
  adminAllowlist: false
27
+ - name: account_list
28
+ publicAllowlist: false
29
+ adminAllowlist: true
30
+ - name: account_create
31
+ publicAllowlist: false
32
+ adminAllowlist: true
33
+ - name: account_delete
34
+ publicAllowlist: false
35
+ adminAllowlist: true
36
+ - name: account_purge
37
+ publicAllowlist: false
38
+ adminAllowlist: true
27
39
  - name: admin-add
28
40
  publicAllowlist: false
29
41
  adminAllowlist: true
@@ -27,12 +27,11 @@ describe("isAdminSeat", () => {
27
27
  });
28
28
  });
29
29
  describe("CONTENT_PRODUCER_AUTHORING_SKILLS", () => {
30
- it("is the render/publish authoring skills, the consulting prose deliverables, and the relocated property brochure", () => {
30
+ it("is the render/publish authoring skills, the consulting prose deliverables, and the relocated property brochure (pitch-deck is admin-orchestrated, not owned)", () => {
31
31
  expect([...CONTENT_PRODUCER_AUTHORING_SKILLS].sort()).toEqual([
32
32
  "a4-print-documents",
33
33
  "decision-memo",
34
34
  "narrative-builder",
35
- "pitch-deck",
36
35
  "professional-document",
37
36
  "property-brochure",
38
37
  "publish-site",
@@ -1 +1 @@
1
- {"version":3,"file":"admin-authoring-skill-gate.test.js","sourceRoot":"","sources":["../../src/__tests__/admin-authoring-skill-gate.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"admin-authoring-skill-gate.test.js","sourceRoot":"","sources":["../../src/__tests__/admin-authoring-skill-gate.test.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,EAAE;AACF,mEAAmE;AACnE,wEAAwE;AACxE,yEAAyE;AACzE,0EAA0E;AAC1E,gEAAgE;AAChE,oEAAoE;AACpE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EACL,iCAAiC,EACjC,WAAW,EACX,qBAAqB,EACrB,wBAAwB,GACzB,MAAM,wBAAwB,CAAC;AAEhC,MAAM,KAAK,GAAG,EAAE,iBAAiB,EAAE,OAAO,EAAE,eAAe,EAAE,EAAE,EAAE,CAAC;AAClE,MAAM,gBAAgB,GAAG,EAAE,iBAAiB,EAAE,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,CAAC;AAC7F,MAAM,MAAM,GAAG,EAAE,iBAAiB,EAAE,QAAQ,EAAE,eAAe,EAAE,EAAE,EAAE,CAAC;AAEpE,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtC,MAAM,CAAC,WAAW,CAAC,EAAE,iBAAiB,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,uBAAuB;IACzF,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,CAAC,WAAW,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,2BAA2B,EAAE,GAAG,EAAE;QACnC,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,2EAA2E,EAAE,GAAG,EAAE;QACnF,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,mCAAmC,EAAE,GAAG,EAAE;IACjD,EAAE,CAAC,8JAA8J,EAAE,GAAG,EAAE;QACtK,MAAM,CAAC,CAAC,GAAG,iCAAiC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAC3D;YACE,oBAAoB;YACpB,eAAe;YACf,mBAAmB;YACnB,uBAAuB;YACvB,mBAAmB;YACnB,cAAc;SACf,CAAC,IAAI,EAAE,CACT,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,8DAA8D,EAAE,GAAG,EAAE;QACtE,oEAAoE;QACpE,iEAAiE;QACjE,MAAM,CAAC,iCAAiC,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC9E,MAAM,CAAC,iCAAiC,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;IACrC,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,MAAM,CAAC,qBAAqB,CAAC,OAAO,EAAE,uCAAuC,CAAC,CAAC,CAAC,IAAI,CAClF,uBAAuB,CACxB,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,CAAC,qBAAqB,CAAC,OAAO,EAAE,8CAA8C,CAAC,CAAC,CAAC,IAAI,CACzF,uBAAuB,CACxB,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,CAAC,qBAAqB,CAAC,2BAA2B,EAAE,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC/F,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,MAAM,CAAC,qBAAqB,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QACpE,MAAM,CAAC,qBAAqB,CAAC,OAAO,EAAE,uBAAuB,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAClF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,0BAA0B,EAAE,GAAG,EAAE;IACxC,EAAE,CAAC,yEAAyE,EAAE,GAAG,EAAE;QACjF,KAAK,MAAM,IAAI,IAAI,iCAAiC,EAAE,CAAC;YACrD,MAAM,CAAC,wBAAwB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,2EAA2E;QAC3E,8EAA8E;QAC9E,KAAK,MAAM,IAAI,IAAI,CAAC,SAAS,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,kBAAkB,CAAC,EAAE,CAAC;YAC7F,MAAM,CAAC,wBAAwB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,MAAM,CAAC,wBAAwB,CAAC,uBAAuB,EAAE,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1F,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,CAAC,wBAAwB,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChF,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,CAAC,wBAAwB,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC/D,MAAM,CAAC,wBAAwB,CAAC,uBAAuB,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,2 +1,2 @@
1
- export declare const CONTENT_PRODUCER_AUTHORING_SKILL_SLUGS: readonly ["professional-document", "a4-print-documents", "publish-site", "decision-memo", "narrative-builder", "pitch-deck", "property-brochure"];
1
+ export declare const CONTENT_PRODUCER_AUTHORING_SKILL_SLUGS: readonly ["professional-document", "a4-print-documents", "publish-site", "decision-memo", "narrative-builder", "property-brochure"];
2
2
  //# sourceMappingURL=content-producer-authoring-skills.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"content-producer-authoring-skills.d.ts","sourceRoot":"","sources":["../src/content-producer-authoring-skills.ts"],"names":[],"mappings":"AA0BA,eAAO,MAAM,sCAAsC,mJAQzC,CAAC"}
1
+ {"version":3,"file":"content-producer-authoring-skills.d.ts","sourceRoot":"","sources":["../src/content-producer-authoring-skills.ts"],"names":[],"mappings":"AAgCA,eAAO,MAAM,sCAAsC,qIAOzC,CAAC"}
@@ -12,25 +12,30 @@
12
12
  // generator's declaration scan already excludes them; this list is the gate's
13
13
  // compiled-in guard against a declaration regression.
14
14
  //
15
- // The first three are the original render/publish/print authoring skills. The
16
- // next three are the management-consulting prose deliverables. The last is the
17
- // property brochure: like the consulting deliverables, its output is finished
18
- // external-facing prose, so it composes on content-producer (the authoring seat,
19
- // which carries plainly + prose-craft + voice-retrieve-conditioning). The 19
20
- // management-consulting analysis-framework skills stay admin-run and are not
21
- // listed here. The founder fundraise pack (investor-data-room) is the other
22
- // remaining admin-run voice consumer but cannot move here: it must dispatch the
23
- // research-assistant specialist for its mandatory citation routing, which
24
- // content-producer (a leaf authoring seat with no Task/Agent tool) cannot do.
25
- // Until that is re-architected, voice-retrieve-conditioning stays on the admin
26
- // surface for investor-data-room.
15
+ // The first three are the original render/publish/print authoring skills.
16
+ // decision-memo and narrative-builder are management-consulting prose
17
+ // deliverables; property-brochure is the estate-agent brochure. Each is
18
+ // finished external-facing prose composed end-to-end on content-producer (the
19
+ // authoring seat, which carries plainly + prose-craft + voice-retrieve-
20
+ // conditioning), so the admin must dispatch them rather than author them.
21
+ //
22
+ // pitch-deck is deliberately NOT here (Task 979). The admin orchestrates it: it
23
+ // asks the operator the deck kind, light or dark, and the colour set — choices
24
+ // only the admin can put to the operator and delegates just the compose and
25
+ // render steps to content-producer. So the admin must be able to load it; gating
26
+ // it would lock those operator choices inside a subagent that cannot ask.
27
+ //
28
+ // The 19 management-consulting analysis-framework skills stay admin-run and are
29
+ // not listed here. The founder fundraise pack (investor-data-room) stays
30
+ // admin-run, keeping voice-retrieve-conditioning on the admin surface, until its
31
+ // mandatory research-assistant citation routing can move off a leaf authoring
32
+ // seat that has no Task/Agent tool.
27
33
  export const CONTENT_PRODUCER_AUTHORING_SKILL_SLUGS = [
28
34
  "professional-document",
29
35
  "a4-print-documents",
30
36
  "publish-site",
31
37
  "decision-memo",
32
38
  "narrative-builder",
33
- "pitch-deck",
34
39
  "property-brochure",
35
40
  ];
36
41
  //# sourceMappingURL=content-producer-authoring-skills.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"content-producer-authoring-skills.js","sourceRoot":"","sources":["../src/content-producer-authoring-skills.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,gFAAgF;AAChF,+EAA+E;AAC/E,8EAA8E;AAC9E,gFAAgF;AAChF,oDAAoD;AACpD,EAAE;AACF,wEAAwE;AACxE,4EAA4E;AAC5E,6EAA6E;AAC7E,+EAA+E;AAC/E,8EAA8E;AAC9E,sDAAsD;AACtD,EAAE;AACF,8EAA8E;AAC9E,+EAA+E;AAC/E,8EAA8E;AAC9E,iFAAiF;AACjF,6EAA6E;AAC7E,6EAA6E;AAC7E,4EAA4E;AAC5E,gFAAgF;AAChF,0EAA0E;AAC1E,8EAA8E;AAC9E,+EAA+E;AAC/E,kCAAkC;AAClC,MAAM,CAAC,MAAM,sCAAsC,GAAG;IACpD,uBAAuB;IACvB,oBAAoB;IACpB,cAAc;IACd,eAAe;IACf,mBAAmB;IACnB,YAAY;IACZ,mBAAmB;CACX,CAAC"}
1
+ {"version":3,"file":"content-producer-authoring-skills.js","sourceRoot":"","sources":["../src/content-producer-authoring-skills.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,gFAAgF;AAChF,+EAA+E;AAC/E,8EAA8E;AAC9E,gFAAgF;AAChF,oDAAoD;AACpD,EAAE;AACF,wEAAwE;AACxE,4EAA4E;AAC5E,6EAA6E;AAC7E,+EAA+E;AAC/E,8EAA8E;AAC9E,sDAAsD;AACtD,EAAE;AACF,0EAA0E;AAC1E,sEAAsE;AACtE,wEAAwE;AACxE,8EAA8E;AAC9E,wEAAwE;AACxE,0EAA0E;AAC1E,EAAE;AACF,gFAAgF;AAChF,+EAA+E;AAC/E,8EAA8E;AAC9E,iFAAiF;AACjF,0EAA0E;AAC1E,EAAE;AACF,gFAAgF;AAChF,yEAAyE;AACzE,iFAAiF;AACjF,8EAA8E;AAC9E,oCAAoC;AACpC,MAAM,CAAC,MAAM,sCAAsC,GAAG;IACpD,uBAAuB;IACvB,oBAAoB;IACpB,cAAc;IACd,eAAe;IACf,mBAAmB;IACnB,mBAAmB;CACX,CAAC"}
@@ -26,6 +26,7 @@ import { publishSite } from "./tools/publish-site.js";
26
26
  import { resolveCapabilitiesHere } from "./tools/capabilities-here.js";
27
27
  import { runCatalogueCensus } from "./lib/catalogue-census.js";
28
28
  import { lifelineTool } from "../../../../lib/mcp-lifeline/dist/index.js";
29
+ import { listAccountsFromDir, isHouseAccount, archiveAccountDir, provisionClientAccount, ACCOUNT_NODE_COUNT_CYPHER, ACCOUNT_PURGE_CYPHER, } from "./tools/account-lifecycle.js";
29
30
  function refusalGuidance(kind) {
30
31
  switch (kind) {
31
32
  case "unsafe-slug":
@@ -3180,6 +3181,107 @@ eagerTool(server, "action-edit", "Edit a pending action's input and then execute
3180
3181
  };
3181
3182
  }
3182
3183
  });
3184
+ // ---------------------------------------------------------------------------
3185
+ // Multi-account lifecycle (Task 983) — admin-core only, never public/specialist.
3186
+ // One Rubytech operator hosts N client accounts on one brand install: one
3187
+ // role:"house" account receives customer-channel inbound; clients are
3188
+ // operator-provisioned tenants. Account scope is a property (n.accountId), never
3189
+ // an edge. The accounts root is install-wide, so these tools resolve it from
3190
+ // PLATFORM_ROOT rather than the per-session ACCOUNT_ID.
3191
+ // ---------------------------------------------------------------------------
3192
+ const ACCOUNTS_ROOT = resolve(PLATFORM_ROOT, "..", "data/accounts");
3193
+ eagerTool(server, "account_list", "List every account on this install (one house + N clients) with its role. Managed-service operator tool — the active account drives which account a switched session reads and writes.", {}, async () => {
3194
+ const accounts = listAccountsFromDir(ACCOUNTS_ROOT);
3195
+ const lines = accounts.map((a) => `${a.accountId} role=${a.role}${a.isHouse ? " (house)" : ""}`);
3196
+ return {
3197
+ content: [{ type: "text", text: accounts.length ? lines.join("\n") : "No accounts found." }],
3198
+ };
3199
+ });
3200
+ eagerTool(server, "account_create", "Provision a new CLIENT account on this install (managed service). Mints a UUID, scaffolds the account directory (agents, specialists, settings, account.json with role:\"client\"), seeds default plugins, and adds you (the install owner) to the account's admins so you can switch to it. Returns the new accountId. Operator-only; never creates the house account (that is the install itself).", {}, async () => {
3201
+ if (!ACCOUNT_ID)
3202
+ return refuseNoAccount("account_create");
3203
+ const newId = randomUUID();
3204
+ try {
3205
+ const accountDir = provisionClientAccount({
3206
+ platformRoot: PLATFORM_ROOT,
3207
+ accountsDir: ACCOUNTS_ROOT,
3208
+ accountId: newId,
3209
+ usersFile: USERS_FILE,
3210
+ });
3211
+ const accountJson = join(accountDir, "account.json");
3212
+ const dirExists = existsSync(accountJson);
3213
+ let role = "client";
3214
+ try {
3215
+ role = String(JSON.parse(readFileSync(accountJson, "utf-8")).role ?? "client");
3216
+ }
3217
+ catch { /* keep default */ }
3218
+ process.stderr.write(`[account-lifecycle] op=create accountId=${newId} dir-exists=${dirExists} role=${role}\n`);
3219
+ if (!dirExists) {
3220
+ return { content: [{ type: "text", text: `account_create FAILED: scaffold did not produce ${accountJson}` }], isError: true };
3221
+ }
3222
+ return { content: [{ type: "text", text: `Created client account ${newId}. Switch to it from the account picker to work in its data.` }] };
3223
+ }
3224
+ catch (err) {
3225
+ const msg = err instanceof Error ? err.message : String(err);
3226
+ process.stderr.write(`[account-lifecycle] op=create accountId=${newId} dir-exists=false role=client error=${msg}\n`);
3227
+ return { content: [{ type: "text", text: `account_create FAILED: ${msg}` }], isError: true };
3228
+ }
3229
+ });
3230
+ eagerTool(server, "account_delete", "Archive a CLIENT account: move its directory to data/accounts/.trash/ so it no longer appears in the account list. Graph data is RETAINED — run account_purge with the typed confirmation to irreversibly delete it. Refuses to archive the house account. Pass the accountId to archive.", {
3231
+ accountId: z.string().describe("The client accountId (UUID) to archive."),
3232
+ }, async ({ accountId }) => {
3233
+ if (!ACCOUNT_ID)
3234
+ return refuseNoAccount("account_delete");
3235
+ if (!existsSync(join(ACCOUNTS_ROOT, accountId, "account.json"))) {
3236
+ return { content: [{ type: "text", text: `account_delete: ${accountId} is not a registered account on this install.` }], isError: true };
3237
+ }
3238
+ if (isHouseAccount(ACCOUNTS_ROOT, accountId)) {
3239
+ return { content: [{ type: "text", text: `account_delete refused: ${accountId} is the house account and cannot be archived.` }], isError: true };
3240
+ }
3241
+ const ts = new Date().toISOString().replace(/[:.]/g, "").replace(/-/g, "");
3242
+ try {
3243
+ const { trashPath, dirAbsent } = archiveAccountDir(ACCOUNTS_ROOT, accountId, ts);
3244
+ process.stderr.write(`[account-lifecycle] op=archive accountId=${accountId} trash-path=${trashPath} dir-absent=${dirAbsent}\n`);
3245
+ return { content: [{ type: "text", text: `Archived ${accountId} to ${trashPath}. Graph data retained — run account_purge to delete it permanently.` }] };
3246
+ }
3247
+ catch (err) {
3248
+ const msg = err instanceof Error ? err.message : String(err);
3249
+ return { content: [{ type: "text", text: `account_delete FAILED: ${msg}` }], isError: true };
3250
+ }
3251
+ });
3252
+ eagerTool(server, "account_purge", "IRREVERSIBLY delete every graph node carrying an account's accountId (DETACH DELETE). Two-step safety: account_delete archives first; this purges. Requires a typed confirmation equal to the accountId itself — any mismatch refuses. Refuses the house account. Returns the node count before and after (after must be zero).", {
3253
+ accountId: z.string().describe("The accountId (UUID) whose graph data to purge."),
3254
+ confirm: z.string().describe("Type the accountId exactly to confirm the irreversible purge."),
3255
+ }, async ({ accountId, confirm }) => {
3256
+ if (!ACCOUNT_ID)
3257
+ return refuseNoAccount("account_purge");
3258
+ if (isHouseAccount(ACCOUNTS_ROOT, accountId)) {
3259
+ return { content: [{ type: "text", text: `account_purge refused: ${accountId} is the house account.` }], isError: true };
3260
+ }
3261
+ if (confirm !== accountId) {
3262
+ return { content: [{ type: "text", text: `account_purge refused: confirmation does not match the accountId. Type the accountId exactly to confirm.` }], isError: true };
3263
+ }
3264
+ const session = getSession();
3265
+ try {
3266
+ const before = await session.run(ACCOUNT_NODE_COUNT_CYPHER, { id: accountId });
3267
+ const beforeCount = Number(before.records[0]?.get("c") ?? 0);
3268
+ await session.run(ACCOUNT_PURGE_CYPHER, { id: accountId });
3269
+ const after = await session.run(ACCOUNT_NODE_COUNT_CYPHER, { id: accountId });
3270
+ const afterCount = Number(after.records[0]?.get("c") ?? 0);
3271
+ process.stderr.write(`[account-lifecycle] op=purge accountId=${accountId} nodes-before=${beforeCount} nodes-after=${afterCount}\n`);
3272
+ if (afterCount !== 0) {
3273
+ return { content: [{ type: "text", text: `account_purge INCOMPLETE: ${afterCount} node(s) still carry accountId ${accountId} (was ${beforeCount}).` }], isError: true };
3274
+ }
3275
+ return { content: [{ type: "text", text: `Purged ${beforeCount} node(s) for account ${accountId}. This is irreversible.` }] };
3276
+ }
3277
+ catch (err) {
3278
+ const msg = err instanceof Error ? err.message : String(err);
3279
+ return { content: [{ type: "text", text: `account_purge FAILED: ${msg}` }], isError: true };
3280
+ }
3281
+ finally {
3282
+ await session.close();
3283
+ }
3284
+ });
3183
3285
  // Cleanup on exit (SIGTERM for systemd service stops on Pi)
3184
3286
  const cleanup = async () => {
3185
3287
  await closeDriver();