@rubytech/create-sitedesk-code 0.1.530 → 0.1.531

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 (97) 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 +7 -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/whatsapp/mcp/dist/index.js +4 -4
  73. package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
  74. package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +3 -3
  75. package/payload/platform/plugins/whatsapp/skills/connect-whatsapp/SKILL.md +6 -2
  76. package/payload/platform/plugins/whatsapp/skills/manage-whatsapp-config/SKILL.md +5 -1
  77. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  78. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +3 -0
  79. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  80. package/payload/platform/services/claude-session-manager/dist/learning-index.d.ts +28 -0
  81. package/payload/platform/services/claude-session-manager/dist/learning-index.d.ts.map +1 -0
  82. package/payload/platform/services/claude-session-manager/dist/learning-index.js +70 -0
  83. package/payload/platform/services/claude-session-manager/dist/learning-index.js.map +1 -0
  84. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  85. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +18 -0
  86. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  87. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts +6 -0
  88. package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -1
  89. package/payload/platform/services/claude-session-manager/dist/system-prompt.js +17 -0
  90. package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
  91. package/payload/platform/templates/specialists/agents/librarian.md +1 -1
  92. package/payload/server/{chunk-2YMGNYA3.js → chunk-SVB5NZ64.js} +41 -30
  93. package/payload/server/{manager-XPRN5UF5.js → manager-4Q3DTLXO.js} +5 -1
  94. package/payload/server/server.js +65 -17
  95. package/payload/platform/plugins/linkedin-extension/extension/assets/pill.css +0 -52
  96. package/payload/platform/plugins/linkedin-extension/extension/content/profile.js +0 -100
  97. package/payload/platform/plugins/linkedin-extension/extension/content/thread.js +0 -102
@@ -2710,6 +2710,12 @@ function enumerateValidAccountIds(accountsDir) {
2710
2710
  cache2.set(accountsDir, valid);
2711
2711
  return valid;
2712
2712
  }
2713
+ function isProvisionedAccount(accountsDir, id) {
2714
+ const cached = cache2.get(accountsDir);
2715
+ if (cached?.includes(id)) return true;
2716
+ cache2.delete(accountsDir);
2717
+ return enumerateValidAccountIds(accountsDir).includes(id);
2718
+ }
2713
2719
  function resolveHouseOrSoleAccountId(accountsDir) {
2714
2720
  const valid = enumerateValidAccountIds(accountsDir);
2715
2721
  const houses = [];
@@ -2784,10 +2790,19 @@ function readChannelRoutingOverride(accountsDir, houseId) {
2784
2790
  }
2785
2791
  return target;
2786
2792
  }
2787
- function resolvePlatformAccountId(accountsDir = ACCOUNTS_DIR) {
2793
+ function resolvePlatformAccountIdWithSource(accountsDir = ACCOUNTS_DIR) {
2788
2794
  const houseId = resolveHouseId(accountsDir);
2789
2795
  const routed = readChannelRoutingOverride(accountsDir, houseId);
2790
- return routed ?? houseId;
2796
+ return routed !== null ? { accountId: routed, source: "routing" } : { accountId: houseId, source: "house" };
2797
+ }
2798
+ function resolvePlatformAccountId(accountsDir = ACCOUNTS_DIR) {
2799
+ return resolvePlatformAccountIdWithSource(accountsDir).accountId;
2800
+ }
2801
+ function resolveConnectionPlatformAccountId(credentialAccountId, accountsDir = ACCOUNTS_DIR) {
2802
+ if (isProvisionedAccount(accountsDir, credentialAccountId)) {
2803
+ return { accountId: credentialAccountId, source: "self" };
2804
+ }
2805
+ return resolvePlatformAccountIdWithSource(accountsDir);
2791
2806
  }
2792
2807
  function resolveHouseAccountId(accountsDir = ACCOUNTS_DIR) {
2793
2808
  return resolveHouseId(accountsDir);
@@ -3964,22 +3979,14 @@ async function init(opts) {
3964
3979
  });
3965
3980
  }
