@rubytech/create-maxy-code 0.1.417 → 0.1.419

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 (128) 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.json +4 -3
  14. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +14 -2
  15. package/payload/platform/plugins/docs/references/internals.md +10 -0
  16. package/payload/platform/plugins/docs/references/memory-guide.md +2 -0
  17. package/payload/platform/plugins/docs/references/outlook-guide.md +1 -1
  18. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-read-active-rules.test.d.ts +2 -0
  19. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-read-active-rules.test.d.ts.map +1 -0
  20. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-read-active-rules.test.js +49 -0
  21. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-read-active-rules.test.js.map +1 -0
  22. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-reconcile.test.d.ts +2 -0
  23. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-reconcile.test.d.ts.map +1 -0
  24. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-reconcile.test.js +99 -0
  25. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/profile-update-reconcile.test.js.map +1 -0
  26. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.d.ts +3 -0
  27. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.d.ts.map +1 -1
  28. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js +11 -3
  29. package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js.map +1 -1
  30. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts +21 -0
  31. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts.map +1 -1
  32. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js +106 -14
  33. package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js.map +1 -1
  34. package/payload/platform/plugins/outlook/mcp/dist/__tests__/calendar-write.test.js +24 -0
  35. package/payload/platform/plugins/outlook/mcp/dist/__tests__/calendar-write.test.js.map +1 -1
  36. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-create.d.ts +15 -2
  37. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-create.d.ts.map +1 -1
  38. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-create.js +16 -7
  39. package/payload/platform/plugins/outlook/mcp/dist/tools/calendar-create.js.map +1 -1
  40. package/payload/platform/plugins/whatsapp/mcp/dist/index.js +10 -5
  41. package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
  42. package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +9 -1
  43. package/payload/platform/scripts/__tests__/provision-role-stamp.test.sh +3 -0
  44. package/payload/platform/scripts/lib/provision-account-dir.sh +9 -1
  45. package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.d.ts +1 -1
  46. package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.d.ts.map +1 -1
  47. package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.js +5 -1
  48. package/payload/platform/services/claude-session-manager/dist/agent-identity-locator.js.map +1 -1
  49. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts +1 -0
  50. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  51. package/payload/platform/services/claude-session-manager/dist/http-server.js +29 -2
  52. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  53. package/payload/platform/services/claude-session-manager/dist/index.js +14 -0
  54. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  55. package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.d.ts +46 -0
  56. package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.d.ts.map +1 -0
  57. package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.js +96 -0
  58. package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.js.map +1 -0
  59. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +33 -0
  60. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  61. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +54 -5
  62. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  63. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.d.ts.map +1 -1
  64. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js +5 -1
  65. package/payload/platform/services/claude-session-manager/dist/public-agent-reachability.js.map +1 -1
  66. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts +6 -0
  67. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -1
  68. package/payload/platform/services/claude-session-manager/dist/system-prompt.js +20 -0
  69. package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
  70. package/payload/platform/services/telegram-channel/dist/instructions.d.ts +9 -0
  71. package/payload/platform/services/telegram-channel/dist/instructions.d.ts.map +1 -0
  72. package/payload/platform/services/telegram-channel/dist/instructions.js +100 -0
  73. package/payload/platform/services/telegram-channel/dist/instructions.js.map +1 -0
  74. package/payload/platform/services/telegram-channel/dist/notification.d.ts +93 -0
  75. package/payload/platform/services/telegram-channel/dist/notification.d.ts.map +1 -0
  76. package/payload/platform/services/telegram-channel/dist/notification.js +119 -0
  77. package/payload/platform/services/telegram-channel/dist/notification.js.map +1 -0
  78. package/payload/platform/services/telegram-channel/dist/server.d.ts +40 -0
  79. package/payload/platform/services/telegram-channel/dist/server.d.ts.map +1 -0
  80. package/payload/platform/services/telegram-channel/dist/server.js +291 -0
  81. package/payload/platform/services/telegram-channel/dist/server.js.map +1 -0
  82. package/payload/platform/services/telegram-channel/dist/turn-follow.d.ts +28 -0
  83. package/payload/platform/services/telegram-channel/dist/turn-follow.d.ts.map +1 -0
  84. package/payload/platform/services/telegram-channel/dist/turn-follow.js +188 -0
  85. package/payload/platform/services/telegram-channel/dist/turn-follow.js.map +1 -0
  86. package/payload/platform/services/webchat-channel/dist/notification.d.ts +5 -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 +6 -1
  89. package/payload/platform/services/webchat-channel/dist/notification.js.map +1 -1
  90. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +6 -0
  91. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
  92. package/payload/platform/services/whatsapp-channel/dist/notification.js +8 -1
  93. package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
  94. package/payload/platform/templates/agents/passive/IDENTITY.md +21 -0
  95. package/payload/server/public/assets/{AdminLoginScreens-CBTx7ZPK.js → AdminLoginScreens-B1PkOF66.js} +1 -1
  96. package/payload/server/public/assets/AdminShell-D8wNArGQ.js +2 -0
  97. package/payload/server/public/assets/{Checkbox-BHTxekRB.js → Checkbox-Bm9Er726.js} +1 -1
  98. package/payload/server/public/assets/{Transcript-5kLbBInN.js → Transcript-CBIyQ8T5.js} +1 -1
  99. package/payload/server/public/assets/{admin-BWCLE32N.js → admin-CWM9G_yA.js} +1 -1
  100. package/payload/server/public/assets/{browser-CRvCPsvV.js → browser-D8QhNffV.js} +1 -1
  101. package/payload/server/public/assets/{calendar-BI-kd-QP.js → calendar-qob5K4t_.js} +1 -1
  102. package/payload/server/public/assets/chat-BNrxVUF6.js +1 -0
  103. package/payload/server/public/assets/chevron-left-BQThFVrd.js +1 -0
  104. package/payload/server/public/assets/data-C-EqNbRi.js +1 -0
  105. package/payload/server/public/assets/{graph-BFSyW_5i.js → graph-DZ2HMpIB.js} +1 -1
  106. package/payload/server/public/assets/{graph-labels-DEjD_qJh.js → graph-labels-BFG4bjXQ.js} +1 -1
  107. package/payload/server/public/assets/{maximize-2-BcMJNeCB.js → maximize-2-DkZ40lSl.js} +1 -1
  108. package/payload/server/public/assets/{operator-Dtvm9ero.js → operator-FrXrSsBj.js} +1 -1
  109. package/payload/server/public/assets/page-CHWMI9Tm.js +32 -0
  110. package/payload/server/public/assets/{page-TXi9dmaB.js → page-CXKV01gT.js} +1 -1
  111. package/payload/server/public/assets/{public-DBx3U0r8.js → public-2p1pJ4WF.js} +1 -1
  112. package/payload/server/public/assets/{rotate-ccw-8ti3QkeY.js → rotate-ccw-D3o_bMBA.js} +1 -1
  113. package/payload/server/public/assets/useSubAccountSwitcher-8dLvF3Ux.js +9 -0
  114. package/payload/server/public/browser.html +3 -3
  115. package/payload/server/public/calendar.html +4 -4
  116. package/payload/server/public/chat.html +8 -8
  117. package/payload/server/public/data.html +7 -7
  118. package/payload/server/public/graph.html +8 -8
  119. package/payload/server/public/index.html +10 -10
  120. package/payload/server/public/operator.html +10 -10
  121. package/payload/server/public/public.html +8 -8
  122. package/payload/server/server.js +237 -38
  123. package/payload/server/public/assets/AdminShell-DPHUeXea.js +0 -2
  124. package/payload/server/public/assets/chat-DFy8hb1y.js +0 -1
  125. package/payload/server/public/assets/chevron-left-Bggm5B5U.js +0 -1
  126. package/payload/server/public/assets/data-HvZ69Ya1.js +0 -1
  127. package/payload/server/public/assets/page-C_pm3hl3.js +0 -32
  128. 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":
