@rubytech/create-maxy-code 0.1.417 → 0.1.423

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 (165) hide show
  1. package/dist/__tests__/brand-port-uniqueness.test.js +74 -0
  2. package/dist/known-brands.js +1 -0
  3. package/package.json +1 -1
  4. package/payload/platform/config/brand-registry.json +8 -0
  5. package/payload/platform/lib/active-rules/dist/index.d.ts +35 -0
  6. package/payload/platform/lib/active-rules/dist/index.d.ts.map +1 -0
  7. package/payload/platform/lib/active-rules/dist/index.js +94 -0
  8. package/payload/platform/lib/active-rules/dist/index.js.map +1 -0
  9. package/payload/platform/lib/active-rules/package.json +7 -0
  10. package/payload/platform/lib/active-rules/src/index.test.ts +89 -0
  11. package/payload/platform/lib/active-rules/src/index.ts +133 -0
  12. package/payload/platform/lib/active-rules/tsconfig.json +9 -0
  13. package/payload/platform/package-lock.json +5158 -2922
  14. package/payload/platform/package.json +4 -3
  15. package/payload/platform/plugins/admin/hooks/__tests__/prompt-optimiser-compliance.test.sh +22 -0
  16. package/payload/platform/plugins/admin/hooks/prompt-optimiser-compliance.sh +14 -1
  17. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +16 -2
  18. package/payload/platform/plugins/docs/references/internals.md +10 -0
  19. package/payload/platform/plugins/docs/references/memory-guide.md +4 -0
  20. package/payload/platform/plugins/docs/references/outlook-guide.md +1 -1
  21. package/payload/platform/plugins/memory/PLUGIN.md +3 -0
  22. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-read-active-rules.test.d.ts +2 -0
  23. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-read-active-rules.test.d.ts.map +1 -0
  24. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-read-active-rules.test.js +49 -0
  25. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-read-active-rules.test.js.map +1 -0
  26. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-reconcile.test.d.ts +2 -0
  27. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-reconcile.test.d.ts.map +1 -0
  28. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-reconcile.test.js +99 -0
  29. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-reconcile.test.js.map +1 -0
  30. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.d.ts +3 -0
  31. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.d.ts.map +1 -1
  32. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js +11 -3
  33. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js.map +1 -1
  34. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts +21 -0
  35. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts.map +1 -1
  36. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js +106 -14
  37. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js.map +1 -1
  38. package/payload/platform/plugins/memory/skills/conversational-memory/SKILL.md +30 -1
  39. package/payload/platform/plugins/memory/skills/preference-audit/SKILL.md +50 -0
  40. package/payload/platform/plugins/outlook/mcp/dist/__tests__/calendar-write.test.js +24 -0
  41. package/payload/platform/plugins/outlook/mcp/dist/__tests__/calendar-write.test.js.map +1 -1
  42. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-create.d.ts +15 -2
  43. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-create.d.ts.map +1 -1
  44. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-create.js +16 -7
  45. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-create.js.map +1 -1
  46. package/payload/platform/plugins/whatsapp/PLUGIN.md +3 -1
  47. package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/resolve-read-account.test.d.ts +2 -0
  48. package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/resolve-read-account.test.d.ts.map +1 -0
  49. package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/resolve-read-account.test.js +15 -0
  50. package/payload/platform/plugins/whatsapp/mcp/dist/__tests__/resolve-read-account.test.js.map +1 -0
  51. package/payload/platform/plugins/whatsapp/mcp/dist/index.js +14 -8
  52. package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
  53. package/payload/platform/plugins/whatsapp/mcp/dist/resolve-read-account.d.ts +7 -0
  54. package/payload/platform/plugins/whatsapp/mcp/dist/resolve-read-account.d.ts.map +1 -0
  55. package/payload/platform/plugins/whatsapp/mcp/dist/resolve-read-account.js +13 -0
  56. package/payload/platform/plugins/whatsapp/mcp/dist/resolve-read-account.js.map +1 -0
  57. package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +9 -1
  58. package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-raised-by-env.test.d.ts +2 -0
  59. package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-raised-by-env.test.d.ts.map +1 -0
  60. package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-raised-by-env.test.js +66 -0
  61. package/payload/platform/plugins/work/mcp/dist/__tests__/work-create-raised-by-env.test.js.map +1 -0
  62. package/payload/platform/plugins/work/mcp/dist/tools/work-create.d.ts.map +1 -1
  63. package/payload/platform/plugins/work/mcp/dist/tools/work-create.js +8 -1
  64. package/payload/platform/plugins/work/mcp/dist/tools/work-create.js.map +1 -1
  65. package/payload/platform/scripts/__tests__/provision-role-stamp.test.sh +3 -0
  66. package/payload/platform/scripts/lib/provision-account-dir.sh +9 -1
  67. package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.d.ts +1 -1
  68. package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.d.ts.map +1 -1
  69. package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.js +5 -1
  70. package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.js.map +1 -1
  71. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +1 -0
  72. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  73. package/payload/platform/services/claude-session-manager/dist/http-server.js +65 -4
  74. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  75. package/payload/platform/services/claude-session-manager/dist/index.js +53 -0
  76. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  77. package/payload/platform/services/claude-session-manager/dist/passive-dispatch-prune.d.ts +37 -0
  78. package/payload/platform/services/claude-session-manager/dist/passive-dispatch-prune.d.ts.map +1 -0
  79. package/payload/platform/services/claude-session-manager/dist/passive-dispatch-prune.js +80 -0
  80. package/payload/platform/services/claude-session-manager/dist/passive-dispatch-prune.js.map +1 -0
  81. package/payload/platform/services/claude-session-manager/dist/passive-intake-audit.d.ts +70 -0
  82. package/payload/platform/services/claude-session-manager/dist/passive-intake-audit.d.ts.map +1 -0
  83. package/payload/platform/services/claude-session-manager/dist/passive-intake-audit.js +129 -0
  84. package/payload/platform/services/claude-session-manager/dist/passive-intake-audit.js.map +1 -0
  85. package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.d.ts +46 -0
  86. package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.d.ts.map +1 -0
  87. package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.js +96 -0
  88. package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.js.map +1 -0
  89. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +39 -0
  90. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  91. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +66 -5
  92. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  93. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.d.ts.map +1 -1
  94. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js +5 -1
  95. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js.map +1 -1
  96. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts +6 -0
  97. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -1
  98. package/payload/platform/services/claude-session-manager/dist/system-prompt.js +20 -0
  99. package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
  100. package/payload/platform/services/telegram-channel/dist/instructions.d.ts +9 -0
  101. package/payload/platform/services/telegram-channel/dist/instructions.d.ts.map +1 -0
  102. package/payload/platform/services/telegram-channel/dist/instructions.js +80 -0
  103. package/payload/platform/services/telegram-channel/dist/instructions.js.map +1 -0
  104. package/payload/platform/services/telegram-channel/dist/notification.d.ts +93 -0
  105. package/payload/platform/services/telegram-channel/dist/notification.d.ts.map +1 -0
  106. package/payload/platform/services/telegram-channel/dist/notification.js +119 -0
  107. package/payload/platform/services/telegram-channel/dist/notification.js.map +1 -0
  108. package/payload/platform/services/telegram-channel/dist/server.d.ts +40 -0
  109. package/payload/platform/services/telegram-channel/dist/server.d.ts.map +1 -0
  110. package/payload/platform/services/telegram-channel/dist/server.js +291 -0
  111. package/payload/platform/services/telegram-channel/dist/server.js.map +1 -0
  112. package/payload/platform/services/telegram-channel/dist/turn-follow.d.ts +28 -0
  113. package/payload/platform/services/telegram-channel/dist/turn-follow.d.ts.map +1 -0
  114. package/payload/platform/services/telegram-channel/dist/turn-follow.js +188 -0
  115. package/payload/platform/services/telegram-channel/dist/turn-follow.js.map +1 -0
  116. package/payload/platform/services/webchat-channel/dist/instructions.d.ts.map +1 -1
  117. package/payload/platform/services/webchat-channel/dist/instructions.js +37 -78
  118. package/payload/platform/services/webchat-channel/dist/instructions.js.map +1 -1
  119. package/payload/platform/services/webchat-channel/dist/notification.d.ts +5 -0
  120. package/payload/platform/services/webchat-channel/dist/notification.d.ts.map +1 -1
  121. package/payload/platform/services/webchat-channel/dist/notification.js +6 -1
  122. package/payload/platform/services/webchat-channel/dist/notification.js.map +1 -1
  123. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +20 -36
  124. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
  125. package/payload/platform/services/whatsapp-channel/dist/notification.js +43 -81
  126. package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
  127. package/payload/platform/templates/agents/admin/IDENTITY.md +2 -1
  128. package/payload/platform/templates/agents/passive/IDENTITY.md +21 -0
  129. package/payload/platform/templates/specialists/agents/content-producer.md +4 -0
  130. package/payload/server/public/assets/{AdminLoginScreens-CBTx7ZPK.js → AdminLoginScreens-B1P6PrZ3.js} +1 -1
  131. package/payload/server/public/assets/AdminShell-Y1fHQApK.js +2 -0
  132. package/payload/server/public/assets/{Checkbox-BHTxekRB.js → Checkbox-CgCqOslz.js} +1 -1
  133. package/payload/server/public/assets/{Transcript-5kLbBInN.js → Transcript-DZzFjhoq.js} +1 -1
  134. package/payload/server/public/assets/{admin-BWCLE32N.js → admin-BRBjJEci.js} +1 -1
  135. package/payload/server/public/assets/{browser-CRvCPsvV.js → browser-ID7iqIi4.js} +1 -1
  136. package/payload/server/public/assets/{calendar-BI-kd-QP.js → calendar-BEQPMTtd.js} +1 -1
  137. package/payload/server/public/assets/chat-D0GDSWM-.js +1 -0
  138. package/payload/server/public/assets/chevron-left-A0HYKwGY.js +1 -0
  139. package/payload/server/public/assets/data-CV38dClT.js +1 -0
  140. package/payload/server/public/assets/{graph-BFSyW_5i.js → graph-BK0MjKRy.js} +1 -1
  141. package/payload/server/public/assets/{graph-labels-DEjD_qJh.js → graph-labels-BfG6FO25.js} +1 -1
  142. package/payload/server/public/assets/{maximize-2-BcMJNeCB.js → maximize-2-DrBaKD3z.js} +1 -1
  143. package/payload/server/public/assets/{operator-Dtvm9ero.js → operator-CnpNyIJ8.js} +1 -1
  144. package/payload/server/public/assets/page-DC_L-IS1.js +32 -0
  145. package/payload/server/public/assets/{page-TXi9dmaB.js → page-DKHLflh7.js} +1 -1
  146. package/payload/server/public/assets/{public-DBx3U0r8.js → public-ByUJr48E.js} +1 -1
  147. package/payload/server/public/assets/{rotate-ccw-8ti3QkeY.js → rotate-ccw-D45TbeAI.js} +1 -1
  148. package/payload/server/public/assets/useSubAccountSwitcher-CCn22f98.css +1 -0
  149. package/payload/server/public/assets/useSubAccountSwitcher-CprHkBnZ.js +9 -0
  150. package/payload/server/public/browser.html +4 -4
  151. package/payload/server/public/calendar.html +5 -5
  152. package/payload/server/public/chat.html +9 -9
  153. package/payload/server/public/data.html +8 -8
  154. package/payload/server/public/graph.html +9 -9
  155. package/payload/server/public/index.html +11 -11
  156. package/payload/server/public/operator.html +11 -11
  157. package/payload/server/public/public.html +9 -9
  158. package/payload/server/server.js +406 -99
  159. package/payload/server/public/assets/AdminShell-DPHUeXea.js +0 -2
  160. package/payload/server/public/assets/chat-DFy8hb1y.js +0 -1
  161. package/payload/server/public/assets/chevron-left-Bggm5B5U.js +0 -1
  162. package/payload/server/public/assets/data-HvZ69Ya1.js +0 -1
  163. package/payload/server/public/assets/page-C_pm3hl3.js +0 -32
  164. package/payload/server/public/assets/useSubAccountSwitcher-BUvlETHN.css +0 -1
  165. package/payload/server/public/assets/useSubAccountSwitcher-Bc5dOBdI.js +0 -9