3966
3981
  }
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;
3982
+ function resolveConnectionAccountIds(accountId, accountsDir) {
3977
3983
  try {
3978
- platformAccountId = resolvePlatformAccountId();
3979
- houseAccountId = resolveHouseAccountId();
3984
+ const resolved = resolveConnectionPlatformAccountId(accountId, accountsDir);
3985
+ const houseAccountId = resolveHouseAccountId(accountsDir);
3980
3986
  console.error(
3981
- `[whatsapp-persist] resolved-account-id waname=${accountId} uuid=${platformAccountId} house=${houseAccountId}`
3987
+ `[whatsapp-persist] resolved-account-id waname=${accountId} uuid=${resolved.accountId} house=${houseAccountId} source=${resolved.source}`
3982
3988
  );
3989
+ return { platformAccountId: resolved.accountId, houseAccountId };
3983
3990
  } catch (err) {
3984
3991
  const reason = err instanceof Error ? err.message : String(err);
3985
3992
  console.error(
@@ -3987,6 +3994,16 @@ async function startConnection(accountId) {
3987
3994
  );
3988
3995
  throw err;
3989
3996
  }
3997
+ }
3998
+ async function startConnection(accountId) {
3999
+ if (!configDir) throw new Error("WhatsApp manager not initialized");
4000
+ const authDir = resolveAuthDir(configDir, accountId);
4001
+ const hasAuth = await authExists(authDir);
4002
+ if (!hasAuth) {
4003
+ console.error(`${TAG17} no credentials for account=${accountId}`);
4004
+ throw new Error(`no credentials for account=${accountId}`);
4005
+ }
4006
+ const { platformAccountId, houseAccountId } = resolveConnectionAccountIds(accountId);
3990
4007
  await stopConnection(accountId);
3991
4008
  const conn = {
3992
4009
  accountId,
@@ -4111,23 +4128,15 @@ function isAnyConnected() {
4111
4128
  function listConnectionPlatformAccountIds() {
4112
4129
  return [...connections.values()].map((conn) => conn.platformAccountId);
4113
4130
  }
4131
+ function listConnectionAccountStamps() {
4132
+ return [...connections.values()].map((conn) => ({
4133
+ accountId: conn.accountId,
4134
+ platformAccountId: conn.platformAccountId
4135
+ }));
4136
+ }
4114
4137
  async function registerLoginSocket(accountId, sock, authDir) {
4115
4138
  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
- }
4139
+ const { platformAccountId, houseAccountId } = resolveConnectionAccountIds(accountId);
4131
4140
  await stopConnection(accountId);
4132
4141
  const conn = {
4133
4142
  accountId,
@@ -5269,6 +5278,7 @@ export {
5269
5278
  deriveCacheKey,
5270
5279
  buildSelfChatDispatch,
5271
5280
  init,
5281
+ resolveConnectionAccountIds,
5272
5282
  startConnection,
5273
5283
  stopConnection,
5274
5284
  getStatus,
@@ -5282,6 +5292,7 @@ export {
5282
5292
  resolveConversationPersistAccount,
5283
5293
  isAnyConnected,
5284
5294
  listConnectionPlatformAccountIds,
5295
+ listConnectionAccountStamps,
5285
5296
  registerLoginSocket,
5286
5297
  reloadConfig,
5287
5298
  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-SVB5NZ64.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,6 +124,7 @@ import {
124
124
  isUserJid,
125
125
  isWithinOwnAccountPartition,
126
126
  jidToE164,
127
+ listConnectionAccountStamps,
127
128
  listConnectionPlatformAccountIds,
128
129
  listConnections,
129
130
  listLedgerAccounts,
@@ -183,7 +184,7 @@ import {
183
184
  waitForConnection,
184
185
  warnNonUuidAccount,
185
186
  writeChatMetadataMerged
186
- } from "./chunk-2YMGNYA3.js";
187
+ } from "./chunk-SVB5NZ64.js";
187
188
  import {
188
189
  ACCOUNTS_DIR,
189
190
  BIN_DIR,
@@ -1586,7 +1587,11 @@ var require_dist5 = __commonJS({
1586
1587
  "Risk",
1587
1588
  "Source",
1588
1589
  "Finding",
1589
- "Hypothesis"
1590
+ "Hypothesis",
1591
+ // Task 2220 — :Learning carries learning_embedding, which learnings-derive
1592
+ // queries by name to reconcile a claim against an existing learning. Without
1593
+ // it here the chokepoint cannot detect a null-embedding Learning write.
1594
+ "Learning"
1590
1595
  ]);
1591
1596
  exports.DOCUMENT_ARCHIVE_LABELS = /* @__PURE__ */ new Set([
1592
1597
  "KnowledgeDocument",
@@ -4999,7 +5004,7 @@ async function defaultHttpGet(url) {
4999
5004
  }
5000
5005
  }
5001
5006
  async function defaultResolve(accountId) {
5002
- const { resolveSocket: resolveSocket2 } = await import("./manager-XPRN5UF5.js");
5007
+ const { resolveSocket: resolveSocket2 } = await import("./manager-4Q3DTLXO.js");
5003
5008
  return resolveSocket2(accountId);
5004
5009
  }
5005
5010
  async function fetchAvatar(accountId, jid, deps = {}) {
@@ -6144,6 +6149,15 @@ function reconcileMessageStoreAccountKeys(input) {
6144
6149
  return { ok: orphanLiveKeys.length === 0, orphanLiveKeys };
6145
6150
  }
6146
6151
 
6152
+ // app/lib/whatsapp/connection-account-reconcile.ts
6153
+ function reconcileConnectionAccountStamps(input) {
6154
+ const registered = new Set(input.registeredAccountIds);
6155
+ const mismatches = input.connections.filter(
6156
+ (c) => registered.has(c.accountId) && c.platformAccountId !== c.accountId
6157
+ );
6158
+ return { ok: mismatches.length === 0, mismatches };
6159
+ }
6160
+
6147
6161
  // app/lib/whatsapp/outbound/send-document.ts
6148
6162
  import { realpathSync } from "fs";
6149
6163
  import { readFile as readFile3, stat as stat2 } from "fs/promises";
@@ -6513,20 +6527,39 @@ function houseSocketScopeDenial(c, op) {
6513
6527
  console.error(`${TAG14} op=authz-deny route=${op} caller=${callerAccountId ?? "none"} houseAdmin=${houseAdminScope ? "y" : "n"}`);
6514
6528
  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
6529
  }
6516
- function selfOrHouseScopeDenial(c, targetAccountId, op) {
6530
+ function selfOrHouseScopeDenial(c, targetAccountId, op, targetSource = "explicit") {
6517
6531
  const { decision, callerAccountId, houseAdminScope } = evaluateCallerScope(c, targetAccountId);
6532
+ const tail = `target=${targetAccountId} targetSource=${targetSource} caller=${callerAccountId ?? "none"} houseAdmin=${houseAdminScope ? "y" : "n"}`;
6518
6533
  if (decision.authorized) {
6519
- console.error(`${TAG14} op=authz-ok route=${op} target=${targetAccountId} caller=${callerAccountId ?? "none"} houseAdmin=${houseAdminScope ? "y" : "n"}`);
6534
+ console.error(`${TAG14} op=authz-ok route=${op} ${tail}`);
6520
6535
  return null;
6521
6536
  }
6522
- console.error(`${TAG14} op=authz-deny route=${op} target=${targetAccountId} caller=${callerAccountId ?? "none"} houseAdmin=${houseAdminScope ? "y" : "n"}`);
6537
+ console.error(`${TAG14} op=authz-deny route=${op} ${tail}`);
6523
6538
  return c.json({ ok: false, error: "Not authorized. This session may only pair or manage its own account." }, 403);
6524
6539
  }
6540
+ function resolveLifecycleAccountId(c, raw) {
6541
+ if (typeof raw === "string" && raw.trim().length > 0) {
6542
+ return { accountId: validateAccountId(raw), targetSource: "explicit" };
6543
+ }
6544
+ const callerAccountId = c.req.header("x-maxy-caller-account")?.trim() || null;
6545
+ const houseAdminScope = c.req.header("x-maxy-house-admin-scope")?.trim() || null;
6546
+ const houseAccountId = resolveAccount()?.accountId ?? "";
6547
+ const callerIsHouse = !!houseAdminScope || !!callerAccountId && callerAccountId === houseAccountId;
6548
+ if (callerIsHouse || !callerAccountId) {
6549
+ return { accountId: "default", targetSource: "house-default" };
6550
+ }
6551
+ return { accountId: validateAccountId(callerAccountId), targetSource: "caller-default" };
6552
+ }
6525
6553
  var app3 = new Hono();
6526
6554
  app3.get("/status", (c) => {
6527
6555
  try {
6528
- const denial = houseSocketScopeDenial(c, "status");
6529
- if (denial) return denial;
6556
+ const houseAccountId = resolveAccount()?.accountId ?? "";
6557
+ const { decision, callerAccountId, houseAdminScope } = evaluateCallerScope(c, houseAccountId);
6558
+ const houseScoped = decision.authorized;
6559
+ if (!houseScoped && !callerAccountId) {
6560
+ console.error(`${TAG14} op=authz-deny route=status caller=none houseAdmin=${houseAdminScope ? "y" : "n"}`);
6561
+ 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);
6562
+ }
6530
6563
  const live = getStatus();
6531
6564
  const liveIds = new Set(live.map((a) => a.accountId));
6532
6565
  const activeLoginIds = new Set(listActiveLoginAccountIds());
@@ -6540,6 +6573,11 @@ app3.get("/status", (c) => {
6540
6573
  }));
6541
6574
  const summary = accounts.map((a) => `${a.accountId}:${a.connected ? "up" : a.linkedUnconfigured ? "linked-unconfigured" : "down"}:${a.dispatchInbound ? "dispatch" : "observe"}`).join(", ");
6542
6575
  console.error(`${TAG14} status accounts=${accounts.length} [${summary}]`);
6576
+ if (!houseScoped) {
6577
+ const own = accounts.filter((a) => a.accountId === callerAccountId);
6578
+ console.error(`${TAG14} op=status-projection caller=${callerAccountId.slice(0, 8)} scope=self rows=${own.length}`);
6579
+ return c.json({ accounts: own });
6580
+ }
6543
6581
  const reachableAccountIds = [
6544
6582
  .../* @__PURE__ */ new Set([...listStoredAccountIds(), ...listConnectionPlatformAccountIds()])
6545
6583
  ];
@@ -6550,7 +6588,16 @@ app3.get("/status", (c) => {
6550
6588
  if (!storeKeyReconcile.ok) {
6551
6589
  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
6590
  }
6553
- return c.json({ accounts, storeKeyReconcile });
6591
+ const connectionAccountReconcile = reconcileConnectionAccountStamps({
6592
+ connections: listConnectionAccountStamps(),
6593
+ registeredAccountIds: listValidAccounts().map((a) => a.accountId)
6594
+ });
6595
+ if (!connectionAccountReconcile.ok) {
6596
+ const pairs = connectionAccountReconcile.mismatches.map((m) => `${m.accountId}:${m.platformAccountId}`).join(",");
6597
+ 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`);
6598
+ }
6599
+ console.error(`${TAG14} op=status-projection caller=${callerAccountId?.slice(0, 8) ?? "none"} scope=house rows=${accounts.length}`);
6600
+ return c.json({ accounts, storeKeyReconcile, connectionAccountReconcile });
6554
6601
  } catch (err) {
6555
6602
  console.error(`${TAG14} status error: ${String(err)}`);
6556
6603
  return c.json({ error: String(err) }, 500);
@@ -6559,8 +6606,8 @@ app3.get("/status", (c) => {
6559
6606
  app3.post("/login/start", async (c) => {
6560
6607
  try {
6561
6608
  const body = await c.req.json().catch(() => ({}));
6562
- const accountId = validateAccountId(body.accountId);
6563
- const denial = selfOrHouseScopeDenial(c, accountId, "login/start");
6609
+ const { accountId, targetSource } = resolveLifecycleAccountId(c, body.accountId);
6610
+ const denial = selfOrHouseScopeDenial(c, accountId, "login/start", targetSource);
6564
6611
  if (denial) return denial;
6565
6612
  const force = body.force ?? false;
6566
6613
  const phone = typeof body.phone === "string" ? body.phone.trim() : "";
@@ -6580,8 +6627,8 @@ app3.post("/login/start", async (c) => {
6580
6627
  app3.post("/login/wait", async (c) => {
6581
6628
  try {
6582
6629
  const body = await c.req.json().catch(() => ({}));
6583
- const accountId = validateAccountId(body.accountId);
6584
- const denial = selfOrHouseScopeDenial(c, accountId, "login/wait");
6630
+ const { accountId, targetSource } = resolveLifecycleAccountId(c, body.accountId);
6631
+ const denial = selfOrHouseScopeDenial(c, accountId, "login/wait", targetSource);
6585
6632
  if (denial) return denial;
6586
6633
  const timeoutMs = body.timeoutMs ?? 6e4;
6587
6634
  const result = await waitForLogin({ accountId, timeoutMs });
@@ -6600,8 +6647,8 @@ app3.post("/login/wait", async (c) => {
6600
6647
  app3.post("/disconnect", async (c) => {
6601
6648
  try {
6602
6649
  const body = await c.req.json().catch(() => ({}));
6603
- const accountId = validateAccountId(body.accountId);
6604
- const denial = selfOrHouseScopeDenial(c, accountId, "disconnect");
6650
+ const { accountId, targetSource } = resolveLifecycleAccountId(c, body.accountId);
6651
+ const denial = selfOrHouseScopeDenial(c, accountId, "disconnect", targetSource);
6605
6652
  if (denial) return denial;
6606
6653
  await stopConnection(accountId);
6607
6654
  return c.json({ disconnected: true, accountId });
@@ -6614,8 +6661,9 @@ app3.post("/reconnect", async (c) => {
6614
6661
  let accountId = "(unresolved)";
6615
6662
  try {
6616
6663
  const body = await c.req.json().catch(() => ({}));
6617
- accountId = validateAccountId(body.accountId);
6618
- const denial = selfOrHouseScopeDenial(c, accountId, "reconnect");
6664
+ const resolved = resolveLifecycleAccountId(c, body.accountId);
6665
+ accountId = resolved.accountId;
6666
+ const denial = selfOrHouseScopeDenial(c, accountId, "reconnect", resolved.targetSource);
6619
6667
  if (denial) return denial;
6620
6668
  await startConnection(accountId);
6621
6669
  console.error(
@@ -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
- }
@@ -1,100 +0,0 @@
1
- // Profile content script. Runs on linkedin.com/in/*.
2
- // Manifest concatenates extractors.js + this file, so MaxyLinkedinExtractors
3
- // is in scope. One click → scrape → post to the service worker → pill state.
4
-
5
- ;(function () {
6
- const TAG_MOUNT = '[linkedin-ext-mount]'
7
- const TAG_SCRAPE = '[linkedin-ext-scrape]'
8
- const TAG_ERROR = '[linkedin-ext-error]'
9
- const PILL_ID = 'maxy-linkedin-pill'
10
-
11
- function setState(pill, state, label) {
12
- pill.dataset.state = state
13
- if (label) pill.textContent = label
14
- }
15
-
16
- async function onClick(pill) {
17
- const dispatchId = crypto.randomUUID()
18
- setState(pill, 'busy', 'Capturing…')
19
-
20
- const result = globalThis.MaxyLinkedinExtractors.extractProfile(document)
21
- if (!result.ok) {
22
- console.error(TAG_SCRAPE + ' page=profile ok=false missing=' + result.missing.join(','))
23
- setState(pill, 'error', 'Missing: ' + result.missing.join(', '))
24
- return
25
- }
26
- console.log(TAG_SCRAPE + ' page=profile ok=true fields=' + Object.keys(result.payload).join(','))
27
-
28
- const envelope = {
29
- source: 'linkedin',
30
- kind: 'profile',
31
- capturedAt: new Date().toISOString(),
32
- pageUrl: location.href.split('?')[0].split('#')[0],
33
- dispatchId: dispatchId,
34
- profile: result.payload,
35
- }
36
-
37
- let reply
38
- try {
39
- reply = await chrome.runtime.sendMessage({ type: 'linkedin-ingest', envelope: envelope })
40
- } catch (err) {
41
- console.error(TAG_ERROR + ' reason=worker-unreachable detail=' + (err && err.message ? err.message : String(err)))
42
- setState(pill, 'error', 'Capture failed')
43
- return
44
- }
45
-
46
- if (!reply || !reply.ok) {
47
- // `worker-unreachable` is the extension's OWN service worker failing to
48
- // answer. It is not `unreachable`, which means the admin host did not
49
- // answer — blaming SiteDesk here sends the operator to check a tunnel that
50
- // was never involved.
51
- const reason = (reply && reply.reason) || 'worker-unreachable'
52
- const detail = (reply && reply.detail) || ''
53
- console.error(TAG_ERROR + ' reason=' + reason + ' detail=' + detail)
54
- // Task 2208 — the ingest token is missing or was refused. There is no
55
- // sign-in to perform: the operator asks the SiteDesk agent for the token and
56
- // pastes it on the options page.
57
- if (reason === 'auth') {
58
- setState(pill, 'auth', 'Paste SiteDesk token')
59
- pill.onclick = function () { chrome.runtime.openOptionsPage() }
60
- return
61
- }
62
- // Task 2185 — the admin host was never granted, so the POST never left
63
- // the browser. Sending the operator to the options page is the fix.
64
- if (reason === 'permission') {
65
- setState(pill, 'auth', 'Grant access')
66
- pill.onclick = function () { chrome.runtime.openOptionsPage() }
67
- return
68
- }
69
- if (reason === 'unreachable') {
70
- setState(pill, 'error', 'SiteDesk unreachable')
71
- return
72
- }
73
- setState(pill, 'error', 'Capture failed')
74
- return
75
- }
76
-
77
- setState(pill, 'ok', 'Captured ✓')
78
- }
79
-
80
- function mount() {
81
- if (document.getElementById(PILL_ID)) return
82
- const anchor = document.querySelector('section.top-card-layout, main') || document.body
83
- if (!anchor) return
84
- const pill = document.createElement('button')
85
- pill.id = PILL_ID
86
- pill.className = 'maxy-pill'
87
- pill.type = 'button'
88
- pill.textContent = 'Add to SiteDesk'
89
- pill.dataset.state = 'idle'
90
- pill.addEventListener('click', function () { onClick(pill) })
91
- anchor.prepend(pill)
92
- console.log(TAG_MOUNT + ' page=profile url=' + location.href)
93
- }
94
-
95
- if (document.readyState === 'loading') {
96
- document.addEventListener('DOMContentLoaded', mount, { once: true })
97
- } else {
98
- mount()
99
- }
100
- })()
@@ -1,102 +0,0 @@
1
- // Thread content script. Runs on linkedin.com/messaging/thread/*.
2
- // Manifest concatenates extractors.js + this file.
3
-
4
- ;(function () {
5
- const TAG_MOUNT = '[linkedin-ext-mount]'
6
- const TAG_SCRAPE = '[linkedin-ext-scrape]'
7
- const TAG_ERROR = '[linkedin-ext-error]'
8
- const PILL_ID = 'maxy-linkedin-pill'
9
-
10
- function setState(pill, state, label) {
11
- pill.dataset.state = state
12
- if (label) pill.textContent = label
13
- }
14
-
15
- async function onClick(pill) {
16
- const dispatchId = crypto.randomUUID()
17
- setState(pill, 'busy', 'Capturing…')
18
-
19
- const result = globalThis.MaxyLinkedinExtractors.extractThread(document)
20
- if (!result.ok) {
21
- console.error(TAG_SCRAPE + ' page=thread ok=false missing=' + result.missing.join(','))
22
- setState(pill, 'error', 'Missing: ' + result.missing.join(', '))
23
- return
24
- }
25
- console.log(
26
- TAG_SCRAPE + ' page=thread ok=true fields=participants,bodyText participantCount=' +
27
- result.payload.participants.length,
28
- )
29
-
30
- const envelope = {
31
- source: 'linkedin',
32
- kind: 'thread',
33
- capturedAt: new Date().toISOString(),
34
- pageUrl: location.href.split('?')[0].split('#')[0],
35
- dispatchId: dispatchId,
36
- thread: result.payload,
37
- }
38
-
39
- let reply
40
- try {
41
- reply = await chrome.runtime.sendMessage({ type: 'linkedin-ingest', envelope: envelope })
42
- } catch (err) {
43
- console.error(TAG_ERROR + ' reason=worker-unreachable detail=' + (err && err.message ? err.message : String(err)))
44
- setState(pill, 'error', 'Capture failed')
45
- return
46
- }
47
-
48
- if (!reply || !reply.ok) {
49
- // `worker-unreachable` is the extension's OWN service worker failing to
50
- // answer. It is not `unreachable`, which means the admin host did not
51
- // answer — blaming SiteDesk here sends the operator to check a tunnel that
52
- // was never involved.
53
- const reason = (reply && reply.reason) || 'worker-unreachable'
54
- const detail = (reply && reply.detail) || ''
55
- console.error(TAG_ERROR + ' reason=' + reason + ' detail=' + detail)
56
- // Task 2208 — the ingest token is missing or was refused. There is no
57
- // sign-in to perform: the operator asks the SiteDesk agent for the token and
58
- // pastes it on the options page.
59
- if (reason === 'auth') {
60
- setState(pill, 'auth', 'Paste SiteDesk token')
61
- pill.onclick = function () { chrome.runtime.openOptionsPage() }
62
- return
63
- }
64
- // Task 2185 — the admin host was never granted, so the POST never left
65
- // the browser. Sending the operator to the options page is the fix.
66
- if (reason === 'permission') {
67
- setState(pill, 'auth', 'Grant access')
68
- pill.onclick = function () { chrome.runtime.openOptionsPage() }
69
- return
70
- }
71
- if (reason === 'unreachable') {
72
- setState(pill, 'error', 'SiteDesk unreachable')
73
- return
74
- }
75
- setState(pill, 'error', 'Capture failed')
76
- return
77
- }
78
-
79
- setState(pill, 'ok', 'Captured ✓')
80
- }
81
-
82
- function mount() {
83
- if (document.getElementById(PILL_ID)) return
84
- const anchor = document.querySelector('.msg-thread, header.msg-overlay-bubble-header, main') || document.body
85
- if (!anchor) return
86
- const pill = document.createElement('button')
87
- pill.id = PILL_ID
88
- pill.className = 'maxy-pill'
89
- pill.type = 'button'
90
- pill.textContent = 'Add to SiteDesk'
91
- pill.dataset.state = 'idle'
92
- pill.addEventListener('click', function () { onClick(pill) })
93
- anchor.prepend(pill)
94
- console.log(TAG_MOUNT + ' page=thread url=' + location.href)
95
- }
96
-
97
- if (document.readyState === 'loading') {
98
- document.addEventListener('DOMContentLoaded', mount, { once: true })
99
- } else {
100
- mount()
101
- }
102
- })()