@@ -4867,6 +4955,7 @@ async function handleInboundMessage(conn, msg) {
4867
4955
  const payload = {
4868
4956
  accountId: conn.accountId,
4869
4957
  agentType: accessResult.agentType,
4958
+ passive: accessResult.passive === true,
4870
4959
  personId: accessResult.personId ?? null,
4871
4960
  senderPhone,
4872
4961
  text: extracted.text,
@@ -6192,6 +6281,9 @@ function createScheduleInjectRoutes(deps) {
6192
6281
  return app57;
6193
6282
  }
6194
6283
 
6284
+ // server/index.ts
6285
+ var import_dist8 = __toESM(require_dist(), 1);
6286
+
6195
6287
  // app/lib/telegram/outbound/send-text.ts
6196
6288
  async function sendTelegramText(botToken, chatId, text) {
6197
6289
  try {
@@ -6414,7 +6506,7 @@ function purgeNonSocketAdminPhonesAtBoot() {
6414
6506
  purgeNonSocketAdminPhones(acct.accountDir, socketOwner);
6415
6507
  }
6416
6508
  }
6417
- function setAccountManager(accountDir, phone, subAccountId) {
6509
+ function setAccountManager(accountDir, phone, subAccountId, mode = "active") {
6418
6510
  const normalized = phone.trim();
6419
6511
  if (!E164_PATTERN.test(normalized)) {
6420
6512
  return { ok: false, error: `Invalid phone format "${normalized}". Expected E.164 (e.g. +441234567890).` };
@@ -6448,7 +6540,7 @@ function setAccountManager(accountDir, phone, subAccountId) {
6448
6540
  if (!wa.accountManagers || typeof wa.accountManagers !== "object") {
6449
6541
  wa.accountManagers = {};
6450
6542
  }
6451
- wa.accountManagers[normalized] = sub;
6543
+ wa.accountManagers[normalized] = mode === "passive" ? { managesAccount: sub, mode: "passive" } : sub;
6452
6544
  const parsed = WhatsAppConfigSchema.safeParse(wa);
6453
6545
  if (!parsed.success) {
6454
6546
  const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
@@ -6456,9 +6548,10 @@ function setAccountManager(accountDir, phone, subAccountId) {
6456
6548
  }
6457
6549
  config.whatsapp = parsed.data;
6458
6550
  writeConfig(accountDir, config);
6459
- console.error(`${TAG17} bound account manager phone=${normalized} managesAccount=${sub}`);
6551
+ console.error(`${TAG17} bound account manager phone=${normalized} managesAccount=${sub} mode=${mode}`);
6460
6552
  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.` };
6553
+ 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.`;
6554
+ return { ok: true, message: `Bound ${normalized} as ${mode} account manager for sub-account ${sub}. ${behaviour}` };
6462
6555
  } catch (err) {
6463
6556
  const msg = err instanceof Error ? err.message : String(err);
6464
6557
  console.error(`${TAG17} setAccountManager failed: ${msg}`);
@@ -6503,6 +6596,12 @@ function readAccountManagers(accountDir) {
6503
6596
  const out = {};
6504
6597
  for (const [k, v] of Object.entries(wa.accountManagers)) {
6505
6598
  if (typeof v === "string") out[k] = v;
6599
+ else if (v && typeof v === "object" && typeof v.managesAccount === "string") {
6600
+ const obj = v;
6601
+ const rawMode = obj.mode;
6602
+ const mode = rawMode === void 0 ? void 0 : rawMode === "active" ? "active" : rawMode === "passive" ? "passive" : "passive";
6603
+ out[k] = { managesAccount: obj.managesAccount, mode };
6604
+ }
6506
6605
  }
6507
6606
  return out;
6508
6607
  } catch {
@@ -7272,7 +7371,7 @@ app2.post("/reconnect", async (c) => {
7272
7371
  app2.post("/config", async (c) => {
7273
7372
  try {
7274
7373
  const body = await c.req.json().catch(() => ({}));
7275
- const { action, phone, slug, fields, accountId, managesAccount } = body;
7374
+ const { action, phone, slug, fields, accountId, managesAccount, mode } = body;
7276
7375
  if (!action || typeof action !== "string") {
7277
7376
  return c.json({ ok: false, error: 'Missing required field "action".' }, 400);
7278
7377
  }
@@ -7310,8 +7409,9 @@ app2.post("/config", async (c) => {
7310
7409
  if (!managesAccount || typeof managesAccount !== "string") {
7311
7410
  return c.json({ ok: false, error: 'Missing required field "managesAccount" (the sub-account UUID this phone should manage).' }, 400);
7312
7411
  }
7313
- const result = setAccountManager(account.accountDir, phone, managesAccount);
7314
- console.error(`${TAG21} config action=add-account-manager phone=${phone} managesAccount=${managesAccount} ok=${result.ok}`);
7412
+ const managerMode = mode === "passive" ? "passive" : "active";
7413
+ const result = setAccountManager(account.accountDir, phone, managesAccount, managerMode);
7414
+ console.error(`${TAG21} config action=add-account-manager phone=${phone} managesAccount=${managesAccount} mode=${managerMode} ok=${result.ok}`);
7315
7415
  return c.json(result, result.ok ? 200 : 400);
7316
7416
  }
7317
7417
  case "remove-account-manager": {
@@ -15000,7 +15100,7 @@ app21.post("/rename", requireAdminSession, async (c) => {
15000
15100
  var files_default = app21;
15001
15101
 
15002
15102
  // ../lib/graph-search/src/index.ts
15003
- var import_dist = __toESM(require_dist());
15103
+ var import_dist = __toESM(require_dist2());
15004
15104
  import { int } from "neo4j-driver";
15005
15105
 
15006
15106
  // ../lib/graph-search/src/rrf-fusion.ts
@@ -15898,8 +15998,8 @@ var graph_search_default = app22;
15898
15998
  import neo4j2 from "neo4j-driver";
15899
15999
 
15900
16000
  // app/lib/graph-labels.ts
15901
- var import_dist2 = __toESM(require_dist2(), 1);
15902
- var import_dist3 = __toESM(require_dist2(), 1);
16001
+ var import_dist2 = __toESM(require_dist3(), 1);
16002
+ var import_dist3 = __toESM(require_dist3(), 1);
15903
16003
  var HIDDEN_BY_DEFAULT_LABELS = Object.freeze(
15904
16004
  /* @__PURE__ */ new Set(["Chunk", "GraphPreference"])
15905
16005
  );
@@ -17950,7 +18050,10 @@ async function managerSpawn(opts) {
17950
18050
  visitorId: opts.visitorId,
17951
18051
  name: opts.name,
17952
18052
  webchatChannel: opts.webchatChannel,
17953
- previousContext: opts.previousContext
18053
+ previousContext: opts.previousContext,
18054
+ initialMessage: opts.initialMessage,
18055
+ passive: opts.passive,
18056
+ targetAccountId: opts.targetAccountId
17954
18057
  })
17955
18058
  }).catch((err) => ({ __throw: err instanceof Error ? err.message : String(err) }));
17956
18059
  if ("__throw" in res) {
@@ -20438,7 +20541,7 @@ app55.get("/free-busy", async (c) => {
20438
20541
  var calendar_public_default = app55;
20439
20542
 
20440
20543
  // app/lib/graph-health.ts
20441
- var import_dist4 = __toESM(require_dist3(), 1);
20544
+ var import_dist4 = __toESM(require_dist4(), 1);
20442
20545
  var HOUR_MS = 60 * 60 * 1e3;
20443
20546
  function renderLabelTop(rows) {
20444
20547
  return rows.map((b) => {
@@ -21050,6 +21153,9 @@ var InboundHub = class {
21050
21153
  }
21051
21154
  };
21052
21155
 
21156
+ // app/lib/whatsapp/gateway/wa-gateway.ts
21157
+ var import_dist5 = __toESM(require_dist(), 1);
21158
+
21053
21159
  // node_modules/hono/dist/utils/stream.js
21054
21160
  var StreamingApi = class {
21055
21161
  writer;
@@ -21380,6 +21486,23 @@ var WaGateway = class {
21380
21486
  }
21381
21487
  /** Handle one inbound admin WhatsApp message. */
21382
21488
  async handleInbound(input) {
21489
+ if (input.passive) {
21490
+ const msgId = `wa-${++this.seq}`;
21491
+ await this.deps.ensureChannelSession({
21492
+ accountId: input.accountId,
21493
+ effectiveAccountId: input.effectiveAccountId,
21494
+ senderId: input.senderId,
21495
+ // agentType stays 'admin'; the constraint rides the passive flag.
21496
+ role: "admin",
21497
+ personId: null,
21498
+ gatewayUrl: this.deps.gatewayUrl,
21499
+ serverPath: this.deps.serverPath,
21500
+ passive: true,
21501
+ text: input.text,
21502
+ msgId
21503
+ });
21504
+ return;
21505
+ }
21383
21506
  const bytes = Buffer.byteLength(input.text, "utf8");
21384
21507
  const hadSubscriber = this.hub.hasSubscriber(input.senderId);
21385
21508
  const willDeliverNow = this.hub.isReady(input.senderId);
@@ -21388,13 +21511,22 @@ var WaGateway = class {
21388
21511
  const source = input.source ?? "user";
21389
21512
  this.replies.set(input.senderId, input.reply);
21390
21513
  this.docContexts.set(input.senderId, { accountId: input.accountId, maxyAccountId: input.effectiveAccountId });
21514
+ let standingRules;
21515
+ if (this.deps.fetchStandingRules) {
21516
+ standingRules = await this.deps.fetchStandingRules(input.effectiveAccountId) || void 0;
21517
+ const injected = standingRules ? (standingRules.match(/^- /gm) ?? []).length : 0;
21518
+ console.error(
21519
+ `[preference-inject] op=inject channel=wa accountId=${input.effectiveAccountId} injected=${injected} capped=${injected >= import_dist5.INJECTION_RULE_CAP}`
21520
+ );
21521
+ }
21391
21522
  this.hub.deliver(
21392
21523
  {
21393
21524
  senderId: input.senderId,
21394
21525
  text: input.text,
21395
21526
  waMessageId: `wa-${++this.seq}`,
21396
21527
  media: input.media,
21397
- source
21528
+ source,
21529
+ standingRules
21398
21530
  },
21399
21531
  Date.now()
21400
21532
  );
@@ -21455,8 +21587,8 @@ function buildWaSpawnRequest(input) {
21455
21587
 
21456
21588
  // app/lib/whatsapp/inbound/route-decision.ts
21457
21589
  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 };
21590
+ if (msg.agentType === "admin") return { action: "route", role: "admin", personId: null, passive: msg.passive === true };
21591
+ if (msg.personId && msg.personId.length > 0) return { action: "route", role: "public", personId: msg.personId, passive: false };
21460
21592
  return { action: "drop", reason: "public-unresolved" };
21461
21593
  }
21462
21594
 
@@ -21617,6 +21749,9 @@ var InboundHub2 = class {
21617
21749
  }
21618
21750
  };
21619
21751
 
21752
+ // app/lib/webchat/gateway/webchat-gateway.ts
21753
+ var import_dist6 = __toESM(require_dist(), 1);
21754
+
21620
21755
  // app/lib/webchat/gateway/routes.ts
21621
21756
  function createWebchatChannelRoutes(deps) {
21622
21757
  const app57 = new Hono();
@@ -22051,8 +22186,16 @@ var WebchatGateway = class _WebchatGateway {
22051
22186
  const bytes = Buffer.byteLength(input.text, "utf8");
22052
22187
  const hadSubscriber = this.hub.hasSubscriber(input.key);
22053
22188
  const willDeliverNow = this.hub.isReady(input.key);
22189
+ let standingRules;
22190
+ if (input.role === "admin" && this.deps.fetchStandingRules) {
22191
+ standingRules = await this.deps.fetchStandingRules(input.accountId) || void 0;
22192
+ const injected = standingRules ? (standingRules.match(/^- /gm) ?? []).length : 0;
22193
+ console.error(
22194
+ `[preference-inject] op=inject channel=web accountId=${input.accountId} injected=${injected} capped=${injected >= import_dist6.INJECTION_RULE_CAP}`
22195
+ );
22196
+ }
22054
22197
  this.hub.deliver(
22055
- { key: input.key, text: input.text, messageId: `wc-${++this.seq}` },
22198
+ { key: input.key, text: input.text, messageId: `wc-${++this.seq}`, standingRules },
22056
22199
  Date.now()
22057
22200
  );
22058
22201
  if (willDeliverNow) this.lastDeliveredAt.set(input.key, Date.now());
@@ -22570,6 +22713,9 @@ var InboundHub3 = class {
22570
22713
  }
22571
22714
  };
22572
22715
 
22716
+ // app/lib/telegram/gateway/telegram-gateway.ts
22717
+ var import_dist7 = __toESM(require_dist(), 1);
22718
+
22573
22719
  // app/lib/telegram/gateway/routes.ts
22574
22720
  function createTelegramChannelRoutes(deps) {
22575
22721
  const app57 = new Hono();
@@ -22700,8 +22846,16 @@ var TelegramGateway = class {
22700
22846
  const willDeliverNow = this.hub.isReady(input.senderId);
22701
22847
  const source = input.source ?? "user";
22702
22848
  this.replies.set(input.senderId, input.reply);
22849
+ let standingRules;
22850
+ if (input.role === "admin" && this.deps.fetchStandingRules) {
22851
+ standingRules = await this.deps.fetchStandingRules(input.accountId) || void 0;
22852
+ const injected = standingRules ? (standingRules.match(/^- /gm) ?? []).length : 0;
22853
+ console.error(
22854
+ `[preference-inject] op=inject channel=tg accountId=${input.accountId} injected=${injected} capped=${injected >= import_dist7.INJECTION_RULE_CAP}`
22855
+ );
22856
+ }
22703
22857
  this.hub.deliver(
22704
- { key: input.senderId, text: input.text, messageId: `tg-${++this.seq}`, source },
22858
+ { key: input.senderId, text: input.text, messageId: `tg-${++this.seq}`, source, standingRules },
22705
22859
  Date.now()
22706
22860
  );
22707
22861
  console.error(
@@ -23057,7 +23211,8 @@ async function firePublicSessionEndReview(input) {
23057
23211
  // app/lib/whatsapp/inbound/channel-admin-binding-drift.ts
23058
23212
  function findAccountManagerDrift(accountManagers, adminPhones, validAccountIds) {
23059
23213
  const drift = [];
23060
- for (const [phone, managesAccount] of Object.entries(accountManagers)) {
23214
+ for (const [phone, entry] of Object.entries(accountManagers)) {
23215
+ const managesAccount = typeof entry === "string" ? entry : entry.managesAccount;
23061
23216
  if (!validAccountIds.includes(managesAccount)) {
23062
23217
  drift.push({ phone, managesAccount, reason: "not-in-registry" });
23063
23218
  } else if (adminPhones.some((a) => phonesMatch(a, phone))) {
@@ -23466,7 +23621,22 @@ function isPublicHost(host) {
23466
23621
  }
23467
23622
  var app56 = new Hono();
23468
23623
  var nativeFileFollowers = /* @__PURE__ */ new Map();
23624
+ async function fetchAccountStandingRules(accountId) {
23625
+ const session = getSession();
23626
+ try {
23627
+ const rules = await (0, import_dist8.activeRuleSet)(session, accountId);
23628
+ return (0, import_dist8.formatStandingRulesBlock)(rules);
23629
+ } catch (err) {
23630
+ console.error(
23631
+ `[preference-inject] op=fetch-failed accountId=${accountId} error=${err instanceof Error ? err.message : String(err)}`
23632
+ );
23633
+ return "";
23634
+ } finally {
23635
+ await session.close();
23636
+ }
23637
+ }
23469
23638
  var waGateway = new WaGateway({
23639
+ fetchStandingRules: fetchAccountStandingRules,
23470
23640
  gatewayUrl: `http://127.0.0.1:${process.env.MAXY_UI_INTERNAL_PORT ?? ""}`,
23471
23641
  serverPath: process.env.MAXY_WA_CHANNEL_SERVER_PATH ?? resolve33(process.env.MAXY_PLATFORM_ROOT ?? join35(__dirname, ".."), "services/whatsapp-channel/dist/server.js"),
23472
23642
  // Task 751 / 1390 — file delivery on the native channel. `maxyAccountId` (the
@@ -23487,7 +23657,30 @@ var waGateway = new WaGateway({
23487
23657
  });
23488
23658
  return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
23489
23659
  },
23490
- ensureChannelSession: async ({ accountId, effectiveAccountId, senderId, role, personId, gatewayUrl, serverPath }) => {
23660
+ ensureChannelSession: async ({ accountId, effectiveAccountId, senderId, role, personId, gatewayUrl, serverPath, passive, text, msgId }) => {
23661
+ if (passive) {
23662
+ const phoneHash = createHash6("sha256").update(senderId).digest("hex").slice(0, 12);
23663
+ const prompt = (text ?? "").trim();
23664
+ if (prompt.length === 0) {
23665
+ console.error(`[wa-passive] op=dropped account=${effectiveAccountId} phoneHash=${phoneHash} msgId=${msgId ?? "none"} reason=empty-text`);
23666
+ return;
23667
+ }
23668
+ console.error(`[wa-passive] op=dispatch account=${effectiveAccountId} phoneHash=${phoneHash} msgId=${msgId ?? "none"}`);
23669
+ const result2 = await managerSpawn({
23670
+ senderId,
23671
+ role: "public",
23672
+ channel: "whatsapp",
23673
+ accountId: effectiveAccountId,
23674
+ targetAccountId: effectiveAccountId,
23675
+ agentSlug: "passive",
23676
+ initialMessage: prompt,
23677
+ passive: true
23678
+ });
23679
+ if ("error" in result2) {
23680
+ console.error(`[wa-passive] op=dispatch-failed account=${effectiveAccountId} msgId=${msgId ?? "none"} error=${result2.error}`);
23681
+ }
23682
+ return;
23683
+ }
23491
23684
  if (role === "admin") {
23492
23685
  const managesAccount = effectiveAccountId !== accountId ? effectiveAccountId : "none";
23493
23686
  console.error(`[whatsapp-native] op=account-manager-route senderId=${senderId} managesAccount=${managesAccount} effectiveAccount=${effectiveAccountId} source=gate`);
@@ -23524,6 +23717,7 @@ var waGateway = new WaGateway({
23524
23717
  app56.route("/", waGateway.routes());
23525
23718
  waGateway.startSweeper();
23526
23719
  var webchatGateway = new WebchatGateway({
23720
+ fetchStandingRules: fetchAccountStandingRules,
23527
23721
  gatewayUrl: webchatGatewayUrl(),
23528
23722
  serverPath: webchatServerPath(),
23529
23723
  ensureChannelSession: async ({ accountId, key, role, adminUserId, gatewayUrl, serverPath, sessionId, public: pub }) => {
@@ -23609,6 +23803,7 @@ webchatGateway.startSweeper();
23609
23803
  webchatGateway.startPublicReaper();
23610
23804
  var telegramFileFollowers = /* @__PURE__ */ new Map();
23611
23805
  var telegramGateway = new TelegramGateway({
23806
+ fetchStandingRules: fetchAccountStandingRules,
23612
23807
  gatewayUrl: telegramGatewayUrl(),
23613
23808
  serverPath: telegramServerPath(),
23614
23809
  ensureChannelSession: async ({ accountId, senderId, role, personId, chatId, gatewayUrl, serverPath }) => {
@@ -23663,10 +23858,11 @@ var scheduleInjectRoutes = createScheduleInjectRoutes({
23663
23858
  if (!sent.ok) throw new Error(sent.error ?? "telegram send failed");
23664
23859
  },
23665
23860
  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;
23861
+ const managed = managedAccountFor(readAccountManagers(accountDir), destination);
23862
+ if (managed === null) return accountId;
23863
+ if (managed.mode === "passive") return null;
23864
+ if (!isValidAccountId(managed.account)) return null;
23865
+ return managed.account;
23670
23866
  }
23671
23867
  });
23672
23868
  app56.route("/api/channel/schedule-inject", scheduleInjectRoutes);
@@ -24716,7 +24912,7 @@ init({
24716
24912
  console.error(`[whatsapp:route] skipped reason=owner-mirror senderId=${msg.senderPhone}`);
24717
24913
  return;
24718
24914
  }
24719
- const decision = decideWaRoute(msg);
24915
+ const decision = decideWaRoute({ agentType: msg.agentType, personId: msg.personId, passive: msg.passive });
24720
24916
  if (decision.action === "drop") {
24721
24917
  console.error(`[whatsapp:route] op=dropped reason=${decision.reason} senderId=${msg.senderPhone}`);
24722
24918
  return;
@@ -24734,7 +24930,7 @@ init({
24734
24930
  console.error(`[whatsapp:route] op=routed agentType=public personId=${decision.personId} senderId=${msg.senderPhone}`);
24735
24931
  }
24736
24932
  try {
24737
- void msg.composing().catch(() => {
24933
+ if (!decision.passive) void msg.composing().catch(() => {
24738
24934
  });
24739
24935
  await waGateway.handleInbound({
24740
24936
  accountId: msg.accountId,
@@ -24742,6 +24938,9 @@ init({
24742
24938
  senderId: msg.senderPhone,
24743
24939
  role: decision.role,
24744
24940
  personId: decision.personId,
24941
+ // Task 1483 — a passive account-manager inbound routes to a fresh
24942
+ // constrained one-shot spawn instead of the shared rc-daemon path.
24943
+ passive: decision.passive,
24745
24944
  text: msg.text,
24746
24945
  reply: msg.reply,
24747
24946
  media: msg.media