@@ -116,8 +116,84 @@ import {
116
116
  __toESM
117
117
  } from "./chunk-PFF6I7KP.js";
118
118
 
119
- // ../lib/graph-trash/dist/index.js
119
+ // ../lib/active-rules/dist/index.js
120
120
  var require_dist = __commonJS({
121
+ "../lib/active-rules/dist/index.js"(exports) {
122
+ "use strict";
123
+ Object.defineProperty(exports, "__esModule", { value: true });
124
+ exports.ACTIVE_CANDIDATE_LIMIT = exports.ACTIVE_CANDIDATE_FLOOR = exports.INJECTION_RULE_CAP = exports.SAME_SUBJECT_COSINE = void 0;
125
+ exports.cosine = cosine;
126
+ exports.deconflictBySubject = deconflictBySubject;
127
+ exports.formatStandingRulesBlock = formatStandingRulesBlock2;
128
+ exports.queryActiveRuleCandidates = queryActiveRuleCandidates;
129
+ exports.activeRuleSet = activeRuleSet2;
130
+ exports.SAME_SUBJECT_COSINE = 0.85;
131
+ exports.INJECTION_RULE_CAP = 12;
132
+ exports.ACTIVE_CANDIDATE_FLOOR = 0.4;
133
+ exports.ACTIVE_CANDIDATE_LIMIT = 30;
134
+ function cosine(a, b) {
135
+ if (!a.length || !b.length || a.length !== b.length)
136
+ return 0;
137
+ let dot = 0;
138
+ let na = 0;
139
+ let nb = 0;
140
+ for (let i = 0; i < a.length; i++) {
141
+ dot += a[i] * b[i];
142
+ na += a[i] * a[i];
143
+ nb += b[i] * b[i];
144
+ }
145
+ if (na === 0 || nb === 0)
146
+ return 0;
147
+ return dot / (Math.sqrt(na) * Math.sqrt(nb));
148
+ }
149
+ function deconflictBySubject(records, cosineThreshold = exports.SAME_SUBJECT_COSINE) {
150
+ const winners = [];
151
+ for (const r of records) {
152
+ const emb = r.embedding;
153
+ const isDup = Array.isArray(emb) && emb.length > 0 && winners.some((w) => Array.isArray(w.embedding) && w.embedding.length > 0 && cosine(emb, w.embedding) >= cosineThreshold);
154
+ if (!isDup)
155
+ winners.push(r);
156
+ }
157
+ return winners.map(({ embedding: _embedding, ...rest }) => rest);
158
+ }
159
+ function formatStandingRulesBlock2(rules, cap = exports.INJECTION_RULE_CAP) {
160
+ if (rules.length === 0)
161
+ return "";
162
+ const lines = rules.slice(0, cap).map((r) => `- ${r.value}`);
163
+ return `## Standing rules
164
+ ${lines.join("\n")}`;
165
+ }
166
+ async function queryActiveRuleCandidates(session, accountId) {
167
+ const result = await session.run(`
168
+ MATCH (pref:Preference {accountId: $accountId, scope: 'admin'})
169
+ WHERE NOT pref:Trashed AND pref.deletedAt IS NULL
170
+ AND coalesce(pref.notApplicable, false) = false
171
+ AND pref.confidence >= $floor
172
+ RETURN pref.preferenceId AS preferenceId, pref.category AS category,
173
+ pref.key AS key, pref.value AS value, pref.confidence AS confidence,
174
+ pref.observedAt AS observedAt, pref.embedding AS embedding
175
+ ORDER BY pref.confidence DESC, pref.observedAt DESC
176
+ LIMIT ${exports.ACTIVE_CANDIDATE_LIMIT}
177
+ `, { accountId, floor: exports.ACTIVE_CANDIDATE_FLOOR });
178
+ return result.records.map((r) => ({
179
+ preferenceId: r.get("preferenceId"),
180
+ category: r.get("category"),
181
+ key: r.get("key"),
182
+ value: r.get("value"),
183
+ confidence: r.get("confidence"),
184
+ observedAt: r.get("observedAt") ?? "",
185
+ embedding: r.get("embedding") ?? null
186
+ }));
187
+ }
188
+ async function activeRuleSet2(session, accountId, cap = exports.INJECTION_RULE_CAP) {
189
+ const candidates = await queryActiveRuleCandidates(session, accountId);
190
+ return deconflictBySubject(candidates).slice(0, cap);
191
+ }
192
+ }
193
+ });
194
+
195
+ // ../lib/graph-trash/dist/index.js
196
+ var require_dist2 = __commonJS({
121
197
  "../lib/graph-trash/dist/index.js"(exports) {
122
198
  "use strict";
123
199
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -398,7 +474,7 @@ var require_dist = __commonJS({
398
474
  });
399
475
 
400
476
  // ../lib/graph-style/dist/index.js
401
- var require_dist2 = __commonJS({
477
+ var require_dist3 = __commonJS({
402
478
  "../lib/graph-style/dist/index.js"(exports) {
403
479
  "use strict";
404
480
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -781,7 +857,7 @@ var require_conversation_provenance = __commonJS({
781
857
  "use strict";
782
858
  Object.defineProperty(exports, "__esModule", { value: true });
783
859
  exports.injectConversationProvenance = injectConversationProvenance;
784
- var index_js_1 = require_dist3();
860
+ var index_js_1 = require_dist4();
785
861
  async function injectConversationProvenance(params) {
786
862
  const { session, relationships, accountId, writeLabels, conversationNodeId, logNamespace, tool } = params;
787
863
  const original = [...relationships];
@@ -831,7 +907,7 @@ var require_conversation_provenance = __commonJS({
831
907
  });
832
908
 
833
909
  // ../lib/graph-write/dist/index.js
834
- var require_dist3 = __commonJS({
910
+ var require_dist4 = __commonJS({
835
911
  "../lib/graph-write/dist/index.js"(exports) {
836
912
  "use strict";
837
913
  var __createBinding = exports && exports.__createBinding || (Object.create ? (function(o, m, k, k2) {
@@ -1308,6 +1384,7 @@ var serveStatic = (options = { root: "" }) => {
1308
1384
  // server/index.ts
1309
1385
  import { readFileSync as readFileSync35, existsSync as existsSync33, watchFile } from "fs";
1310
1386
  import { spawn as spawn3 } from "child_process";
1387
+ import { createHash as createHash6 } from "crypto";
1311
1388
  import { resolve as resolve33, join as join35, basename as basename13 } from "path";
1312
1389
  import { homedir as homedir3 } from "os";
1313
1390
  import { monitorEventLoopDelay } from "perf_hooks";
@@ -1739,7 +1816,13 @@ var WhatsAppConfigSchema = z.object({
1739
1816
  accounts: z.record(z.string(), WhatsAppAccountSchema.optional()).optional().describe("Per-account config keyed by account ID."),
1740
1817
  dmPolicy: DmPolicySchema.optional().default("disabled").describe("Who can message your public agent via WhatsApp DM. 'Open' lets anyone message. 'Allowlist' restricts to approved phone numbers. 'Disabled' blocks all public messages."),
1741
1818
  adminPhones: z.array(z.string()).optional().describe("Phone numbers (E.164) that route to the admin agent. Managed via add-admin-phone / remove-admin-phone. Authoritative only on the account that owns the WhatsApp paired socket; a list on any other account is inert and purged at boot \u2014 use accountManagers for sub-account routing."),
1742
- accountManagers: z.record(z.string(), z.string()).optional().describe("Account-manager bindings: E.164 phone \u2192 sub-account UUID. A designated phone spawns an admin session scoped to that sub-account rather than the house. Managed via add-account-manager / remove-account-manager. A phone here must not also be in adminPhones."),
1819
+ accountManagers: z.record(
1820
+ z.string(),
1821
+ z.union([
1822
+ z.string(),
1823
+ z.object({ managesAccount: z.string(), mode: z.enum(["active", "passive"]).optional() })
1824
+ ])
1825
+ ).optional().describe("Account-manager bindings: E.164 phone \u2192 sub-account. The value is a bare sub-account UUID (mode 'active', the default: a full admin session) or { managesAccount, mode } where mode 'passive' selects a constrained requirement-intake spawn (only work-create, no reply). A bare string and an object without mode both resolve to 'active'. Managed via add-account-manager / remove-account-manager. A phone here must not also be in adminPhones."),
1743
1826
  allowFrom: z.array(z.string()).optional().describe("Phone numbers allowed to message the public agent when DM policy is 'allowlist'. Add '*' for open access. Does not affect admin routing \u2014 use adminPhones for that."),
1744
1827
  sendReadReceipts: z.boolean().optional().default(true).describe("Whether to send read receipts (blue ticks) when messages are received. Disabling may feel less responsive but preserves privacy."),
1745
1828
  mediaMaxMb: z.number().int().positive().optional().default(50).describe("Maximum file size in MB for media the agent will download and process."),
@@ -2630,16 +2713,21 @@ function extractMessage(msg) {
2630
2713
  }
2631
2714
 
2632
2715
  // app/lib/whatsapp/managed-account.ts
2716
+ function normalizeEntry(v) {
2717
+ return typeof v === "string" ? { account: v, mode: "active" } : { account: v.managesAccount, mode: v.mode ?? "active" };
2718
+ }
2633
2719
  function managedAccountFor(accountManagers, phone) {
2634
- for (const [managerPhone, subAccountId] of Object.entries(accountManagers)) {
2635
- if (subAccountId && phonesMatch(managerPhone, phone)) return subAccountId;
2720
+ for (const [managerPhone, entry] of Object.entries(accountManagers)) {
2721
+ const { account, mode } = normalizeEntry(entry);
2722
+ if (account && phonesMatch(managerPhone, phone)) return { account, mode };
2636
2723
  }
2637
2724
  return null;
2638
2725
  }
2639
2726
  function managerPhonesForAccount(accountManagers, subAccountId) {
2640
2727
  const phones = [];
2641
- for (const [managerPhone, boundSub] of Object.entries(accountManagers)) {
2642
- if (boundSub && boundSub === subAccountId) phones.push(managerPhone);
2728
+ for (const [managerPhone, entry] of Object.entries(accountManagers)) {
2729
+ const { account } = normalizeEntry(entry);
2730
+ if (account && account === subAccountId) phones.push(managerPhone);
2643
2731
  }
2644
2732
  return phones;
2645
2733
  }
@@ -2670,12 +2758,12 @@ function checkDmAccess(params) {
2670
2758
  if (isAdminPhone(senderPhone, cfg.adminPhones)) {
2671
2759
  return { allowed: true, reason: "admin-binding", agentType: "admin", effectiveAccountId: accountId };
2672
2760
  }
2673
- const managedSub = managedAccountFor(cfg.accountManagers, senderPhone);
2674
- if (managedSub) {
2675
- if (params.isValidAccount && !params.isValidAccount(managedSub)) {
2761
+ const managed = managedAccountFor(cfg.accountManagers, senderPhone);
2762
+ if (managed) {
2763
+ if (params.isValidAccount && !params.isValidAccount(managed.account)) {
2676
2764
  return { allowed: false, reason: "account-manager-unresolved", agentType: "admin" };
2677
2765
  }
2678
- return { allowed: true, reason: "account-manager", agentType: "admin", effectiveAccountId: managedSub };
2766
+ return { allowed: true, reason: "account-manager", agentType: "admin", effectiveAccountId: managed.account, passive: managed.mode === "passive" };
2679
2767
  }
2680
2768
  switch (cfg.dmPolicy) {
2681
2769
  case "open":
@@ -2918,6 +3006,22 @@ async function resolvePersonByPhone(accountId, phone) {
2918
3006
  await session.close();
2919
3007
  }
2920
3008
  }
3009
+ async function ensureManagerPerson(accountId, phone) {
3010
+ const session = getSession2();
3011
+ const normalized = normalizeE164(phone) || phone;
3012
+ try {
3013
+ const result = await session.run(
3014
+ `MERGE (p:Person {accountId: $accountId, phone: $phone})
3015
+ ON CREATE SET p.source = 'wa-passive-manager'
3016
+ RETURN elementId(p) AS personId`,
3017
+ { accountId, phone: normalized }
3018
+ );
3019
+ if (result.records.length === 0) return null;
3020
+ return result.records[0].get("personId");
3021
+ } finally {
3022
+ await session.close();
3023
+ }
3024
+ }
2921
3025
  async function enrolPerson(input) {
2922
3026
  const session = getSession2();
2923
3027
  const sliceToken = randomUUID2();
@@ -4867,6 +4971,7 @@ async function handleInboundMessage(conn, msg) {
4867
4971
  const payload = {
4868
4972
  accountId: conn.accountId,
4869
4973
  agentType: accessResult.agentType,
4974
+ passive: accessResult.passive === true,
4870
4975
  personId: accessResult.personId ?? null,
4871
4976
  senderPhone,
4872
4977
  text: extracted.text,
@@ -6192,6 +6297,9 @@ function createScheduleInjectRoutes(deps) {
6192
6297
  return app57;
6193
6298
  }
6194
6299
 
6300
+ // server/index.ts
6301
+ var import_dist8 = __toESM(require_dist(), 1);
6302
+
6195
6303
  // app/lib/telegram/outbound/send-text.ts
6196
6304
  async function sendTelegramText(botToken, chatId, text) {
6197
6305
  try {
@@ -6414,7 +6522,7 @@ function purgeNonSocketAdminPhonesAtBoot() {
6414
6522
  purgeNonSocketAdminPhones(acct.accountDir, socketOwner);
6415
6523
  }
6416
6524
  }
6417
- function setAccountManager(accountDir, phone, subAccountId) {
6525
+ function setAccountManager(accountDir, phone, subAccountId, mode = "active") {
6418
6526
  const normalized = phone.trim();
6419
6527
  if (!E164_PATTERN.test(normalized)) {
6420
6528
  return { ok: false, error: `Invalid phone format "${normalized}". Expected E.164 (e.g. +441234567890).` };
@@ -6448,7 +6556,7 @@ function setAccountManager(accountDir, phone, subAccountId) {
6448
6556
  if (!wa.accountManagers || typeof wa.accountManagers !== "object") {
6449
6557
  wa.accountManagers = {};
6450
6558
  }
6451
- wa.accountManagers[normalized] = sub;
6559
+ wa.accountManagers[normalized] = mode === "passive" ? { managesAccount: sub, mode: "passive" } : sub;
6452
6560
  const parsed = WhatsAppConfigSchema.safeParse(wa);
6453
6561
  if (!parsed.success) {
6454
6562
  const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
@@ -6456,9 +6564,10 @@ function setAccountManager(accountDir, phone, subAccountId) {
6456
6564
  }
6457
6565
  config.whatsapp = parsed.data;
6458
6566
  writeConfig(accountDir, config);
6459
- console.error(`${TAG17} bound account manager phone=${normalized} managesAccount=${sub}`);
6567
+ console.error(`${TAG17} bound account manager phone=${normalized} managesAccount=${sub} mode=${mode}`);
6460
6568
  reloadManagerConfig(accountDir);
6461
- return { ok: true, message: `Bound ${normalized} as account manager for sub-account ${sub}. Their WhatsApp messages will reach that sub-account's admin agent; replies still go out over this account's WhatsApp device.` };
6569
+ const behaviour = mode === "passive" ? `Their WhatsApp messages will each file one task scoped to that sub-account; the agent sends nothing back.` : `Their WhatsApp messages will reach that sub-account's admin agent; replies still go out over this account's WhatsApp device.`;
6570
+ return { ok: true, message: `Bound ${normalized} as ${mode} account manager for sub-account ${sub}. ${behaviour}` };
6462
6571
  } catch (err) {
6463
6572
  const msg = err instanceof Error ? err.message : String(err);
6464
6573
  console.error(`${TAG17} setAccountManager failed: ${msg}`);
@@ -6503,6 +6612,12 @@ function readAccountManagers(accountDir) {
6503
6612
  const out = {};
6504
6613
  for (const [k, v] of Object.entries(wa.accountManagers)) {
6505
6614
  if (typeof v === "string") out[k] = v;
6615
+ else if (v && typeof v === "object" && typeof v.managesAccount === "string") {
6616
+ const obj = v;
6617
+ const rawMode = obj.mode;
6618
+ const mode = rawMode === void 0 ? void 0 : rawMode === "active" ? "active" : rawMode === "passive" ? "passive" : "passive";
6619
+ out[k] = { managesAccount: obj.managesAccount, mode };
6620
+ }
6506
6621
  }
6507
6622
  return out;
6508
6623
  } catch {
@@ -7189,9 +7304,103 @@ function reconcileCredsOnDisk(opts) {
7189
7304
  return entries;
7190
7305
  }
7191
7306
 
7307
+ // app/lib/whatsapp-reader/select-sessions.ts
7308
+ function isReaderChannelSession(role, channel) {
7309
+ return role === "admin" && (channel === "whatsapp" || channel === "telegram") || role === "public" && (channel === "whatsapp" || channel === "webchat");
7310
+ }
7311
+ function selectReaderChannelSessions(rows) {
7312
+ return rows.filter((r) => isReaderChannelSession(r.role, r.channel)).sort((a, b) => b.startedAt.localeCompare(a.startedAt)).map((r) => ({
7313
+ sessionId: r.sessionId,
7314
+ projectDir: r.projectDir,
7315
+ title: r.title,
7316
+ senderId: r.senderId,
7317
+ startedAt: r.startedAt,
7318
+ channel: r.channel,
7319
+ role: r.role,
7320
+ operatorName: null,
7321
+ whatsappName: null,
7322
+ lastMessageAt: r.lastMessageAt,
7323
+ modelGated: r.modelGated,
7324
+ model: r.model,
7325
+ source: "session"
7326
+ }));
7327
+ }
7328
+ function conversationCounterpartyPhone(remoteJid, earliestSenderTelephone) {
7329
+ if (remoteJid.endsWith("@g.us")) return null;
7330
+ if (remoteJid.endsWith("@s.whatsapp.net")) return jidToE164(remoteJid) ?? earliestSenderTelephone;
7331
+ return earliestSenderTelephone;
7332
+ }
7333
+ function counterpartyBoundToSubAccount(managerPhones, counterpartyPhone) {
7334
+ return counterpartyPhone != null && managerPhones.some((mp) => phonesMatch(mp, counterpartyPhone));
7335
+ }
7336
+ function buildStoreConversationRows(accountId, summaries) {
7337
+ const rows = [];
7338
+ for (const [remoteJid, { first, last, firstNamedSender }] of summaries) {
7339
+ const scope = last.scope;
7340
+ const senderTelephone = conversationCounterpartyPhone(remoteJid, first.senderTelephone);
7341
+ rows.push({
7342
+ sessionId: "",
7343
+ projectDir: "",
7344
+ title: remoteJid,
7345
+ senderId: senderTelephone,
7346
+ startedAt: first.dateSent,
7347
+ channel: "whatsapp",
7348
+ role: scope === "admin" ? "admin" : "public",
7349
+ operatorName: null,
7350
+ whatsappName: firstNamedSender,
7351
+ lastMessageAt: last.dateSent,
7352
+ modelGated: false,
7353
+ model: null,
7354
+ source: "store",
7355
+ accountId,
7356
+ remoteJid,
7357
+ scope
7358
+ });
7359
+ }
7360
+ return rows.sort((a, b) => (b.lastMessageAt ?? "").localeCompare(a.lastMessageAt ?? ""));
7361
+ }
7362
+
7363
+ // app/lib/whatsapp/sub-account-recall.ts
7364
+ function resolveStoreProjection(params) {
7365
+ const { requestedAccountId, houseId, validAccountIds, houseAccountManagers } = params;
7366
+ const requestedIsValid = validAccountIds.includes(requestedAccountId);
7367
+ const isSubAccount = houseId !== null && validAccountIds.length > 1 && requestedIsValid && requestedAccountId !== houseId;
7368
+ if (!isSubAccount) return { storeAccountId: requestedAccountId, managerPhones: null };
7369
+ return {
7370
+ storeAccountId: houseId,
7371
+ managerPhones: managerPhonesForAccount(houseAccountManagers, requestedAccountId)
7372
+ };
7373
+ }
7374
+ function filterConversationsForSubAccount(conversations, managerPhones) {
7375
+ const out = /* @__PURE__ */ new Map();
7376
+ for (const [remoteJid, msgs] of conversations) {
7377
+ const counterparty = conversationCounterpartyPhone(remoteJid, msgs[0]?.senderTelephone ?? "");
7378
+ if (counterpartyBoundToSubAccount(managerPhones, counterparty)) out.set(remoteJid, msgs);
7379
+ }
7380
+ return out;
7381
+ }
7382
+
7192
7383
  // server/routes/whatsapp.ts
7193
7384
  var TAG21 = "[whatsapp:api]";
7194
7385
  var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT || "";
7386
+ function projectionFor(requestedAccountId) {
7387
+ const validAccounts = listValidAccounts();
7388
+ if (validAccounts.length <= 1) return { storeAccountId: requestedAccountId, managerPhones: null };
7389
+ let houseId = null;
7390
+ try {
7391
+ houseId = resolveHouseOrSoleAccountId(ACCOUNTS_DIR);
7392
+ } catch {
7393
+ houseId = null;
7394
+ }
7395
+ const houseAccount = houseId ? validAccounts.find((a) => a.accountId === houseId) : void 0;
7396
+ const houseAccountManagers = houseAccount ? readAccountManagers(houseAccount.accountDir) : {};
7397
+ return resolveStoreProjection({
7398
+ requestedAccountId,
7399
+ houseId,
7400
+ validAccountIds: validAccounts.map((a) => a.accountId),
7401
+ houseAccountManagers
7402
+ });
7403
+ }
7195
7404
  var app2 = new Hono();
7196
7405
  app2.get("/status", (c) => {
7197
7406
  try {
@@ -7272,7 +7481,7 @@ app2.post("/reconnect", async (c) => {
7272
7481
  app2.post("/config", async (c) => {
7273
7482
  try {
7274
7483
  const body = await c.req.json().catch(() => ({}));
7275
- const { action, phone, slug, fields, accountId, managesAccount } = body;
7484
+ const { action, phone, slug, fields, accountId, managesAccount, mode } = body;
7276
7485
  if (!action || typeof action !== "string") {
7277
7486
  return c.json({ ok: false, error: 'Missing required field "action".' }, 400);
7278
7487
  }
@@ -7310,8 +7519,9 @@ app2.post("/config", async (c) => {
7310
7519
  if (!managesAccount || typeof managesAccount !== "string") {
7311
7520
  return c.json({ ok: false, error: 'Missing required field "managesAccount" (the sub-account UUID this phone should manage).' }, 400);
7312
7521
  }
7313
- const result = setAccountManager(account.accountDir, phone, managesAccount);
7314
- console.error(`${TAG21} config action=add-account-manager phone=${phone} managesAccount=${managesAccount} ok=${result.ok}`);
7522
+ const managerMode = mode === "passive" ? "passive" : "active";
7523
+ const result = setAccountManager(account.accountDir, phone, managesAccount, managerMode);
7524
+ console.error(`${TAG21} config action=add-account-manager phone=${phone} managesAccount=${managesAccount} mode=${managerMode} ok=${result.ok}`);
7315
7525
  return c.json(result, result.ok ? 200 : 400);
7316
7526
  }
7317
7527
  case "remove-account-manager": {
@@ -7539,8 +7749,11 @@ app2.get("/conversation-graph-state", async (c) => {
7539
7749
  const accountIdQuery = c.req.query("accountId");
7540
7750
  const t0 = Date.now();
7541
7751
  if (!directCacheKey && !jid) {
7542
- const accountId2 = validateAccountId(accountIdQuery ?? null);
7543
- const conversations = readAllConversations(accountId2);
7752
+ const requestedAccountId = validateAccountId(accountIdQuery ?? null);
7753
+ const projection2 = projectionFor(requestedAccountId);
7754
+ const managerPhonesBound = projection2.managerPhones?.length ?? 0;
7755
+ const allConversations = readAllConversations(projection2.storeAccountId);
7756
+ const conversations = projection2.managerPhones ? filterConversationsForSubAccount(allConversations, projection2.managerPhones) : allConversations;
7544
7757
  const rows2 = [];
7545
7758
  const nameCache = /* @__PURE__ */ new Map();
7546
7759
  let unresolvedSenders = 0;
@@ -7552,7 +7765,7 @@ app2.get("/conversation-graph-state", async (c) => {
7552
7765
  if (!nameCache.has(m.senderTelephone)) {
7553
7766
  const result = await session.run(
7554
7767
  `MATCH (p:Person {telephone: $telephone, accountId: $accountId}) RETURN p.givenName AS givenName, p.familyName AS familyName`,
7555
- { telephone: m.senderTelephone, accountId: accountId2 }
7768
+ { telephone: m.senderTelephone, accountId: projection2.storeAccountId }
7556
7769
  );
7557
7770
  const rec = result.records[0];
7558
7771
  const resolved = rec ? [rec.get("givenName"), rec.get("familyName")].filter(Boolean).join(" ") || null : null;
@@ -7574,8 +7787,8 @@ app2.get("/conversation-graph-state", async (c) => {
7574
7787
  if (session) await session.close();
7575
7788
  }
7576
7789
  const ms2 = Date.now() - t0;
7577
- console.error(`[mcp:whatsapp] tool=whatsapp-conversation-graph-state mode=all-senders accountId=${accountId2} rows=${rows2.length} unresolvedSenders=${unresolvedSenders} ms=${ms2}`);
7578
- return c.json({ accountId: accountId2, rows: rows2, unresolvedSenders, ms: ms2 });
7790
+ console.error(`[whatsapp-read-tool] op=query scope=${requestedAccountId} storeAccount=${projection2.storeAccountId} managerPhonesBound=${managerPhonesBound} resultCount=${rows2.length} unresolvedSenders=${unresolvedSenders} ms=${ms2}`);
7791
+ return c.json({ accountId: requestedAccountId, rows: rows2, unresolvedSenders, ms: ms2, managerPhonesBound });
7579
7792
  }
7580
7793
  let remoteJid;
7581
7794
  let accountId;
@@ -7590,7 +7803,13 @@ app2.get("/conversation-graph-state", async (c) => {
7590
7803
  accountId = validateAccountId(accountIdQuery ?? null);
7591
7804
  remoteJid = jid;
7592
7805
  cacheKey = isGroupJid(remoteJid) ? deriveCacheKey({ agentType: "public", accountId, senderPhone: "", isGroup: true, groupJid: remoteJid }) : deriveCacheKey({ agentType: "public", accountId, senderPhone: remoteJid.replace(/^(\d+)(?::\d+)?@s\.whatsapp\.net$/i, "$1"), isGroup: false });
7593
- const messages = readConversation(accountId, remoteJid);
7806
+ const projection = projectionFor(accountId);
7807
+ const storeMessages = readConversation(projection.storeAccountId, remoteJid);
7808
+ const counterpartyBound = projection.managerPhones == null || counterpartyBoundToSubAccount(
7809
+ projection.managerPhones,
7810
+ conversationCounterpartyPhone(remoteJid, storeMessages[0]?.senderTelephone ?? "")
7811
+ );
7812
+ const messages = counterpartyBound ? storeMessages : [];
7594
7813
  const sessionId = messages[0]?.sessionId ?? null;
7595
7814
  const rows = messages.map((m) => ({
7596
7815
  messageId: m.messageId,
@@ -7600,12 +7819,12 @@ app2.get("/conversation-graph-state", async (c) => {
7600
7819
  body: m.body
7601
7820
  }));
7602
7821
  const ms = Date.now() - t0;
7603
- console.error(`[mcp:whatsapp] tool=whatsapp-conversation-graph-state cacheKey=${cacheKey} graphRows=${rows.length} sessionId=${sessionId ?? "null"} ms=${ms}`);
7822
+ console.error(`[whatsapp-read-tool] op=query mode=single scope=${accountId} storeAccount=${projection.storeAccountId} graphRows=${rows.length} sessionId=${sessionId ?? "null"} ms=${ms}`);
7604
7823
  return c.json({
7605
7824
  cacheKey,
7606
7825
  sessionId,
7607
7826
  graphRows: rows,
7608
- cypher: `on-disk read: ~/.<brand>/data/whatsapp-messages/${accountId}/${remoteJid}.jsonl`,
7827
+ cypher: `on-disk read: ~/.<brand>/data/whatsapp-messages/${projection.storeAccountId}/${remoteJid}.jsonl`,
7609
7828
  ms
7610
7829
  });
7611
7830
  } catch (err) {
@@ -7655,57 +7874,6 @@ import { basename as basename5, dirname as dirname4, isAbsolute, join as join14,
7655
7874
  // server/routes/admin/sidebar-sessions.ts
7656
7875
  import { readdirSync as readdirSync5, readFileSync as readFileSync12, statSync as statSync3 } from "fs";
7657
7876
  import { basename as basename4, dirname as dirname3, join as join11, resolve as resolve11 } from "path";
7658
-
7659
- // app/lib/whatsapp-reader/select-sessions.ts
7660
- function isReaderChannelSession(role, channel) {
7661
- return role === "admin" && (channel === "whatsapp" || channel === "telegram") || role === "public" && (channel === "whatsapp" || channel === "webchat");
7662
- }
7663
- function selectReaderChannelSessions(rows) {
7664
- return rows.filter((r) => isReaderChannelSession(r.role, r.channel)).sort((a, b) => b.startedAt.localeCompare(a.startedAt)).map((r) => ({
7665
- sessionId: r.sessionId,
7666
- projectDir: r.projectDir,
7667
- title: r.title,
7668
- senderId: r.senderId,
7669
- startedAt: r.startedAt,
7670
- channel: r.channel,
7671
- role: r.role,
7672
- operatorName: null,
7673
- whatsappName: null,
7674
- lastMessageAt: r.lastMessageAt,
7675
- modelGated: r.modelGated,
7676
- model: r.model,
7677
- source: "session"
7678
- }));
7679
- }
7680
- function buildStoreConversationRows(accountId, summaries) {
7681
- const rows = [];
7682
- for (const [remoteJid, { first, last, firstNamedSender }] of summaries) {
7683
- const scope = last.scope;
7684
- const isGroup = remoteJid.endsWith("@g.us");
7685
- const senderTelephone = isGroup ? null : remoteJid.endsWith("@s.whatsapp.net") ? jidToE164(remoteJid) ?? first.senderTelephone : first.senderTelephone;
7686
- rows.push({
7687
- sessionId: "",
7688
- projectDir: "",
7689
- title: remoteJid,
7690
- senderId: senderTelephone,
7691
- startedAt: first.dateSent,
7692
- channel: "whatsapp",
7693
- role: scope === "admin" ? "admin" : "public",
7694
- operatorName: null,
7695
- whatsappName: firstNamedSender,
7696
- lastMessageAt: last.dateSent,
7697
- modelGated: false,
7698
- model: null,
7699
- source: "store",
7700
- accountId,
7701
- remoteJid,
7702
- scope
7703
- });
7704
- }
7705
- return rows.sort((a, b) => (b.lastMessageAt ?? "").localeCompare(a.lastMessageAt ?? ""));
7706
- }
7707
-
7708
- // server/routes/admin/sidebar-sessions.ts
7709
7877
  var SESSION_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.jsonl$/i;
7710
7878
  function lastAssistantModel(body) {
7711
7879
  const lines = body.split("\n");
@@ -8179,7 +8347,7 @@ function unwrapChannel(text) {
8179
8347
  }
8180
8348
  return source ? { text: inner, source } : { text: inner };
8181
8349
  }
8182
- var COMPOSED_ADMIN_FRAME = /^## Context\n([\s\S]*)\n\n## Instruction\n[\s\S]*$/;
8350
+ var COMPOSED_ADMIN_FRAME = /^(?:## Standing rules\n[\s\S]*?\n\n)?## Context\n([\s\S]*)\n\n## Instruction\n[\s\S]*$/;
8183
8351
  var UPLOAD_ANNOTATION = /(?:\n\n)?\[The operator uploaded \d+ file\(s\): [\s\S]*\]$/;
8184
8352
  function rawFromComposedAdminFrame(text) {
8185
8353
  const context = text.match(COMPOSED_ADMIN_FRAME)?.[1];
@@ -8546,7 +8714,7 @@ app4.get("/conversations", requireAdminSession, async (c) => {
8546
8714
  managerPhonesBound = managerPhones.length;
8547
8715
  if (managerPhones.length > 0) {
8548
8716
  managedRows = buildStoreConversationRows(houseId, readConversationSummaries(houseId)).filter(
8549
- (r) => r.senderId != null && managerPhones.some((mp) => phonesMatch(mp, r.senderId))
8717
+ (r) => counterpartyBoundToSubAccount(managerPhones, r.senderId)
8550
8718
  );
8551
8719
  }
8552
8720
  }
@@ -15000,7 +15168,7 @@ app21.post("/rename", requireAdminSession, async (c) => {
15000
15168
  var files_default = app21;
15001
15169
 
15002
15170
  // ../lib/graph-search/src/index.ts
15003
- var import_dist = __toESM(require_dist());
15171
+ var import_dist = __toESM(require_dist2());
15004
15172
  import { int } from "neo4j-driver";
15005
15173
 
15006
15174
  // ../lib/graph-search/src/rrf-fusion.ts
@@ -15898,8 +16066,8 @@ var graph_search_default = app22;
15898
16066
  import neo4j2 from "neo4j-driver";
15899
16067
 
15900
16068
  // app/lib/graph-labels.ts
15901
- var import_dist2 = __toESM(require_dist2(), 1);
15902
- var import_dist3 = __toESM(require_dist2(), 1);
16069
+ var import_dist2 = __toESM(require_dist3(), 1);
16070
+ var import_dist3 = __toESM(require_dist3(), 1);
15903
16071
  var HIDDEN_BY_DEFAULT_LABELS = Object.freeze(
15904
16072
  /* @__PURE__ */ new Set(["Chunk", "GraphPreference"])
15905
16073
  );
@@ -17950,7 +18118,10 @@ async function managerSpawn(opts) {
17950
18118
  visitorId: opts.visitorId,
17951
18119
  name: opts.name,
17952
18120
  webchatChannel: opts.webchatChannel,
17953
- previousContext: opts.previousContext
18121
+ previousContext: opts.previousContext,
18122
+ initialMessage: opts.initialMessage,
18123
+ passive: opts.passive,
18124
+ targetAccountId: opts.targetAccountId
17954
18125
  })
17955
18126
  }).catch((err) => ({ __throw: err instanceof Error ? err.message : String(err) }));
17956
18127
  if ("__throw" in res) {
@@ -20438,7 +20609,7 @@ app55.get("/free-busy", async (c) => {
20438
20609
  var calendar_public_default = app55;
20439
20610
 
20440
20611
  // app/lib/graph-health.ts
20441
- var import_dist4 = __toESM(require_dist3(), 1);
20612
+ var import_dist4 = __toESM(require_dist4(), 1);
20442
20613
  var HOUR_MS = 60 * 60 * 1e3;
20443
20614
  function renderLabelTop(rows) {
20444
20615
  return rows.map((b) => {
@@ -21050,6 +21221,9 @@ var InboundHub = class {
21050
21221
  }
21051
21222
  };
21052
21223
 
21224
+ // app/lib/whatsapp/gateway/wa-gateway.ts
21225
+ var import_dist5 = __toESM(require_dist(), 1);
21226
+
21053
21227
  // node_modules/hono/dist/utils/stream.js
21054
21228
  var StreamingApi = class {
21055
21229
  writer;
@@ -21380,6 +21554,23 @@ var WaGateway = class {
21380
21554
  }
21381
21555
  /** Handle one inbound admin WhatsApp message. */
21382
21556
  async handleInbound(input) {
21557
+ if (input.passive) {
21558
+ const msgId = `wa-${++this.seq}`;
21559
+ await this.deps.ensureChannelSession({
21560
+ accountId: input.accountId,
21561
+ effectiveAccountId: input.effectiveAccountId,
21562
+ senderId: input.senderId,
21563
+ // agentType stays 'admin'; the constraint rides the passive flag.
21564
+ role: "admin",
21565
+ personId: null,
21566
+ gatewayUrl: this.deps.gatewayUrl,
21567
+ serverPath: this.deps.serverPath,
21568
+ passive: true,
21569
+ text: input.text,
21570
+ msgId
21571
+ });
21572
+ return;
21573
+ }
21383
21574
  const bytes = Buffer.byteLength(input.text, "utf8");
21384
21575
  const hadSubscriber = this.hub.hasSubscriber(input.senderId);
21385
21576
  const willDeliverNow = this.hub.isReady(input.senderId);
@@ -21388,13 +21579,22 @@ var WaGateway = class {
21388
21579
  const source = input.source ?? "user";
21389
21580
  this.replies.set(input.senderId, input.reply);
21390
21581
  this.docContexts.set(input.senderId, { accountId: input.accountId, maxyAccountId: input.effectiveAccountId });
21582
+ let standingRules;
21583
+ if (this.deps.fetchStandingRules) {
21584
+ standingRules = await this.deps.fetchStandingRules(input.effectiveAccountId) || void 0;
21585
+ const injected = standingRules ? (standingRules.match(/^- /gm) ?? []).length : 0;
21586
+ console.error(
21587
+ `[preference-inject] op=inject channel=wa accountId=${input.effectiveAccountId} injected=${injected} capped=${injected >= import_dist5.INJECTION_RULE_CAP}`
21588
+ );
21589
+ }
21391
21590
  this.hub.deliver(
21392
21591
  {
21393
21592
  senderId: input.senderId,
21394
21593
  text: input.text,
21395
21594
  waMessageId: `wa-${++this.seq}`,
21396
21595
  media: input.media,
21397
- source
21596
+ source,
21597
+ standingRules
21398
21598
  },
21399
21599
  Date.now()
21400
21600
  );
@@ -21455,8 +21655,8 @@ function buildWaSpawnRequest(input) {
21455
21655
 
21456
21656
  // app/lib/whatsapp/inbound/route-decision.ts
21457
21657
  function decideWaRoute(msg) {
21458
- if (msg.agentType === "admin") return { action: "route", role: "admin", personId: null };
21459
- if (msg.personId && msg.personId.length > 0) return { action: "route", role: "public", personId: msg.personId };
21658
+ if (msg.agentType === "admin") return { action: "route", role: "admin", personId: null, passive: msg.passive === true };
21659
+ if (msg.personId && msg.personId.length > 0) return { action: "route", role: "public", personId: msg.personId, passive: false };
21460
21660
  return { action: "drop", reason: "public-unresolved" };
21461
21661
  }
21462
21662
 
@@ -21617,6 +21817,9 @@ var InboundHub2 = class {
21617
21817
  }
21618
21818
  };
21619
21819
 
21820
+ // app/lib/webchat/gateway/webchat-gateway.ts
21821
+ var import_dist6 = __toESM(require_dist(), 1);
21822
+
21620
21823
  // app/lib/webchat/gateway/routes.ts
21621
21824
  function createWebchatChannelRoutes(deps) {
21622
21825
  const app57 = new Hono();
@@ -22051,8 +22254,16 @@ var WebchatGateway = class _WebchatGateway {
22051
22254
  const bytes = Buffer.byteLength(input.text, "utf8");
22052
22255
  const hadSubscriber = this.hub.hasSubscriber(input.key);
22053
22256
  const willDeliverNow = this.hub.isReady(input.key);
22257
+ let standingRules;
22258
+ if (input.role === "admin" && this.deps.fetchStandingRules) {
22259
+ standingRules = await this.deps.fetchStandingRules(input.accountId) || void 0;
22260
+ const injected = standingRules ? (standingRules.match(/^- /gm) ?? []).length : 0;
22261
+ console.error(
22262
+ `[preference-inject] op=inject channel=web accountId=${input.accountId} injected=${injected} capped=${injected >= import_dist6.INJECTION_RULE_CAP}`
22263
+ );
22264
+ }
22054
22265
  this.hub.deliver(
22055
- { key: input.key, text: input.text, messageId: `wc-${++this.seq}` },
22266
+ { key: input.key, text: input.text, messageId: `wc-${++this.seq}`, standingRules },
22056
22267
  Date.now()
22057
22268
  );
22058
22269
  if (willDeliverNow) this.lastDeliveredAt.set(input.key, Date.now());
@@ -22570,6 +22781,9 @@ var InboundHub3 = class {
22570
22781
  }
22571
22782
  };
22572
22783
 
22784
+ // app/lib/telegram/gateway/telegram-gateway.ts
22785
+ var import_dist7 = __toESM(require_dist(), 1);
22786
+
22573
22787
  // app/lib/telegram/gateway/routes.ts
22574
22788
  function createTelegramChannelRoutes(deps) {
22575
22789
  const app57 = new Hono();
@@ -22700,8 +22914,16 @@ var TelegramGateway = class {
22700
22914
  const willDeliverNow = this.hub.isReady(input.senderId);
22701
22915
  const source = input.source ?? "user";
22702
22916
  this.replies.set(input.senderId, input.reply);
22917
+ let standingRules;
22918
+ if (input.role === "admin" && this.deps.fetchStandingRules) {
22919
+ standingRules = await this.deps.fetchStandingRules(input.accountId) || void 0;
22920
+ const injected = standingRules ? (standingRules.match(/^- /gm) ?? []).length : 0;
22921
+ console.error(
22922
+ `[preference-inject] op=inject channel=tg accountId=${input.accountId} injected=${injected} capped=${injected >= import_dist7.INJECTION_RULE_CAP}`
22923
+ );
22924
+ }
22703
22925
  this.hub.deliver(
22704
- { key: input.senderId, text: input.text, messageId: `tg-${++this.seq}`, source },
22926
+ { key: input.senderId, text: input.text, messageId: `tg-${++this.seq}`, source, standingRules },
22705
22927
  Date.now()
22706
22928
  );
22707
22929
  console.error(
@@ -23057,7 +23279,8 @@ async function firePublicSessionEndReview(input) {
23057
23279
  // app/lib/whatsapp/inbound/channel-admin-binding-drift.ts
23058
23280
  function findAccountManagerDrift(accountManagers, adminPhones, validAccountIds) {
23059
23281
  const drift = [];
23060
- for (const [phone, managesAccount] of Object.entries(accountManagers)) {
23282
+ for (const [phone, entry] of Object.entries(accountManagers)) {
23283
+ const managesAccount = typeof entry === "string" ? entry : entry.managesAccount;
23061
23284
  if (!validAccountIds.includes(managesAccount)) {
23062
23285
  drift.push({ phone, managesAccount, reason: "not-in-registry" });
23063
23286
  } else if (adminPhones.some((a) => phonesMatch(a, phone))) {
@@ -23466,7 +23689,42 @@ function isPublicHost(host) {
23466
23689
  }
23467
23690
  var app56 = new Hono();
23468
23691
  var nativeFileFollowers = /* @__PURE__ */ new Map();
23692
+ async function fetchAccountStandingRules(accountId) {
23693
+ const session = getSession();
23694
+ try {
23695
+ const rules = await (0, import_dist8.activeRuleSet)(session, accountId);
23696
+ return (0, import_dist8.formatStandingRulesBlock)(rules);
23697
+ } catch (err) {
23698
+ console.error(
23699
+ `[preference-inject] op=fetch-failed accountId=${accountId} error=${err instanceof Error ? err.message : String(err)}`
23700
+ );
23701
+ return "";
23702
+ } finally {
23703
+ await session.close();
23704
+ }
23705
+ }
23706
+ async function recordPassiveDispatch(input) {
23707
+ const session = getSession();
23708
+ try {
23709
+ await session.run(
23710
+ `CREATE (d:PassiveDispatch {
23711
+ accountId: $accountId,
23712
+ sessionId: $sessionId,
23713
+ msgId: $msgId,
23714
+ dispatchedAt: $dispatchedAt
23715
+ })`,
23716
+ { ...input, dispatchedAt: (/* @__PURE__ */ new Date()).toISOString() }
23717
+ );
23718
+ } catch (err) {
23719
+ console.error(
23720
+ `[wa-passive] op=ledger-write-failed account=${input.accountId} sessionId=${input.sessionId} error=${err instanceof Error ? err.message : String(err)}`
23721
+ );
23722
+ } finally {
23723
+ await session.close();
23724
+ }
23725
+ }
23469
23726
  var waGateway = new WaGateway({
23727
+ fetchStandingRules: fetchAccountStandingRules,
23470
23728
  gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
23471
23729
  serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve33(process.env.MAXY_PLATFORM_ROOT ?? join35(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
23472
23730
  // Task 751 / 1390 — file delivery on the native channel. `maxyAccountId` (the
@@ -23487,7 +23745,50 @@ var waGateway = new WaGateway({
23487
23745
  });
23488
23746
  return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
23489
23747
  },
23490
- ensureChannelSession: async ({ accountId, effectiveAccountId, senderId, role, personId, gatewayUrl, serverPath }) => {
23748
+ ensureChannelSession: async ({ accountId, effectiveAccountId, senderId, role, personId, gatewayUrl, serverPath, passive, text, msgId }) => {
23749
+ if (passive) {
23750
+ const phoneHash = createHash6("sha256").update(senderId).digest("hex").slice(0, 12);
23751
+ const prompt = (text ?? "").trim();
23752
+ if (prompt.length === 0) {
23753
+ console.error(`[wa-passive] op=dropped account=${effectiveAccountId} phoneHash=${phoneHash} msgId=${msgId ?? "none"} reason=empty-text`);
23754
+ return;
23755
+ }
23756
+ let raisedByPersonId;
23757
+ try {
23758
+ raisedByPersonId = await ensureManagerPerson(effectiveAccountId, senderId) ?? void 0;
23759
+ } catch (err) {
23760
+ console.error(`[wa-passive] op=raised-by-error account=${effectiveAccountId} phoneHash=${phoneHash} msgId=${msgId ?? "none"} error=${err instanceof Error ? err.message : String(err)}`);
23761
+ }
23762
+ if (!raisedByPersonId) {
23763
+ console.error(`[wa-passive] op=dropped account=${effectiveAccountId} phoneHash=${phoneHash} msgId=${msgId ?? "none"} reason=raised-by-unresolved`);
23764
+ return;
23765
+ }
23766
+ console.error(`[wa-passive] op=dispatch account=${effectiveAccountId} phoneHash=${phoneHash} msgId=${msgId ?? "none"} raisedBy=${raisedByPersonId}`);
23767
+ const result2 = await managerSpawn({
23768
+ senderId,
23769
+ role: "public",
23770
+ channel: "whatsapp",
23771
+ accountId: effectiveAccountId,
23772
+ targetAccountId: effectiveAccountId,
23773
+ agentSlug: "passive",
23774
+ initialMessage: prompt,
23775
+ passive: true,
23776
+ // Task 1493 — the manager's elementId rides the personId field
23777
+ // end-to-end (managerSpawn → /public-spawn → spawn args → work env as
23778
+ // RAISED_BY_PERSON_ID). It is also this passive session's identity.
23779
+ personId: raisedByPersonId
23780
+ });
23781
+ if ("error" in result2) {
23782
+ console.error(`[wa-passive] op=dispatch-failed account=${effectiveAccountId} msgId=${msgId ?? "none"} error=${result2.error}`);
23783
+ return;
23784
+ }
23785
+ await recordPassiveDispatch({
23786
+ accountId: effectiveAccountId,
23787
+ sessionId: result2.sessionId,
23788
+ msgId: msgId ?? ""
23789
+ });
23790
+ return;
23791
+ }
23491
23792
  if (role === "admin") {
23492
23793
  const managesAccount = effectiveAccountId !== accountId ? effectiveAccountId : "none";
23493
23794
  console.error(`[whatsapp-native] op=account-manager-route senderId=${senderId} managesAccount=${managesAccount} effectiveAccount=${effectiveAccountId} source=gate`);
@@ -23524,6 +23825,7 @@ var waGateway = new WaGateway({
23524
23825
  app56.route("/", waGateway.routes());
23525
23826
  waGateway.startSweeper();
23526
23827
  var webchatGateway = new WebchatGateway({
23828
+ fetchStandingRules: fetchAccountStandingRules,
23527
23829
  gatewayUrl: webchatGatewayUrl(),
23528
23830
  serverPath: webchatServerPath(),
23529
23831
  ensureChannelSession: async ({ accountId, key, role, adminUserId, gatewayUrl, serverPath, sessionId, public: pub }) => {
@@ -23609,6 +23911,7 @@ webchatGateway.startSweeper();
23609
23911
  webchatGateway.startPublicReaper();
23610
23912
  var telegramFileFollowers = /* @__PURE__ */ new Map();
23611
23913
  var telegramGateway = new TelegramGateway({
23914
+ fetchStandingRules: fetchAccountStandingRules,
23612
23915
  gatewayUrl: telegramGatewayUrl(),
23613
23916
  serverPath: telegramServerPath(),
23614
23917
  ensureChannelSession: async ({ accountId, senderId, role, personId, chatId, gatewayUrl, serverPath }) => {
@@ -23663,10 +23966,11 @@ var scheduleInjectRoutes = createScheduleInjectRoutes({
23663
23966
  if (!sent.ok) throw new Error(sent.error ?? "telegram send failed");
23664
23967
  },
23665
23968
  effectiveAccountFor: (accountId, accountDir, destination) => {
23666
- const managedSub = managedAccountFor(readAccountManagers(accountDir), destination);
23667
- if (managedSub === null) return accountId;
23668
- if (!isValidAccountId(managedSub)) return null;
23669
- return managedSub;
23969
+ const managed = managedAccountFor(readAccountManagers(accountDir), destination);
23970
+ if (managed === null) return accountId;
23971
+ if (managed.mode === "passive") return null;
23972
+ if (!isValidAccountId(managed.account)) return null;
23973
+ return managed.account;
23670
23974
  }
23671
23975
  });
23672
23976
  app56.route("/api/channel/schedule-inject", scheduleInjectRoutes);
@@ -24716,7 +25020,7 @@ init({
24716
25020
  console.error(`[whatsapp:route] skipped reason=owner-mirror senderId=${msg.senderPhone}`);
24717
25021
  return;
24718
25022
  }
24719
- const decision = decideWaRoute(msg);
25023
+ const decision = decideWaRoute({ agentType: msg.agentType, personId: msg.personId, passive: msg.passive });
24720
25024
  if (decision.action === "drop") {
24721
25025
  console.error(`[whatsapp:route] op=dropped reason=${decision.reason} senderId=${msg.senderPhone}`);
24722
25026
  return;
@@ -24734,7 +25038,7 @@ init({
24734
25038
  console.error(`[whatsapp:route] op=routed agentType=public personId=${decision.personId} senderId=${msg.senderPhone}`);
24735
25039
  }
24736
25040
  try {
24737
- void msg.composing().catch(() => {
25041
+ if (!decision.passive) void msg.composing().catch(() => {
24738
25042
  });
24739
25043
  await waGateway.handleInbound({
24740
25044
  accountId: msg.accountId,
@@ -24742,6 +25046,9 @@ init({
24742
25046
  senderId: msg.senderPhone,
24743
25047
  role: decision.role,
24744
25048
  personId: decision.personId,
25049
+ // Task 1483 — a passive account-manager inbound routes to a fresh
25050
+ // constrained one-shot spawn instead of the shared rc-daemon path.
25051
+ passive: decision.passive,
24745
25052
  text: msg.text,
24746
25053
  reply: msg.reply,
24747
25054
  media: msg.media