@rubytech/create-sitedesk-code 0.1.530 → 0.1.532

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 (99) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/graph-write/dist/index.d.ts.map +1 -1
  3. package/payload/platform/lib/graph-write/dist/index.js +4 -0
  4. package/payload/platform/lib/graph-write/dist/index.js.map +1 -1
  5. package/payload/platform/lib/graph-write/src/index.ts +4 -0
  6. package/payload/platform/lib/learning-constants/dist/index.d.ts +35 -0
  7. package/payload/platform/lib/learning-constants/dist/index.d.ts.map +1 -0
  8. package/payload/platform/lib/learning-constants/dist/index.js +50 -0
  9. package/payload/platform/lib/learning-constants/dist/index.js.map +1 -0
  10. package/payload/platform/lib/learning-constants/package.json +7 -0
  11. package/payload/platform/lib/learning-constants/src/index.test.ts +47 -0
  12. package/payload/platform/lib/learning-constants/src/index.ts +57 -0
  13. package/payload/platform/lib/learning-constants/tsconfig.json +9 -0
  14. package/payload/platform/neo4j/schema.cypher +24 -0
  15. package/payload/platform/package.json +1 -1
  16. package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
  17. package/payload/platform/plugins/admin/PLUGIN.md +1 -1
  18. package/payload/platform/plugins/admin/mcp/dist/index.js +2 -2
  19. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  20. package/payload/platform/plugins/admin/skills/insight/SKILL.md +1 -1
  21. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +58 -10
  22. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +12 -0
  23. package/payload/platform/plugins/docs/references/linkedin-extension.md +18 -9
  24. package/payload/platform/plugins/docs/references/session-retrospective.md +39 -0
  25. package/payload/platform/plugins/linkedin-extension/.claude-plugin/plugin.json +1 -1
  26. package/payload/platform/plugins/linkedin-extension/PLUGIN.md +7 -10
  27. package/payload/platform/plugins/linkedin-extension/extension/README.md +20 -17
  28. package/payload/platform/plugins/linkedin-extension/extension/background/sw.js +96 -13
  29. package/payload/platform/plugins/linkedin-extension/extension/manifest.json +3 -18
  30. package/payload/platform/plugins/linkedin-extension/extension/options/options.html +2 -2
  31. package/payload/platform/plugins/linkedin-extension/extension/options/options.js +1 -1
  32. package/payload/platform/plugins/memory/.claude-plugin/plugin.json +1 -1
  33. package/payload/platform/plugins/memory/PLUGIN.md +15 -1
  34. package/payload/platform/plugins/memory/mcp/dist/index.js +169 -0
  35. package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
  36. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/session-jsonl.test.d.ts +2 -0
  37. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/session-jsonl.test.d.ts.map +1 -0
  38. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/session-jsonl.test.js +95 -0
  39. package/payload/platform/plugins/memory/mcp/dist/lib/__tests__/session-jsonl.test.js.map +1 -0
  40. package/payload/platform/plugins/memory/mcp/dist/lib/session-jsonl.d.ts +60 -0
  41. package/payload/platform/plugins/memory/mcp/dist/lib/session-jsonl.d.ts.map +1 -0
  42. package/payload/platform/plugins/memory/mcp/dist/lib/session-jsonl.js +153 -0
  43. package/payload/platform/plugins/memory/mcp/dist/lib/session-jsonl.js.map +1 -0
  44. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/cross-account.test.js +6 -4
  45. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/cross-account.test.js.map +1 -1
  46. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-derive.test.d.ts +2 -0
  47. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-derive.test.d.ts.map +1 -0
  48. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-derive.test.js +160 -0
  49. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-derive.test.js.map +1 -0
  50. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-recall.test.d.ts +2 -0
  51. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-recall.test.d.ts.map +1 -0
  52. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-recall.test.js +64 -0
  53. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-recall.test.js.map +1 -0
  54. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-scan.test.d.ts +2 -0
  55. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-scan.test.d.ts.map +1 -0
  56. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-scan.test.js +78 -0
  57. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/learnings-scan.test.js.map +1 -0
  58. package/payload/platform/plugins/memory/mcp/dist/tools/learnings-derive.d.ts +41 -0
  59. package/payload/platform/plugins/memory/mcp/dist/tools/learnings-derive.d.ts.map +1 -0
  60. package/payload/platform/plugins/memory/mcp/dist/tools/learnings-derive.js +225 -0
  61. package/payload/platform/plugins/memory/mcp/dist/tools/learnings-derive.js.map +1 -0
  62. package/payload/platform/plugins/memory/mcp/dist/tools/learnings-recall.d.ts +31 -0
  63. package/payload/platform/plugins/memory/mcp/dist/tools/learnings-recall.d.ts.map +1 -0
  64. package/payload/platform/plugins/memory/mcp/dist/tools/learnings-recall.js +67 -0
  65. package/payload/platform/plugins/memory/mcp/dist/tools/learnings-recall.js.map +1 -0
  66. package/payload/platform/plugins/memory/mcp/dist/tools/learnings-scan.d.ts +40 -0
  67. package/payload/platform/plugins/memory/mcp/dist/tools/learnings-scan.d.ts.map +1 -0
  68. package/payload/platform/plugins/memory/mcp/dist/tools/learnings-scan.js +55 -0
  69. package/payload/platform/plugins/memory/mcp/dist/tools/learnings-scan.js.map +1 -0
  70. package/payload/platform/plugins/memory/references/schema-base.md +3 -0
  71. package/payload/platform/plugins/memory/skills/learnings-recall/SKILL.md +27 -0
  72. package/payload/platform/plugins/scheduling/PLUGIN.md +1 -1
  73. package/payload/platform/plugins/whatsapp/PLUGIN.md +1 -1
  74. package/payload/platform/plugins/whatsapp/mcp/dist/index.js +5 -5
  75. package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
  76. package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +6 -6
  77. package/payload/platform/plugins/whatsapp/skills/connect-whatsapp/SKILL.md +6 -2
  78. package/payload/platform/plugins/whatsapp/skills/manage-whatsapp-config/SKILL.md +6 -2
  79. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  80. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +3 -0
  81. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  82. package/payload/platform/services/claude-session-manager/dist/learning-index.d.ts +28 -0
  83. package/payload/platform/services/claude-session-manager/dist/learning-index.d.ts.map +1 -0
  84. package/payload/platform/services/claude-session-manager/dist/learning-index.js +70 -0
  85. package/payload/platform/services/claude-session-manager/dist/learning-index.js.map +1 -0
  86. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  87. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +18 -0
  88. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  89. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts +6 -0
  90. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -1
  91. package/payload/platform/services/claude-session-manager/dist/system-prompt.js +17 -0
  92. package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
  93. package/payload/platform/templates/specialists/agents/librarian.md +1 -1
  94. package/payload/server/{chunk-2YMGNYA3.js → chunk-UH6GCQO4.js} +78 -38
  95. package/payload/server/{manager-XPRN5UF5.js → manager-H34562SN.js} +5 -1
  96. package/payload/server/server.js +121 -38
  97. package/payload/platform/plugins/linkedin-extension/extension/assets/pill.css +0 -52
  98. package/payload/platform/plugins/linkedin-extension/extension/content/profile.js +0 -100
  99. package/payload/platform/plugins/linkedin-extension/extension/content/thread.js +0 -102
@@ -249,6 +249,13 @@ function listCredentialAccountIds(configDir2) {
249
249
  return [];
250
250
  }
251
251
  }
252
+ function listCredentialSelfPhones(credsRoot) {
253
+ try {
254
+ return fsSync.readdirSync(credsRoot, { withFileTypes: true }).filter((e) => e.isDirectory() && hasCredsSync(path.join(credsRoot, e.name))).map((e) => ({ accountId: e.name, selfPhone: readSelfId(path.join(credsRoot, e.name)).e164 }));
255
+ } catch {
256
+ return [];
257
+ }
258
+ }
252
259
 
253
260
  // app/lib/whatsapp/session.ts
254
261
  import { randomUUID } from "crypto";
@@ -1642,20 +1649,32 @@ function isAdminPhone(phone, adminPhones) {
1642
1649
  return adminPhones.some((entry2) => phonesMatch(entry2, normalized));
1643
1650
  }
1644
1651
  function checkDmAccess(params) {
1645
- const { senderPhone, selfPhone, accountId } = params;
1652
+ const { senderPhone, selfPhone, accountId, houseAccountId } = params;
1646
1653
  const cfg = resolveAccountConfig(params.config, params.accountConfig);
1647
1654
  if (phonesMatch(senderPhone, selfPhone)) {
1648
1655
  return { allowed: true, reason: "self-phone", agentType: "admin", effectiveAccountId: accountId };
1649
1656
  }
1650
- if (isAdminPhone(senderPhone, cfg.adminPhones)) {
1651
- return { allowed: true, reason: "admin-binding", agentType: "admin", effectiveAccountId: accountId };
1652
- }
1653
1657
  const managed = managedAccountFor(cfg.accountManagers, senderPhone);
1654
- if (managed) {
1655
- if (params.isValidAccount && !params.isValidAccount(managed.account)) {
1658
+ const managerAdmit = (bound) => {
1659
+ if (params.isValidAccount && !params.isValidAccount(bound.account)) {
1656
1660
  return { allowed: false, reason: "account-manager-unresolved", agentType: "admin" };
1657
1661
  }
1658
- return { allowed: true, reason: "account-manager", agentType: "admin", effectiveAccountId: managed.account, passive: managed.mode === "passive" };
1662
+ return {
1663
+ allowed: true,
1664
+ reason: "account-manager",
1665
+ agentType: "admin",
1666
+ effectiveAccountId: bound.account,
1667
+ passive: bound.mode === "passive"
1668
+ };
1669
+ };
1670
+ if (managed && accountId !== void 0 && managed.account === accountId) {
1671
+ return managerAdmit(managed);
1672
+ }
1673
+ if (isAdminPhone(senderPhone, cfg.adminPhones)) {
1674
+ return { allowed: true, reason: "admin-binding", agentType: "admin", effectiveAccountId: accountId };
1675
+ }
1676
+ if (managed && houseAccountId !== void 0 && accountId === houseAccountId) {
1677
+ return managerAdmit(managed);
1659
1678
  }
1660
1679
  switch (cfg.dmPolicy) {
1661
1680
  case "open":
@@ -2710,6 +2729,12 @@ function enumerateValidAccountIds(accountsDir) {
2710
2729
  cache2.set(accountsDir, valid);
2711
2730
  return valid;
2712
2731
  }
2732
+ function isProvisionedAccount(accountsDir, id) {
2733
+ const cached = cache2.get(accountsDir);
2734
+ if (cached?.includes(id)) return true;
2735
+ cache2.delete(accountsDir);
2736
+ return enumerateValidAccountIds(accountsDir).includes(id);
2737
+ }
2713
2738
  function resolveHouseOrSoleAccountId(accountsDir) {
2714
2739
  const valid = enumerateValidAccountIds(accountsDir);
2715
2740
  const houses = [];
@@ -2784,10 +2809,19 @@ function readChannelRoutingOverride(accountsDir, houseId) {
2784
2809
  }
2785
2810
  return target;
2786
2811
  }
2787
- function resolvePlatformAccountId(accountsDir = ACCOUNTS_DIR) {
2812
+ function resolvePlatformAccountIdWithSource(accountsDir = ACCOUNTS_DIR) {
2788
2813
  const houseId = resolveHouseId(accountsDir);
2789
2814
  const routed = readChannelRoutingOverride(accountsDir, houseId);
2790
- return routed ?? houseId;
2815
+ return routed !== null ? { accountId: routed, source: "routing" } : { accountId: houseId, source: "house" };
2816
+ }
2817
+ function resolvePlatformAccountId(accountsDir = ACCOUNTS_DIR) {
2818
+ return resolvePlatformAccountIdWithSource(accountsDir).accountId;
2819
+ }
2820
+ function resolveConnectionPlatformAccountId(credentialAccountId, accountsDir = ACCOUNTS_DIR) {
2821
+ if (isProvisionedAccount(accountsDir, credentialAccountId)) {
2822
+ return { accountId: credentialAccountId, source: "self" };
2823
+ }
2824
+ return resolvePlatformAccountIdWithSource(accountsDir);
2791
2825
  }
2792
2826
  function resolveHouseAccountId(accountsDir = ACCOUNTS_DIR) {
2793
2827
  return resolveHouseId(accountsDir);
@@ -3964,22 +3998,14 @@ async function init(opts) {
3964
3998
  });
3965
3999
  }
3966
4000
  }
3967
- async function startConnection(accountId) {
3968
- if (!configDir) throw new Error("WhatsApp manager not initialized");
3969
- const authDir = resolveAuthDir(configDir, accountId);
3970
- const hasAuth = await authExists(authDir);
3971
- if (!hasAuth) {
3972
- console.error(`${TAG17} no credentials for account=${accountId}`);
3973
- throw new Error(`no credentials for account=${accountId}`);
3974
- }
3975
- let platformAccountId;
3976
- let houseAccountId;
4001
+ function resolveConnectionAccountIds(accountId, accountsDir) {
3977
4002
  try {
3978
- platformAccountId = resolvePlatformAccountId();
3979
- houseAccountId = resolveHouseAccountId();
4003
+ const resolved = resolveConnectionPlatformAccountId(accountId, accountsDir);
4004
+ const houseAccountId = resolveHouseAccountId(accountsDir);
3980
4005
  console.error(
3981
- `[whatsapp-persist] resolved-account-id waname=${accountId} uuid=${platformAccountId} house=${houseAccountId}`
4006
+ `[whatsapp-persist] resolved-account-id waname=${accountId} uuid=${resolved.accountId} house=${houseAccountId} source=${resolved.source}`
3982
4007
  );
4008
+ return { platformAccountId: resolved.accountId, houseAccountId };
3983
4009
  } catch (err) {
3984
4010
  const reason = err instanceof Error ? err.message : String(err);
3985
4011
  console.error(
@@ -3987,6 +4013,16 @@ async function startConnection(accountId) {
3987
4013
  );
3988
4014
  throw err;
3989
4015
  }
4016
+ }
4017
+ async function startConnection(accountId) {
4018
+ if (!configDir) throw new Error("WhatsApp manager not initialized");
4019
+ const authDir = resolveAuthDir(configDir, accountId);
4020
+ const hasAuth = await authExists(authDir);
4021
+ if (!hasAuth) {
4022
+ console.error(`${TAG17} no credentials for account=${accountId}`);
4023
+ throw new Error(`no credentials for account=${accountId}`);
4024
+ }
4025
+ const { platformAccountId, houseAccountId } = resolveConnectionAccountIds(accountId);
3990
4026
  await stopConnection(accountId);
3991
4027
  const conn = {
3992
4028
  accountId,
@@ -4095,6 +4131,11 @@ function resolveConversationPersistAccount(sendAccountId, counterpartyPhone) {
4095
4131
  // to `effectiveAccountId` for every non-manager admit, and that value reaches
4096
4132
  // the graph and the spawn; the dirname is a credential path, not an account.
4097
4133
  accountId: conn.platformAccountId,
4134
+ // Task 2227 — arm 4. The task named only the two inbound call sites; without
4135
+ // it here a non-admin manager's REPLY persists under the connection account
4136
+ // while their inbound persisted under the bound sub-account: one thread, two
4137
+ // accounts, the split Task 1786 exists to prevent.
4138
+ houseAccountId: conn.houseAccountId,
4098
4139
  isValidAccount: (id) => isValidAccountId(id)
4099
4140
  });
4100
4141
  return resolveInboundPersistAccount({
@@ -4111,23 +4152,15 @@ function isAnyConnected() {
4111
4152
  function listConnectionPlatformAccountIds() {
4112
4153
  return [...connections.values()].map((conn) => conn.platformAccountId);
4113
4154
  }
4155
+ function listConnectionAccountStamps() {
4156
+ return [...connections.values()].map((conn) => ({
4157
+ accountId: conn.accountId,
4158
+ platformAccountId: conn.platformAccountId
4159
+ }));
4160
+ }
4114
4161
  async function registerLoginSocket(accountId, sock, authDir) {
4115
4162
  if (!configDir) throw new Error("WhatsApp manager not initialized");
4116
- let platformAccountId;
4117
- let houseAccountId;
4118
- try {
4119
- platformAccountId = resolvePlatformAccountId();
4120
- houseAccountId = resolveHouseAccountId();
4121
- console.error(
4122
- `[whatsapp-persist] resolved-account-id waname=${accountId} uuid=${platformAccountId} house=${houseAccountId}`
4123
- );
4124
- } catch (err) {
4125
- const reason = err instanceof Error ? err.message : String(err);
4126
- console.error(
4127
- `[whatsapp-persist] resolved-account-id FAIL waname=${accountId} reason=${reason}`
4128
- );
4129
- throw err;
4130
- }
4163
+ const { platformAccountId, houseAccountId } = resolveConnectionAccountIds(accountId);
4131
4164
  await stopConnection(accountId);
4132
4165
  const conn = {
4133
4166
  accountId,
@@ -4591,6 +4624,8 @@ function monitorInbound(conn) {
4591
4624
  // Task 1850 — the account SCOPE is the platform UUID, never the
4592
4625
  // dirname. See resolveConversationPersistAccount.
4593
4626
  accountId: conn.platformAccountId,
4627
+ // Task 2227 — the account of the number messaged decides the arm.
4628
+ houseAccountId: conn.houseAccountId,
4594
4629
  isValidAccount: (id) => isValidAccountId(id)
4595
4630
  });
4596
4631
  const persistAccountId = resolveInboundPersistAccount({
@@ -5021,12 +5056,14 @@ async function handleInboundMessage(conn, msg) {
5021
5056
  config: whatsAppConfig,
5022
5057
  accountConfig: whatsAppConfig.accounts?.[conn.accountId],
5023
5058
  accountId: conn.platformAccountId,
5059
+ // Task 2227 — the account of the number messaged decides the arm.
5060
+ houseAccountId: conn.houseAccountId,
5024
5061
  isValidAccount: (id) => isValidAccountId(id),
5025
5062
  resolvePersonByPhone
5026
5063
  });
5027
5064
  }
5028
5065
  console.error(
5029
- `${TAG17} inbound account=${conn.accountId} from=${senderPhone} group=${isGroup} access=${accessResult.allowed ? "allowed" : "blocked"}(${accessResult.reason}) agent=${accessResult.agentType}` + (extracted.mediaType ? ` media=${extracted.mediaType}` : "") + (extracted.quotedMessage ? ` replyTo=${extracted.quotedMessage.id}` : "")
5066
+ `${TAG17} inbound account=${conn.accountId} from=${senderPhone} group=${isGroup} access=${accessResult.allowed ? "allowed" : "blocked"}(${accessResult.reason}) agent=${accessResult.agentType} effectiveAccount=${accessResult.effectiveAccountId ?? ""} destAccount=${conn.platformAccountId}` + (extracted.mediaType ? ` media=${extracted.mediaType}` : "") + (extracted.quotedMessage ? ` replyTo=${extracted.quotedMessage.id}` : "")
5030
5067
  );
5031
5068
  if (!accessResult.allowed && accessResult.reason === "account-manager-unresolved") {
5032
5069
  console.error(
@@ -5189,6 +5226,7 @@ export {
5189
5226
  getAuthAgeMs,
5190
5227
  clearAuth,
5191
5228
  discardAuthDirSync,
5229
+ listCredentialSelfPhones,
5192
5230
  startVersionDriftCensus,
5193
5231
  drainCredsSaveQueue,
5194
5232
  createWaSocket,
@@ -5269,6 +5307,7 @@ export {
5269
5307
  deriveCacheKey,
5270
5308
  buildSelfChatDispatch,
5271
5309
  init,
5310
+ resolveConnectionAccountIds,
5272
5311
  startConnection,
5273
5312
  stopConnection,
5274
5313
  getStatus,
@@ -5282,6 +5321,7 @@ export {
5282
5321
  resolveConversationPersistAccount,
5283
5322
  isAnyConnected,
5284
5323
  listConnectionPlatformAccountIds,
5324
+ listConnectionAccountStamps,
5285
5325
  registerLoginSocket,
5286
5326
  reloadConfig,
5287
5327
  getMessages,
@@ -11,6 +11,7 @@ import {
11
11
  isAnyConnected,
12
12
  isManagerRoutedToHouse,
13
13
  isObserveAccount,
14
+ listConnectionAccountStamps,
14
15
  listConnectionPlatformAccountIds,
15
16
  listConnections,
16
17
  listConversationJids,
@@ -18,6 +19,7 @@ import {
18
19
  messageStoreKey,
19
20
  registerLoginSocket,
20
21
  reloadConfig,
22
+ resolveConnectionAccountIds,
21
23
  resolveConversationPersistAccount,
22
24
  resolveInboundPersistAccount,
23
25
  resolveReadStoreKey,
@@ -28,7 +30,7 @@ import {
28
30
  startConnection,
29
31
  stopConnection,
30
32
  storeMessage
31
- } from "./chunk-2YMGNYA3.js";
33
+ } from "./chunk-UH6GCQO4.js";
32
34
  import "./chunk-BP7LH7MC.js";
33
35
  import "./chunk-PFF6I7KP.js";
34
36
  export {
@@ -43,6 +45,7 @@ export {
43
45
  isAnyConnected,
44
46
  isManagerRoutedToHouse,
45
47
  isObserveAccount,
48
+ listConnectionAccountStamps,
46
49
  listConnectionPlatformAccountIds,
47
50
  listConnections,
48
51
  listConversationJids,
@@ -50,6 +53,7 @@ export {
50
53
  messageStoreKey,
51
54
  registerLoginSocket,
52
55
  reloadConfig,
56
+ resolveConnectionAccountIds,
53
57
  resolveConversationPersistAccount,
54
58
  resolveInboundPersistAccount,
55
59
  resolveReadStoreKey,
@@ -124,8 +124,10 @@ import {
124
124
  isUserJid,
125
125
  isWithinOwnAccountPartition,
126
126
  jidToE164,
127
+ listConnectionAccountStamps,
127
128
  listConnectionPlatformAccountIds,
128
129
  listConnections,
130
+ listCredentialSelfPhones,
129
131
  listLedgerAccounts,
130
132
  listStoreAccountKeys,
131
133
  listStoredAccountIds,
@@ -183,7 +185,7 @@ import {
183
185
  waitForConnection,
184
186
  warnNonUuidAccount,
185
187
  writeChatMetadataMerged
186
- } from "./chunk-2YMGNYA3.js";
188
+ } from "./chunk-UH6GCQO4.js";
187
189
  import {
188
190
  ACCOUNTS_DIR,
189
191
  BIN_DIR,
@@ -1586,7 +1588,11 @@ var require_dist5 = __commonJS({
1586
1588
  "Risk",
1587
1589
  "Source",
1588
1590
  "Finding",
1589
- "Hypothesis"
1591
+ "Hypothesis",
1592
+ // Task 2220 — :Learning carries learning_embedding, which learnings-derive
1593
+ // queries by name to reconcile a claim against an existing learning. Without
1594
+ // it here the chokepoint cannot detect a null-embedding Learning write.
1595
+ "Learning"
1590
1596
  ]);
1591
1597
  exports.DOCUMENT_ARCHIVE_LABELS = /* @__PURE__ */ new Set([
1592
1598
  "KnowledgeDocument",
@@ -4999,7 +5005,7 @@ async function defaultHttpGet(url) {
4999
5005
  }
5000
5006
  }
5001
5007
  async function defaultResolve(accountId) {
5002
- const { resolveSocket: resolveSocket2 } = await import("./manager-XPRN5UF5.js");
5008
+ const { resolveSocket: resolveSocket2 } = await import("./manager-H34562SN.js");
5003
5009
  return resolveSocket2(accountId);
5004
5010
  }
5005
5011
  async function fetchAvatar(accountId, jid, deps = {}) {
@@ -5309,13 +5315,27 @@ function persistAfterPairing(accountDir, accountId, selfPhone) {
5309
5315
  }
5310
5316
  if (selfPhone) {
5311
5317
  const normalized = selfPhone.startsWith("+") ? selfPhone : `+${selfPhone}`;
5312
- if (!Array.isArray(wa.adminPhones)) {
5313
- wa.adminPhones = [];
5318
+ let houseId = null;
5319
+ try {
5320
+ houseId = resolveHouseOrSoleAccountId(dirname2(resolve5(accountDir)));
5321
+ } catch (err) {
5322
+ console.error(
5323
+ `${TAG10} op=selfphone-skipped account=${accountId} phone=${normalized} house=unresolved reason=house-unresolved err=${err instanceof Error ? err.message : String(err)}`
5324
+ );
5314
5325
  }
5315
- const adminPhones = wa.adminPhones;
5316
- if (!adminPhones.includes(normalized)) {
5317
- adminPhones.push(normalized);
5318
- console.error(`${TAG10} added selfPhone=${normalized} to adminPhones`);
5326
+ if (houseId !== null) {
5327
+ if (accountId === "default" || accountId === houseId) {
5328
+ if (!Array.isArray(wa.adminPhones)) {
5329
+ wa.adminPhones = [];
5330
+ }
5331
+ const adminPhones = wa.adminPhones;
5332
+ if (!adminPhones.includes(normalized)) {
5333
+ adminPhones.push(normalized);
5334
+ }
5335
+ console.error(`${TAG10} op=selfphone-registered account=${accountId} phone=${normalized} house=${houseId}`);
5336
+ } else {
5337
+ console.error(`${TAG10} op=selfphone-skipped account=${accountId} phone=${normalized} house=${houseId} reason=non-house`);
5338
+ }
5319
5339
  }
5320
5340
  } else {
5321
5341
  console.error(`${TAG10} skipping adminPhones \u2014 selfPhone is null account=${accountId}`);
@@ -5365,9 +5385,6 @@ function addAdminPhone(accountDir, phone) {
5365
5385
  if (adminPhones.includes(normalized)) {
5366
5386
  return { ok: true, message: `Phone ${normalized} is already in the admin list.` };
5367
5387
  }
5368
- if (managedAccountFor(readAccountManagers(accountDir), normalized)) {
5369
- return { ok: false, error: `${normalized} is bound as an account manager. Remove the account-manager binding before adding it as a house admin \u2014 a phone is a house admin or a sub-account manager, never both.` };
5370
- }
5371
5388
  adminPhones.push(normalized);
5372
5389
  const parsed = WhatsAppConfigSchema.safeParse(wa);
5373
5390
  if (!parsed.success) {
@@ -5563,12 +5580,6 @@ function setAccountManager(accountDir, phone, subAccountId, mode = "active") {
5563
5580
  }
5564
5581
  const wa = config.whatsapp;
5565
5582
  const adminPhones = Array.isArray(wa.adminPhones) ? wa.adminPhones.filter((p) => typeof p === "string") : [];
5566
- if (isAdminPhone(normalized, adminPhones)) {
5567
- return {
5568
- ok: false,
5569
- error: `${normalized} is a house admin phone. Remove it from admin phones before binding it as an account manager \u2014 a phone is a house admin or a sub-account manager, never both.`
5570
- };
5571
- }
5572
5583
  if (!wa.accountManagers || typeof wa.accountManagers !== "object") {
5573
5584
  wa.accountManagers = {};
5574
5585
  }
@@ -5580,7 +5591,9 @@ function setAccountManager(accountDir, phone, subAccountId, mode = "active") {
5580
5591
  }
5581
5592
  config.whatsapp = parsed.data;
5582
5593
  writeConfig(accountDir, config);
5583
- console.error(`${TAG10} bound account manager phone=${normalized} managesAccount=${sub} mode=${mode}`);
5594
+ console.error(
5595
+ `${TAG10} bound account manager phone=${normalized} managesAccount=${sub} mode=${mode} alsoAdmin=${isAdminPhone(normalized, adminPhones)}`
5596
+ );
5584
5597
  reloadManagerConfig(accountDir);
5585
5598
  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.`;
5586
5599
  return { ok: true, message: `Bound ${normalized} as ${mode} account manager for sub-account ${sub}. ${behaviour}` };
@@ -6144,6 +6157,32 @@ function reconcileMessageStoreAccountKeys(input) {
6144
6157
  return { ok: orphanLiveKeys.length === 0, orphanLiveKeys };
6145
6158
  }
6146
6159
 
6160
+ // app/lib/whatsapp/connection-account-reconcile.ts
6161
+ function reconcileConnectionAccountStamps(input) {
6162
+ const registered = new Set(input.registeredAccountIds);
6163
+ const mismatches = input.connections.filter(
6164
+ (c) => registered.has(c.accountId) && c.platformAccountId !== c.accountId
6165
+ );
6166
+ return { ok: mismatches.length === 0, mismatches };
6167
+ }
6168
+
6169
+ // app/lib/whatsapp/self-phone-admin-reconcile.ts
6170
+ var HOUSE_CREDENTIAL_DIRNAME = "default";
6171
+ function reconcileSelfPhoneAdmins(input) {
6172
+ const nonHouse = input.credentials.filter(
6173
+ (c) => c.accountId !== HOUSE_CREDENTIAL_DIRNAME && c.accountId !== input.houseAccountId
6174
+ );
6175
+ const matches = [];
6176
+ for (const cred of nonHouse) {
6177
+ const selfPhone = cred.selfPhone;
6178
+ if (!selfPhone) continue;
6179
+ if (input.adminPhones.some((entry) => phonesMatch(entry, selfPhone))) {
6180
+ matches.push({ accountId: cred.accountId, phone: selfPhone });
6181
+ }
6182
+ }
6183
+ return { ok: matches.length === 0, checked: nonHouse.length, matches };
6184
+ }
6185
+
6147
6186
  // app/lib/whatsapp/outbound/send-document.ts
6148
6187
  import { realpathSync } from "fs";
6149
6188
  import { readFile as readFile3, stat as stat2 } from "fs/promises";
@@ -6513,20 +6552,39 @@ function houseSocketScopeDenial(c, op) {
6513
6552
  console.error(`${TAG14} op=authz-deny route=${op} caller=${callerAccountId ?? "none"} houseAdmin=${houseAdminScope ? "y" : "n"}`);
6514
6553
  return c.json({ ok: false, error: "Not authorized. The WhatsApp connection and its settings are house-scoped; this session may only act on its own account." }, 403);
6515
6554
  }
6516
- function selfOrHouseScopeDenial(c, targetAccountId, op) {
6555
+ function selfOrHouseScopeDenial(c, targetAccountId, op, targetSource = "explicit") {
6517
6556
  const { decision, callerAccountId, houseAdminScope } = evaluateCallerScope(c, targetAccountId);
6557
+ const tail = `target=${targetAccountId} targetSource=${targetSource} caller=${callerAccountId ?? "none"} houseAdmin=${houseAdminScope ? "y" : "n"}`;
6518
6558
  if (decision.authorized) {
6519
- console.error(`${TAG14} op=authz-ok route=${op} target=${targetAccountId} caller=${callerAccountId ?? "none"} houseAdmin=${houseAdminScope ? "y" : "n"}`);
6559
+ console.error(`${TAG14} op=authz-ok route=${op} ${tail}`);
6520
6560
  return null;
6521
6561
  }
6522
- console.error(`${TAG14} op=authz-deny route=${op} target=${targetAccountId} caller=${callerAccountId ?? "none"} houseAdmin=${houseAdminScope ? "y" : "n"}`);
6562
+ console.error(`${TAG14} op=authz-deny route=${op} ${tail}`);
6523
6563
  return c.json({ ok: false, error: "Not authorized. This session may only pair or manage its own account." }, 403);
6524
6564
  }
6565
+ function resolveLifecycleAccountId(c, raw) {
6566
+ if (typeof raw === "string" && raw.trim().length > 0) {
6567
+ return { accountId: validateAccountId(raw), targetSource: "explicit" };
6568
+ }
6569
+ const callerAccountId = c.req.header("x-maxy-caller-account")?.trim() || null;
6570
+ const houseAdminScope = c.req.header("x-maxy-house-admin-scope")?.trim() || null;
6571
+ const houseAccountId = resolveAccount()?.accountId ?? "";
6572
+ const callerIsHouse = !!houseAdminScope || !!callerAccountId && callerAccountId === houseAccountId;
6573
+ if (callerIsHouse || !callerAccountId) {
6574
+ return { accountId: "default", targetSource: "house-default" };
6575
+ }
6576
+ return { accountId: validateAccountId(callerAccountId), targetSource: "caller-default" };
6577
+ }
6525
6578
  var app3 = new Hono();
6526
6579
  app3.get("/status", (c) => {
6527
6580
  try {
6528
- const denial = houseSocketScopeDenial(c, "status");
6529
- if (denial) return denial;
6581
+ const houseAccountId = resolveAccount()?.accountId ?? "";
6582
+ const { decision, callerAccountId, houseAdminScope } = evaluateCallerScope(c, houseAccountId);
6583
+ const houseScoped = decision.authorized;
6584
+ if (!houseScoped && !callerAccountId) {
6585
+ console.error(`${TAG14} op=authz-deny route=status caller=none houseAdmin=${houseAdminScope ? "y" : "n"}`);
6586
+ return c.json({ ok: false, error: "Not authorized. The WhatsApp connection and its settings are house-scoped; this session may only act on its own account." }, 403);
6587
+ }
6530
6588
  const live = getStatus();
6531
6589
  const liveIds = new Set(live.map((a) => a.accountId));
6532
6590
  const activeLoginIds = new Set(listActiveLoginAccountIds());
@@ -6540,6 +6598,11 @@ app3.get("/status", (c) => {
6540
6598
  }));
6541
6599
  const summary = accounts.map((a) => `${a.accountId}:${a.connected ? "up" : a.linkedUnconfigured ? "linked-unconfigured" : "down"}:${a.dispatchInbound ? "dispatch" : "observe"}`).join(", ");
6542
6600
  console.error(`${TAG14} status accounts=${accounts.length} [${summary}]`);
6601
+ if (!houseScoped) {
6602
+ const own = accounts.filter((a) => a.accountId === callerAccountId);
6603
+ console.error(`${TAG14} op=status-projection caller=${callerAccountId.slice(0, 8)} scope=self rows=${own.length}`);
6604
+ return c.json({ accounts: own });
6605
+ }
6543
6606
  const reachableAccountIds = [
6544
6607
  .../* @__PURE__ */ new Set([...listStoredAccountIds(), ...listConnectionPlatformAccountIds()])
6545
6608
  ];
@@ -6550,7 +6613,25 @@ app3.get("/status", (c) => {
6550
6613
  if (!storeKeyReconcile.ok) {
6551
6614
  console.warn(`${TAG14} store-key-drift orphanLiveKeys=[${storeKeyReconcile.orphanLiveKeys.join(",")}] \u2014 in-memory cache keyed by a value with no disk account; read tools will miss`);
6552
6615
  }
6553
- return c.json({ accounts, storeKeyReconcile });
6616
+ const connectionAccountReconcile = reconcileConnectionAccountStamps({
6617
+ connections: listConnectionAccountStamps(),
6618
+ registeredAccountIds: listValidAccounts().map((a) => a.accountId)
6619
+ });
6620
+ if (!connectionAccountReconcile.ok) {
6621
+ const pairs = connectionAccountReconcile.mismatches.map((m) => `${m.accountId}:${m.platformAccountId}`).join(",");
6622
+ console.warn(`${TAG14} connection-account-drift mismatches=[${pairs}] \u2014 a socket keyed by a registered account is stamping a different one; its messages persist and read under that account`);
6623
+ }
6624
+ const selfPhoneAdminReconcile = reconcileSelfPhoneAdmins({
6625
+ credentials: listCredentialSelfPhones(credsRoot),
6626
+ houseAccountId,
6627
+ adminPhones: accountDir ? readAdminPhones(accountDir) : []
6628
+ });
6629
+ console.error(`${TAG14} op=selfphone-admin-reconcile checked=${selfPhoneAdminReconcile.checked} matched=${selfPhoneAdminReconcile.matches.length} ok=${selfPhoneAdminReconcile.ok}`);
6630
+ for (const match of selfPhoneAdminReconcile.matches) {
6631
+ console.warn(`${TAG14} selfphone-admin-drift account=${match.accountId} phone=${match.phone} \u2014 this sub-account's own handset is a house admin on the house number; remove it with whatsapp-config action:"remove-admin-phone"`);
6632
+ }
6633
+ console.error(`${TAG14} op=status-projection caller=${callerAccountId?.slice(0, 8) ?? "none"} scope=house rows=${accounts.length}`);
6634
+ return c.json({ accounts, storeKeyReconcile, connectionAccountReconcile, selfPhoneAdminReconcile });
6554
6635
  } catch (err) {
6555
6636
  console.error(`${TAG14} status error: ${String(err)}`);
6556
6637
  return c.json({ error: String(err) }, 500);
@@ -6559,8 +6640,8 @@ app3.get("/status", (c) => {
6559
6640
  app3.post("/login/start", async (c) => {
6560
6641
  try {
6561
6642
  const body = await c.req.json().catch(() => ({}));
6562
- const accountId = validateAccountId(body.accountId);
6563
- const denial = selfOrHouseScopeDenial(c, accountId, "login/start");
6643
+ const { accountId, targetSource } = resolveLifecycleAccountId(c, body.accountId);
6644
+ const denial = selfOrHouseScopeDenial(c, accountId, "login/start", targetSource);
6564
6645
  if (denial) return denial;
6565
6646
  const force = body.force ?? false;
6566
6647
  const phone = typeof body.phone === "string" ? body.phone.trim() : "";
@@ -6580,8 +6661,8 @@ app3.post("/login/start", async (c) => {
6580
6661
  app3.post("/login/wait", async (c) => {
6581
6662
  try {
6582
6663
  const body = await c.req.json().catch(() => ({}));
6583
- const accountId = validateAccountId(body.accountId);
6584
- const denial = selfOrHouseScopeDenial(c, accountId, "login/wait");
6664
+ const { accountId, targetSource } = resolveLifecycleAccountId(c, body.accountId);
6665
+ const denial = selfOrHouseScopeDenial(c, accountId, "login/wait", targetSource);
6585
6666
  if (denial) return denial;
6586
6667
  const timeoutMs = body.timeoutMs ?? 6e4;
6587
6668
  const result = await waitForLogin({ accountId, timeoutMs });
@@ -6600,8 +6681,8 @@ app3.post("/login/wait", async (c) => {
6600
6681
  app3.post("/disconnect", async (c) => {
6601
6682
  try {
6602
6683
  const body = await c.req.json().catch(() => ({}));
6603
- const accountId = validateAccountId(body.accountId);
6604
- const denial = selfOrHouseScopeDenial(c, accountId, "disconnect");
6684
+ const { accountId, targetSource } = resolveLifecycleAccountId(c, body.accountId);
6685
+ const denial = selfOrHouseScopeDenial(c, accountId, "disconnect", targetSource);
6605
6686
  if (denial) return denial;
6606
6687
  await stopConnection(accountId);
6607
6688
  return c.json({ disconnected: true, accountId });
@@ -6614,8 +6695,9 @@ app3.post("/reconnect", async (c) => {
6614
6695
  let accountId = "(unresolved)";
6615
6696
  try {
6616
6697
  const body = await c.req.json().catch(() => ({}));
6617
- accountId = validateAccountId(body.accountId);
6618
- const denial = selfOrHouseScopeDenial(c, accountId, "reconnect");
6698
+ const resolved = resolveLifecycleAccountId(c, body.accountId);
6699
+ accountId = resolved.accountId;
6700
+ const denial = selfOrHouseScopeDenial(c, accountId, "reconnect", resolved.targetSource);
6619
6701
  if (denial) return denial;
6620
6702
  await startConnection(accountId);
6621
6703
  console.error(
@@ -29644,14 +29726,12 @@ async function resolveClientOwnerUserId(accountId, deps = {}) {
29644
29726
  }
29645
29727
 
29646
29728
  // app/lib/whatsapp/inbound/channel-admin-binding-drift.ts
29647
- function findAccountManagerDrift(accountManagers, adminPhones, validAccountIds) {
29729
+ function findAccountManagerDrift(accountManagers, validAccountIds) {
29648
29730
  const drift = [];
29649
29731
  for (const [phone, entry] of Object.entries(accountManagers)) {
29650
29732
  const managesAccount = typeof entry === "string" ? entry : entry.managesAccount;
29651
29733
  if (!validAccountIds.includes(managesAccount)) {
29652
29734
  drift.push({ phone, managesAccount, reason: "not-in-registry" });
29653
- } else if (adminPhones.some((a) => phonesMatch(a, phone))) {
29654
- drift.push({ phone, managesAccount, reason: "phone-also-admin" });
29655
29735
  }
29656
29736
  }
29657
29737
  return drift;
@@ -29709,7 +29789,7 @@ function warnOnChannelAdminBindingDrift() {
29709
29789
  `[admin-identity] binding-drift accountId=${acct.accountId} phone=${d.phone} reason=${d.reason}${tail}`
29710
29790
  );
29711
29791
  }
29712
- const managerDrift = findAccountManagerDrift(readAccountManagers(acct.accountDir), adminPhones, validAccountIds);
29792
+ const managerDrift = findAccountManagerDrift(readAccountManagers(acct.accountDir), validAccountIds);
29713
29793
  for (const d of managerDrift) {
29714
29794
  console.error(
29715
29795
  `[admin-identity] account-manager-drift accountId=${acct.accountId} phone=${d.phone} managesAccount=${d.managesAccount} reason=${d.reason}`
@@ -29720,6 +29800,9 @@ function warnOnChannelAdminBindingDrift() {
29720
29800
 
29721
29801
  // server/routes/channel/effective-account.ts
29722
29802
  function resolveScheduledEffectiveAccount(deps, accountDir, destination) {
29803
+ if (isAdminPhone(destination, deps.readAdminPhones(accountDir))) {
29804
+ return deps.resolvePlatformAccountId();
29805
+ }
29723
29806
  const managed = managedAccountFor(deps.readAccountManagers(accountDir), destination);
29724
29807
  if (managed === null) return deps.resolvePlatformAccountId();
29725
29808
  if (managed.mode === "passive") return null;
@@ -29728,7 +29811,7 @@ function resolveScheduledEffectiveAccount(deps, accountDir, destination) {
29728
29811
  }
29729
29812
  function resolveScheduledEffectiveAccountForInstall(accountDir, destination) {
29730
29813
  return resolveScheduledEffectiveAccount(
29731
- { readAccountManagers, resolvePlatformAccountId, isValidAccountId },
29814
+ { readAccountManagers, readAdminPhones, resolvePlatformAccountId, isValidAccountId },
29732
29815
  accountDir,
29733
29816
  destination
29734
29817
  );
@@ -1,52 +0,0 @@
1
- /* "Add to SiteDesk" pill injected into LinkedIn profile and DM thread pages. */
2
-
3
- #maxy-linkedin-pill.maxy-pill {
4
- position: relative;
5
- z-index: 9999;
6
- display: inline-flex;
7
- align-items: center;
8
- gap: 6px;
9
- margin: 12px;
10
- padding: 6px 14px;
11
- border: 1px solid #0a66c2;
12
- border-radius: 999px;
13
- background: #ffffff;
14
- color: #0a66c2;
15
- font: 600 13px/1.2 -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
16
- cursor: pointer;
17
- box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
18
- transition: background 120ms ease, color 120ms ease, border-color 120ms ease;
19
- }
20
-
21
- #maxy-linkedin-pill.maxy-pill:hover {
22
- background: #0a66c2;
23
- color: #ffffff;
24
- }
25
-
26
- #maxy-linkedin-pill.maxy-pill[data-state="busy"] {
27
- background: #f4f7fb;
28
- border-color: #a3b6c9;
29
- color: #46596d;
30
- cursor: progress;
31
- }
32
-
33
- #maxy-linkedin-pill.maxy-pill[data-state="ok"] {
34
- background: #e6f4ea;
35
- border-color: #2e7d32;
36
- color: #2e7d32;
37
- cursor: default;
38
- }
39
-
40
- #maxy-linkedin-pill.maxy-pill[data-state="error"] {
41
- background: #fdecea;
42
- border-color: #b00020;
43
- color: #b00020;
44
- cursor: pointer;
45
- }
46
-
47
- #maxy-linkedin-pill.maxy-pill[data-state="auth"] {
48
- background: #fff7e0;
49
- border-color: #b8860b;
50
- color: #b8860b;
51
- cursor: pointer;
52
- }