@rubytech/create-maxy-code 0.1.533 → 0.1.535
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.
- package/package.json +1 -1
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +3 -1
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +13 -0
- package/payload/platform/plugins/docs/references/admin-ui.md +2 -0
- package/payload/platform/plugins/email/mcp/dist/__tests__/signature.test.js +40 -1
- package/payload/platform/plugins/email/mcp/dist/__tests__/signature.test.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/compose.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/compose.js +4 -1
- package/payload/platform/plugins/email/mcp/dist/lib/compose.js.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/signature.d.ts +27 -0
- package/payload/platform/plugins/email/mcp/dist/lib/signature.d.ts.map +1 -1
- package/payload/platform/plugins/email/mcp/dist/lib/signature.js +62 -2
- package/payload/platform/plugins/email/mcp/dist/lib/signature.js.map +1 -1
- package/payload/platform/plugins/memory/skills/preference-audit/SKILL.md +11 -1
- package/payload/platform/plugins/whatsapp/PLUGIN.md +10 -2
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js +41 -3
- package/payload/platform/plugins/whatsapp/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/whatsapp/skills/manage-whatsapp-config/SKILL.md +17 -1
- package/payload/platform/scripts/__tests__/vnc-cdp-liveness.test.sh +14 -12
- package/payload/platform/scripts/vnc.sh +24 -19
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +1 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +35 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/index.js +13 -4
- package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.d.ts +1 -1
- package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.js +46 -3
- package/payload/platform/services/claude-session-manager/dist/preference-reconciliation-audit.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +9 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +7 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/specialist-roster.d.ts +12 -0
- package/payload/platform/services/claude-session-manager/dist/specialist-roster.d.ts.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/specialist-roster.js +75 -0
- package/payload/platform/services/claude-session-manager/dist/specialist-roster.js.map +1 -0
- package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts +5 -0
- package/payload/platform/services/claude-session-manager/dist/system-prompt.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/system-prompt.js +10 -2
- package/payload/platform/services/claude-session-manager/dist/system-prompt.js.map +1 -1
- package/payload/server/{chunk-AJC3SQSF.js → chunk-4QCYFTAI.js} +198 -8
- package/payload/server/{manager-PLRHVALC.js → manager-ZNHQQCED.js} +1 -1
- package/payload/server/public/activity.html +5 -5
- package/payload/server/public/agents.html +4 -4
- package/payload/server/public/assets/AdminLoginScreens-DxjuTFjL.js +1 -0
- package/payload/server/public/assets/AdminLoginScreens-DxjuTFjL.js.br +0 -0
- package/payload/server/public/assets/AdminLoginScreens-DxjuTFjL.js.gz +0 -0
- package/payload/server/public/assets/AdminShell-B6H3J5C2.js +2 -0
- package/payload/server/public/assets/AdminShell-B6H3J5C2.js.br +0 -0
- package/payload/server/public/assets/AdminShell-B6H3J5C2.js.gz +0 -0
- package/payload/server/public/assets/CdpScreencastSurface-BRSv89v-.js +1 -0
- package/payload/server/public/assets/CdpScreencastSurface-BRSv89v-.js.br +0 -0
- package/payload/server/public/assets/CdpScreencastSurface-BRSv89v-.js.gz +0 -0
- package/payload/server/public/assets/{activity-0IcMRF5a.js → activity-DKeRUjQ9.js} +1 -1
- package/payload/server/public/assets/activity-DKeRUjQ9.js.br +0 -0
- package/payload/server/public/assets/activity-DKeRUjQ9.js.gz +0 -0
- package/payload/server/public/assets/admin-BxRWjeyZ.js +1 -0
- package/payload/server/public/assets/admin-BxRWjeyZ.js.br +0 -0
- package/payload/server/public/assets/admin-BxRWjeyZ.js.gz +0 -0
- package/payload/server/public/assets/{agents-xjw-fJjo.js → agents-CrR_OMe8.js} +1 -1
- package/payload/server/public/assets/agents-CrR_OMe8.js.br +0 -0
- package/payload/server/public/assets/agents-CrR_OMe8.js.gz +0 -0
- package/payload/server/public/assets/{browser-ByWKTnUQ.js → browser-sfwnP1kH.js} +1 -1
- package/payload/server/public/assets/browser-sfwnP1kH.js.br +0 -0
- package/payload/server/public/assets/browser-sfwnP1kH.js.gz +0 -0
- package/payload/server/public/assets/{calendar-DX2tE1B2.js → calendar-ByLH0Q5p.js} +1 -1
- package/payload/server/public/assets/calendar-ByLH0Q5p.js.br +0 -0
- package/payload/server/public/assets/calendar-ByLH0Q5p.js.gz +0 -0
- package/payload/server/public/assets/chat-C3xYZlGo.js +1 -0
- package/payload/server/public/assets/chat-C3xYZlGo.js.br +2 -0
- package/payload/server/public/assets/chat-C3xYZlGo.js.gz +0 -0
- package/payload/server/public/assets/chevron-left-C0HG8MBj.js +1 -0
- package/payload/server/public/assets/chevron-right-B49Xpptm.js +1 -0
- package/payload/server/public/assets/chevron-right-B49Xpptm.js.br +0 -0
- package/payload/server/public/assets/clock-CJq9vvt8.js +1 -0
- package/payload/server/public/assets/clock-CJq9vvt8.js.br +0 -0
- package/payload/server/public/assets/clock-CJq9vvt8.js.gz +0 -0
- package/payload/server/public/assets/data-DmeTKd-P.js +1 -0
- package/payload/server/public/assets/data-DmeTKd-P.js.br +0 -0
- package/payload/server/public/assets/data-DmeTKd-P.js.gz +0 -0
- package/payload/server/public/assets/{file-download-ChX5qAB2.js → file-download-BWe9Muoh.js} +1 -1
- package/payload/server/public/assets/file-download-BWe9Muoh.js.br +0 -0
- package/payload/server/public/assets/file-download-BWe9Muoh.js.gz +0 -0
- package/payload/server/public/assets/{file-text-DziWy8hF.js → file-text-5osg-EVb.js} +1 -1
- package/payload/server/public/assets/file-text-5osg-EVb.js.br +0 -0
- package/payload/server/public/assets/file-text-5osg-EVb.js.gz +0 -0
- package/payload/server/public/assets/{graph-Ci8RmSp3.js → graph-CAUo1OTY.js} +2 -2
- package/payload/server/public/assets/graph-CAUo1OTY.js.br +0 -0
- package/payload/server/public/assets/graph-CAUo1OTY.js.gz +0 -0
- package/payload/server/public/assets/{graph-labels-JkRVzaVL.js → graph-labels-TJ-3ljQo.js} +1 -1
- package/payload/server/public/assets/graph-labels-TJ-3ljQo.js.br +0 -0
- package/payload/server/public/assets/graph-labels-TJ-3ljQo.js.gz +0 -0
- package/payload/server/public/assets/{maximize-2-DnINkTp0.js → maximize-2-S9W3q1O4.js} +1 -1
- package/payload/server/public/assets/maximize-2-S9W3q1O4.js.br +0 -0
- package/payload/server/public/assets/maximize-2-S9W3q1O4.js.gz +0 -0
- package/payload/server/public/assets/operator-CzOJfank.js +1 -0
- package/payload/server/public/assets/operator-CzOJfank.js.br +0 -0
- package/payload/server/public/assets/operator-CzOJfank.js.gz +0 -0
- package/payload/server/public/assets/page-BUFW-PWF.js +32 -0
- package/payload/server/public/assets/page-BUFW-PWF.js.br +0 -0
- package/payload/server/public/assets/page-BUFW-PWF.js.gz +0 -0
- package/payload/server/public/assets/page-jT4queFM.js +1 -0
- package/payload/server/public/assets/page-jT4queFM.js.br +0 -0
- package/payload/server/public/assets/page-jT4queFM.js.gz +0 -0
- package/payload/server/public/assets/public-Cx2ddU9a.js +1 -0
- package/payload/server/public/assets/public-Cx2ddU9a.js.br +0 -0
- package/payload/server/public/assets/public-Cx2ddU9a.js.gz +0 -0
- package/payload/server/public/assets/{rotate-ccw-CfaIgRAI.js → rotate-ccw-C2qSSBFA.js} +1 -1
- package/payload/server/public/assets/rotate-ccw-C2qSSBFA.js.br +0 -0
- package/payload/server/public/assets/rotate-ccw-C2qSSBFA.js.gz +0 -0
- package/payload/server/public/assets/{routines-BIlcD8sn.js → routines-80uEwjng.js} +1 -1
- package/payload/server/public/assets/routines-80uEwjng.js.br +0 -0
- package/payload/server/public/assets/routines-80uEwjng.js.gz +0 -0
- package/payload/server/public/assets/{skills-DGID2WqF.js → skills-CmsYXVd3.js} +1 -1
- package/payload/server/public/assets/skills-CmsYXVd3.js.br +0 -0
- package/payload/server/public/assets/skills-CmsYXVd3.js.gz +0 -0
- package/payload/server/public/assets/tasks-CFfvn-KE.js +3 -0
- package/payload/server/public/assets/tasks-CFfvn-KE.js.br +0 -0
- package/payload/server/public/assets/tasks-CFfvn-KE.js.gz +0 -0
- package/payload/server/public/assets/{time-entry-format-DNl35lg9.js → time-entry-format-BD99oR05.js} +1 -1
- package/payload/server/public/assets/time-entry-format-BD99oR05.js.br +0 -0
- package/payload/server/public/assets/time-entry-format-BD99oR05.js.gz +0 -0
- package/payload/server/public/assets/{triangle-alert-DI_VASJO.js → triangle-alert-CuRAK3jk.js} +1 -1
- package/payload/server/public/assets/triangle-alert-CuRAK3jk.js.br +0 -0
- package/payload/server/public/assets/triangle-alert-CuRAK3jk.js.gz +0 -0
- package/payload/server/public/assets/{useCopyFeedback-Bu0ZQZQM.js → useCopyFeedback-BGzzIsSD.js} +1 -1
- package/payload/server/public/assets/useCopyFeedback-BGzzIsSD.js.br +0 -0
- package/payload/server/public/assets/useCopyFeedback-BGzzIsSD.js.gz +0 -0
- package/payload/server/public/assets/{useSubAccountSwitcher-CZBZ3iV8.css → useSubAccountSwitcher-BDQ8Wsdt.css} +1 -1
- package/payload/server/public/assets/useSubAccountSwitcher-BDQ8Wsdt.css.br +0 -0
- package/payload/server/public/assets/{useSubAccountSwitcher-CZBZ3iV8.css.gz → useSubAccountSwitcher-BDQ8Wsdt.css.gz} +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-D1VtIEp2.js +14 -0
- package/payload/server/public/assets/useSubAccountSwitcher-D1VtIEp2.js.br +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-D1VtIEp2.js.gz +0 -0
- package/payload/server/public/assets/useVoiceRecorder-fpwWw2VW.js +2 -0
- package/payload/server/public/assets/useVoiceRecorder-fpwWw2VW.js.br +0 -0
- package/payload/server/public/assets/useVoiceRecorder-fpwWw2VW.js.gz +0 -0
- package/payload/server/public/assets/{wrench-p4gW_ImW.js → wrench-DxSsCrue.js} +1 -1
- package/payload/server/public/assets/wrench-DxSsCrue.js.br +2 -0
- package/payload/server/public/assets/wrench-DxSsCrue.js.gz +0 -0
- package/payload/server/public/browser.html +5 -5
- package/payload/server/public/calendar.html +7 -7
- package/payload/server/public/chat.html +13 -13
- package/payload/server/public/data.html +12 -12
- package/payload/server/public/graph.html +9 -9
- package/payload/server/public/index.html +16 -16
- package/payload/server/public/operator.html +15 -15
- package/payload/server/public/public.html +13 -13
- package/payload/server/public/routines.html +7 -7
- package/payload/server/public/skills.html +5 -5
- package/payload/server/public/tasks.html +6 -6
- package/payload/server/server.js +460 -28
- package/payload/server/public/assets/AdminLoginScreens-CGRty7xT.js +0 -1
- package/payload/server/public/assets/AdminLoginScreens-CGRty7xT.js.br +0 -0
- package/payload/server/public/assets/AdminLoginScreens-CGRty7xT.js.gz +0 -0
- package/payload/server/public/assets/AdminShell-BQy9Ob22.js +0 -2
- package/payload/server/public/assets/AdminShell-BQy9Ob22.js.br +0 -0
- package/payload/server/public/assets/AdminShell-BQy9Ob22.js.gz +0 -0
- package/payload/server/public/assets/CdpScreencastSurface-BrDZji8F.js +0 -1
- package/payload/server/public/assets/CdpScreencastSurface-BrDZji8F.js.br +0 -0
- package/payload/server/public/assets/CdpScreencastSurface-BrDZji8F.js.gz +0 -0
- package/payload/server/public/assets/activity-0IcMRF5a.js.br +0 -0
- package/payload/server/public/assets/activity-0IcMRF5a.js.gz +0 -0
- package/payload/server/public/assets/admin-CKy-bmrs.js +0 -1
- package/payload/server/public/assets/admin-CKy-bmrs.js.br +0 -0
- package/payload/server/public/assets/admin-CKy-bmrs.js.gz +0 -0
- package/payload/server/public/assets/agents-xjw-fJjo.js.br +0 -0
- package/payload/server/public/assets/agents-xjw-fJjo.js.gz +0 -0
- package/payload/server/public/assets/browser-ByWKTnUQ.js.br +0 -0
- package/payload/server/public/assets/browser-ByWKTnUQ.js.gz +0 -0
- package/payload/server/public/assets/calendar-DX2tE1B2.js.br +0 -0
- package/payload/server/public/assets/calendar-DX2tE1B2.js.gz +0 -0
- package/payload/server/public/assets/chat-DkNDthRg.js +0 -1
- package/payload/server/public/assets/chat-DkNDthRg.js.br +0 -0
- package/payload/server/public/assets/chat-DkNDthRg.js.gz +0 -0
- package/payload/server/public/assets/chevron-left-CPwk14nY.js +0 -1
- package/payload/server/public/assets/chevron-right-Bz6INVTF.js +0 -1
- package/payload/server/public/assets/chevron-right-Bz6INVTF.js.br +0 -0
- package/payload/server/public/assets/clock-3YLNejcB.js +0 -1
- package/payload/server/public/assets/clock-3YLNejcB.js.br +0 -0
- package/payload/server/public/assets/clock-3YLNejcB.js.gz +0 -0
- package/payload/server/public/assets/data-DiDeaOUN.js +0 -1
- package/payload/server/public/assets/data-DiDeaOUN.js.br +0 -0
- package/payload/server/public/assets/data-DiDeaOUN.js.gz +0 -0
- package/payload/server/public/assets/file-download-ChX5qAB2.js.br +0 -0
- package/payload/server/public/assets/file-download-ChX5qAB2.js.gz +0 -0
- package/payload/server/public/assets/file-text-DziWy8hF.js.br +0 -0
- package/payload/server/public/assets/file-text-DziWy8hF.js.gz +0 -0
- package/payload/server/public/assets/graph-Ci8RmSp3.js.br +0 -0
- package/payload/server/public/assets/graph-Ci8RmSp3.js.gz +0 -0
- package/payload/server/public/assets/graph-labels-JkRVzaVL.js.br +0 -0
- package/payload/server/public/assets/graph-labels-JkRVzaVL.js.gz +0 -0
- package/payload/server/public/assets/maximize-2-DnINkTp0.js.br +0 -0
- package/payload/server/public/assets/maximize-2-DnINkTp0.js.gz +0 -0
- package/payload/server/public/assets/operator-CFOJBXCH.js +0 -1
- package/payload/server/public/assets/operator-CFOJBXCH.js.br +0 -0
- package/payload/server/public/assets/operator-CFOJBXCH.js.gz +0 -0
- package/payload/server/public/assets/page-BGv8bLm5.js +0 -32
- package/payload/server/public/assets/page-BGv8bLm5.js.br +0 -0
- package/payload/server/public/assets/page-BGv8bLm5.js.gz +0 -0
- package/payload/server/public/assets/page-DXTCBZRa.js +0 -1
- package/payload/server/public/assets/page-DXTCBZRa.js.br +0 -0
- package/payload/server/public/assets/page-DXTCBZRa.js.gz +0 -0
- package/payload/server/public/assets/public-yBY2wTJN.js +0 -1
- package/payload/server/public/assets/public-yBY2wTJN.js.br +0 -0
- package/payload/server/public/assets/public-yBY2wTJN.js.gz +0 -0
- package/payload/server/public/assets/rotate-ccw-CfaIgRAI.js.br +0 -0
- package/payload/server/public/assets/rotate-ccw-CfaIgRAI.js.gz +0 -0
- package/payload/server/public/assets/routines-BIlcD8sn.js.br +0 -0
- package/payload/server/public/assets/routines-BIlcD8sn.js.gz +0 -0
- package/payload/server/public/assets/skills-DGID2WqF.js.br +0 -0
- package/payload/server/public/assets/skills-DGID2WqF.js.gz +0 -0
- package/payload/server/public/assets/tasks-BukjwR7A.js +0 -3
- package/payload/server/public/assets/tasks-BukjwR7A.js.br +0 -0
- package/payload/server/public/assets/tasks-BukjwR7A.js.gz +0 -0
- package/payload/server/public/assets/time-entry-format-DNl35lg9.js.br +0 -0
- package/payload/server/public/assets/time-entry-format-DNl35lg9.js.gz +0 -0
- package/payload/server/public/assets/triangle-alert-DI_VASJO.js.br +0 -0
- package/payload/server/public/assets/triangle-alert-DI_VASJO.js.gz +0 -0
- package/payload/server/public/assets/useCopyFeedback-Bu0ZQZQM.js.br +0 -0
- package/payload/server/public/assets/useCopyFeedback-Bu0ZQZQM.js.gz +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-B6Ns2Z43.js +0 -14
- package/payload/server/public/assets/useSubAccountSwitcher-B6Ns2Z43.js.br +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-B6Ns2Z43.js.gz +0 -0
- package/payload/server/public/assets/useSubAccountSwitcher-CZBZ3iV8.css.br +0 -0
- package/payload/server/public/assets/useVoiceRecorder-CbVwXEjY.js +0 -2
- package/payload/server/public/assets/useVoiceRecorder-CbVwXEjY.js.br +0 -0
- package/payload/server/public/assets/useVoiceRecorder-CbVwXEjY.js.gz +0 -0
- package/payload/server/public/assets/wrench-p4gW_ImW.js.br +0 -0
- package/payload/server/public/assets/wrench-p4gW_ImW.js.gz +0 -0
package/payload/server/server.js
CHANGED
|
@@ -67,6 +67,7 @@ import {
|
|
|
67
67
|
MAX_DATA_UPLOAD_BYTES,
|
|
68
68
|
MAX_FILES_PER_MESSAGE,
|
|
69
69
|
MAX_FILE_SIZE_BYTES,
|
|
70
|
+
PARTY_LABEL_PATTERN,
|
|
70
71
|
STORE_ROOT,
|
|
71
72
|
SUPPORTED_MIME_TYPES,
|
|
72
73
|
WHATSAPP_DOCUMENT_MAX_BYTES,
|
|
@@ -156,6 +157,7 @@ import {
|
|
|
156
157
|
recordAccessFailedAttempt,
|
|
157
158
|
recordRequestLinkAttempt,
|
|
158
159
|
registerLoginSocket,
|
|
160
|
+
registeredPartyFor,
|
|
159
161
|
reloadConfig,
|
|
160
162
|
resolveAccountConfig,
|
|
161
163
|
resolveClaudeUploadsPath,
|
|
@@ -190,7 +192,7 @@ import {
|
|
|
190
192
|
waitForConnection,
|
|
191
193
|
warnNonUuidAccount,
|
|
192
194
|
writeChatMetadataMerged
|
|
193
|
-
} from "./chunk-
|
|
195
|
+
} from "./chunk-4QCYFTAI.js";
|
|
194
196
|
import {
|
|
195
197
|
ACCOUNTS_DIR,
|
|
196
198
|
BIN_DIR,
|
|
@@ -5019,7 +5021,7 @@ async function defaultHttpGet(url) {
|
|
|
5019
5021
|
}
|
|
5020
5022
|
}
|
|
5021
5023
|
async function defaultResolve(accountId) {
|
|
5022
|
-
const { resolveSocket: resolveSocket2 } = await import("./manager-
|
|
5024
|
+
const { resolveSocket: resolveSocket2 } = await import("./manager-ZNHQQCED.js");
|
|
5023
5025
|
return resolveSocket2(accountId);
|
|
5024
5026
|
}
|
|
5025
5027
|
async function fetchAvatar(accountId, jid, deps = {}) {
|
|
@@ -5404,6 +5406,10 @@ function addAdminPhone(accountDir, phone) {
|
|
|
5404
5406
|
error: `${targetId} does not own the WhatsApp paired socket, so its admin phones are never consulted. To route a phone to a sub-account, bind it as an account manager (add-account-manager) instead.`
|
|
5405
5407
|
};
|
|
5406
5408
|
}
|
|
5409
|
+
if (registeredPartyFor(readRegisteredPartiesFrom(config), normalized)) {
|
|
5410
|
+
console.error(`${TAG10} add-admin-phone rejected phone=${normalized} reason=party-registration-shadowed`);
|
|
5411
|
+
return { ok: false, reason: "party-registration-shadowed", error: partyShadowMessage(normalized, "registeredParties") };
|
|
5412
|
+
}
|
|
5407
5413
|
if (!config.whatsapp || typeof config.whatsapp !== "object") {
|
|
5408
5414
|
config.whatsapp = {};
|
|
5409
5415
|
}
|
|
@@ -5605,6 +5611,10 @@ function setAccountManager(accountDir, phone, subAccountId, mode = "active") {
|
|
|
5605
5611
|
error: `The account-manager routing table is read from ${houseId}'s account.json only, so a binding stored on ${targetId} is never consulted. Bind ${normalized} on ${houseId} instead.`
|
|
5606
5612
|
};
|
|
5607
5613
|
}
|
|
5614
|
+
if (registeredPartyFor(readRegisteredPartiesFrom(config), normalized)) {
|
|
5615
|
+
console.error(`${TAG10} set-account-manager rejected phone=${normalized} reason=party-registration-shadowed`);
|
|
5616
|
+
return { ok: false, reason: "party-registration-shadowed", error: partyShadowMessage(normalized, "registeredParties") };
|
|
5617
|
+
}
|
|
5608
5618
|
if (!config.whatsapp || typeof config.whatsapp !== "object") {
|
|
5609
5619
|
config.whatsapp = {};
|
|
5610
5620
|
}
|
|
@@ -5633,6 +5643,200 @@ function setAccountManager(accountDir, phone, subAccountId, mode = "active") {
|
|
|
5633
5643
|
return { ok: false, error: msg };
|
|
5634
5644
|
}
|
|
5635
5645
|
}
|
|
5646
|
+
function readRegisteredPartiesFrom(config) {
|
|
5647
|
+
const wa = config.whatsapp;
|
|
5648
|
+
const map = wa?.registeredParties;
|
|
5649
|
+
if (!map || typeof map !== "object" || Array.isArray(map)) return {};
|
|
5650
|
+
const out = {};
|
|
5651
|
+
for (const [k, v] of Object.entries(map)) {
|
|
5652
|
+
if (v && typeof v === "object" && typeof v.account === "string" && typeof v.party === "string") {
|
|
5653
|
+
out[k] = { account: v.account, party: v.party };
|
|
5654
|
+
}
|
|
5655
|
+
}
|
|
5656
|
+
return out;
|
|
5657
|
+
}
|
|
5658
|
+
function partyShadowedBy(wa, phone) {
|
|
5659
|
+
const adminPhones = Array.isArray(wa.adminPhones) ? wa.adminPhones.filter((p) => typeof p === "string") : [];
|
|
5660
|
+
if (isAdminPhone(phone, adminPhones)) return "adminPhones";
|
|
5661
|
+
const managers = wa.accountManagers && typeof wa.accountManagers === "object" && !Array.isArray(wa.accountManagers) ? wa.accountManagers : {};
|
|
5662
|
+
if (managedAccountFor(managers, phone)) return "accountManagers";
|
|
5663
|
+
return null;
|
|
5664
|
+
}
|
|
5665
|
+
function partyShadowMessage(phone, shadowedBy) {
|
|
5666
|
+
if (shadowedBy === "registeredParties") {
|
|
5667
|
+
return `${phone} is already a registered party. Arms 1-4 of the inbound gate all precede the registered-party arm, so adding this registration would silently disable the party binding rather than layering on it. Remove the party registration first (remove-registered-party) if that is what you intend.`;
|
|
5668
|
+
}
|
|
5669
|
+
const surface = shadowedBy === "adminPhones" ? "an admin phone" : "an account manager";
|
|
5670
|
+
const remedy = shadowedBy === "adminPhones" ? "remove-admin-phone" : "remove-account-manager";
|
|
5671
|
+
return `${phone} is already ${surface}. Arms 1-4 of the inbound gate all precede the registered-party arm, so this registration would never be reached. Remove the existing registration first (${remedy}) if you want this phone treated as a counterparty rather than an operator.`;
|
|
5672
|
+
}
|
|
5673
|
+
function setRegisteredParty(accountDir, phone, account, party) {
|
|
5674
|
+
const normalized = phone.trim();
|
|
5675
|
+
if (!E164_PATTERN.test(normalized)) {
|
|
5676
|
+
return { ok: false, error: `Invalid phone format "${normalized}". Expected E.164 (e.g. +441234567890).` };
|
|
5677
|
+
}
|
|
5678
|
+
const label = party.trim();
|
|
5679
|
+
if (!PARTY_LABEL_PATTERN.test(label)) {
|
|
5680
|
+
return {
|
|
5681
|
+
ok: false,
|
|
5682
|
+
reason: "invalid-party-label",
|
|
5683
|
+
error: `Invalid party label "${label}". Use 1-64 characters of lowercase letters, digits and hyphens (e.g. "driver").`
|
|
5684
|
+
};
|
|
5685
|
+
}
|
|
5686
|
+
const acct = account.trim();
|
|
5687
|
+
if (!acct) return { ok: false, error: "Missing account id." };
|
|
5688
|
+
let validIds;
|
|
5689
|
+
try {
|
|
5690
|
+
validIds = listValidAccounts().map((a) => a.accountId);
|
|
5691
|
+
} catch (err) {
|
|
5692
|
+
return { ok: false, error: `Could not read the account registry to validate the account: ${err instanceof Error ? err.message : String(err)}. Try again.` };
|
|
5693
|
+
}
|
|
5694
|
+
if (!validIds.includes(acct)) {
|
|
5695
|
+
return { ok: false, error: `Account "${acct}" is not in this install's account registry. Check the id and try again.` };
|
|
5696
|
+
}
|
|
5697
|
+
let houseId;
|
|
5698
|
+
try {
|
|
5699
|
+
houseId = resolveHouseOrSoleAccountId(dirname2(resolve5(accountDir)));
|
|
5700
|
+
} catch (err) {
|
|
5701
|
+
return { ok: false, error: `Could not resolve the house account to validate the registration: ${err instanceof Error ? err.message : String(err)}. Try again.` };
|
|
5702
|
+
}
|
|
5703
|
+
if (acct === houseId) {
|
|
5704
|
+
console.error(`${TAG10} set-registered-party rejected phone=${normalized} reason=house-account-binding`);
|
|
5705
|
+
return {
|
|
5706
|
+
ok: false,
|
|
5707
|
+
reason: "house-account-binding",
|
|
5708
|
+
error: `${acct} is the house account. A registered party is admitted only on the account's OWN number, and on the house number they take the house DM policy instead, so a house registration would never be consulted.`
|
|
5709
|
+
};
|
|
5710
|
+
}
|
|
5711
|
+
try {
|
|
5712
|
+
const config = readConfig(accountDir);
|
|
5713
|
+
const targetId = typeof config.accountId === "string" && config.accountId ? config.accountId : basename(resolve5(accountDir));
|
|
5714
|
+
if (targetId !== houseId) {
|
|
5715
|
+
console.error(`${TAG10} set-registered-party rejected phone=${normalized} reason=not-socket-account`);
|
|
5716
|
+
return {
|
|
5717
|
+
ok: false,
|
|
5718
|
+
reason: "not-socket-account",
|
|
5719
|
+
error: `The registered-party table is read from ${houseId}'s account.json only, so a registration stored on ${targetId} is never consulted. Register ${normalized} on ${houseId} instead.`
|
|
5720
|
+
};
|
|
5721
|
+
}
|
|
5722
|
+
if (!config.whatsapp || typeof config.whatsapp !== "object") {
|
|
5723
|
+
config.whatsapp = {};
|
|
5724
|
+
}
|
|
5725
|
+
const wa = config.whatsapp;
|
|
5726
|
+
const shadow = partyShadowedBy(wa, normalized);
|
|
5727
|
+
if (shadow) {
|
|
5728
|
+
console.error(`${TAG10} set-registered-party rejected phone=${normalized} reason=party-registration-shadowed shadowedBy=${shadow}`);
|
|
5729
|
+
return { ok: false, reason: "party-registration-shadowed", error: partyShadowMessage(normalized, shadow) };
|
|
5730
|
+
}
|
|
5731
|
+
if (!wa.registeredParties || typeof wa.registeredParties !== "object") {
|
|
5732
|
+
wa.registeredParties = {};
|
|
5733
|
+
}
|
|
5734
|
+
wa.registeredParties[normalized] = { account: acct, party: label };
|
|
5735
|
+
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
5736
|
+
if (!parsed.success) {
|
|
5737
|
+
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
5738
|
+
return { ok: false, error: `Validation failed: ${msg}` };
|
|
5739
|
+
}
|
|
5740
|
+
config.whatsapp = parsed.data;
|
|
5741
|
+
writeConfig(accountDir, config);
|
|
5742
|
+
console.error(`${TAG10} registered party phone=${normalized} account=${acct} party=${label}`);
|
|
5743
|
+
reloadManagerConfig(accountDir);
|
|
5744
|
+
return {
|
|
5745
|
+
ok: true,
|
|
5746
|
+
message: `Registered ${normalized} as "${label}" for account ${acct}. Their WhatsApp messages to that account's own number reach its public agent carrying the label; they are not granted admin, and on the house number they take the house DM policy.`
|
|
5747
|
+
};
|
|
5748
|
+
} catch (err) {
|
|
5749
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5750
|
+
console.error(`${TAG10} setRegisteredParty failed: ${msg}`);
|
|
5751
|
+
return { ok: false, error: msg };
|
|
5752
|
+
}
|
|
5753
|
+
}
|
|
5754
|
+
function clearRegisteredParty(accountDir, phone) {
|
|
5755
|
+
const normalized = phone.trim();
|
|
5756
|
+
try {
|
|
5757
|
+
const config = readConfig(accountDir);
|
|
5758
|
+
const wa = config.whatsapp;
|
|
5759
|
+
if (!wa || typeof wa !== "object" || !wa.registeredParties || typeof wa.registeredParties !== "object") {
|
|
5760
|
+
return { ok: true, message: `No registered-party registration for ${normalized}.` };
|
|
5761
|
+
}
|
|
5762
|
+
const map = wa.registeredParties;
|
|
5763
|
+
const key = Object.keys(map).find((k) => phonesMatch(k, normalized));
|
|
5764
|
+
if (!key) {
|
|
5765
|
+
return { ok: true, message: `No registered-party registration for ${normalized}.` };
|
|
5766
|
+
}
|
|
5767
|
+
delete map[key];
|
|
5768
|
+
const parsed = WhatsAppConfigSchema.safeParse(wa);
|
|
5769
|
+
if (!parsed.success) {
|
|
5770
|
+
const msg = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join("; ");
|
|
5771
|
+
return { ok: false, error: `Validation failed: ${msg}` };
|
|
5772
|
+
}
|
|
5773
|
+
config.whatsapp = parsed.data;
|
|
5774
|
+
writeConfig(accountDir, config);
|
|
5775
|
+
console.error(`${TAG10} cleared registered party phone=${normalized}`);
|
|
5776
|
+
reloadManagerConfig(accountDir);
|
|
5777
|
+
return { ok: true, message: `Cleared the registered-party registration for ${normalized}.` };
|
|
5778
|
+
} catch (err) {
|
|
5779
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
5780
|
+
console.error(`${TAG10} clearRegisteredParty failed: ${msg}`);
|
|
5781
|
+
return { ok: false, error: msg };
|
|
5782
|
+
}
|
|
5783
|
+
}
|
|
5784
|
+
function readRegisteredParties(accountDir) {
|
|
5785
|
+
try {
|
|
5786
|
+
return readRegisteredPartiesFrom(readConfig(accountDir));
|
|
5787
|
+
} catch {
|
|
5788
|
+
return {};
|
|
5789
|
+
}
|
|
5790
|
+
}
|
|
5791
|
+
function purgeNonSocketRegisteredParties(accountDir, socketOwnerId) {
|
|
5792
|
+
try {
|
|
5793
|
+
const config = readConfig(accountDir);
|
|
5794
|
+
const wa = config.whatsapp;
|
|
5795
|
+
if (!wa || typeof wa !== "object") return { removed: [] };
|
|
5796
|
+
const targetId = typeof config.accountId === "string" && config.accountId ? config.accountId : basename(resolve5(accountDir));
|
|
5797
|
+
if (targetId === socketOwnerId) return { removed: [] };
|
|
5798
|
+
const map = wa.registeredParties;
|
|
5799
|
+
if (!map || typeof map !== "object" || Array.isArray(map)) return { removed: [] };
|
|
5800
|
+
const removed = Object.entries(map).map(([phone, entry]) => ({
|
|
5801
|
+
phone,
|
|
5802
|
+
account: entry && typeof entry === "object" && typeof entry.account === "string" ? entry.account : "",
|
|
5803
|
+
party: entry && typeof entry === "object" && typeof entry.party === "string" ? entry.party : ""
|
|
5804
|
+
}));
|
|
5805
|
+
if (removed.length === 0) return { removed: [] };
|
|
5806
|
+
delete wa.registeredParties;
|
|
5807
|
+
writeConfig(accountDir, config);
|
|
5808
|
+
for (const { phone, account, party } of removed) {
|
|
5809
|
+
console.error(`[admin-identity] op=purge-nonsocket-registeredparty accountId=${targetId} phone=${phone} account=${account} party=${party} removed=true`);
|
|
5810
|
+
}
|
|
5811
|
+
return { removed };
|
|
5812
|
+
} catch (err) {
|
|
5813
|
+
console.error(`[admin-identity] purge-nonsocket-registeredparty failed dir=${accountDir}: ${String(err)}`);
|
|
5814
|
+
return { removed: [] };
|
|
5815
|
+
}
|
|
5816
|
+
}
|
|
5817
|
+
function purgeNonSocketRegisteredPartiesAtBoot() {
|
|
5818
|
+
let socketOwner;
|
|
5819
|
+
try {
|
|
5820
|
+
socketOwner = resolveHouseOrSoleAccountId(ACCOUNTS_DIR);
|
|
5821
|
+
} catch (err) {
|
|
5822
|
+
console.error(`[admin-identity] purge-nonsocket-registeredparty skipped \u2014 cannot resolve socket owner: ${String(err)}`);
|
|
5823
|
+
return;
|
|
5824
|
+
}
|
|
5825
|
+
const configOwner = resolveAccount()?.accountId ?? null;
|
|
5826
|
+
if (configOwner !== socketOwner) {
|
|
5827
|
+
console.error(`[admin-identity] purge-nonsocket-registeredparty skipped \u2014 resolver disagreement house=${socketOwner} configLoadedFrom=${configOwner ?? "unresolved"}`);
|
|
5828
|
+
return;
|
|
5829
|
+
}
|
|
5830
|
+
let accounts;
|
|
5831
|
+
try {
|
|
5832
|
+
accounts = listValidAccounts();
|
|
5833
|
+
} catch {
|
|
5834
|
+
return;
|
|
5835
|
+
}
|
|
5836
|
+
for (const acct of accounts) {
|
|
5837
|
+
purgeNonSocketRegisteredParties(acct.accountDir, socketOwner);
|
|
5838
|
+
}
|
|
5839
|
+
}
|
|
5636
5840
|
function setDispatchInbound(accountDir, accountId, value, ctx) {
|
|
5637
5841
|
const id = accountId.trim();
|
|
5638
5842
|
if (!id) return { ok: false, reason: "missing-account-id", error: "Missing account id." };
|
|
@@ -6299,6 +6503,60 @@ function reconcileConnectionAccountStamps(input) {
|
|
|
6299
6503
|
return { ok: mismatches.length === 0, mismatches };
|
|
6300
6504
|
}
|
|
6301
6505
|
|
|
6506
|
+
// app/lib/whatsapp/person-identity-reconcile.ts
|
|
6507
|
+
function bare(value) {
|
|
6508
|
+
return value.startsWith("+") ? value.slice(1) : value;
|
|
6509
|
+
}
|
|
6510
|
+
function reconcilePersonIdentities(rows) {
|
|
6511
|
+
const perAccount = /* @__PURE__ */ new Map();
|
|
6512
|
+
for (const row of rows) {
|
|
6513
|
+
let entry = perAccount.get(row.accountId);
|
|
6514
|
+
if (!entry) {
|
|
6515
|
+
entry = { seen: /* @__PURE__ */ new Map(), divergent: 0 };
|
|
6516
|
+
perAccount.set(row.accountId, entry);
|
|
6517
|
+
}
|
|
6518
|
+
const p = row.phone ? bare(row.phone) : null;
|
|
6519
|
+
const t = row.telephone ? bare(row.telephone) : null;
|
|
6520
|
+
if (p && t && p !== t) entry.divergent += 1;
|
|
6521
|
+
const key = p ?? t;
|
|
6522
|
+
if (key) entry.seen.set(key, (entry.seen.get(key) ?? 0) + 1);
|
|
6523
|
+
}
|
|
6524
|
+
const accounts = [];
|
|
6525
|
+
let duplicates = 0;
|
|
6526
|
+
let divergent = 0;
|
|
6527
|
+
for (const [accountId, entry] of perAccount) {
|
|
6528
|
+
let dup = 0;
|
|
6529
|
+
for (const count of entry.seen.values()) if (count > 1) dup += count - 1;
|
|
6530
|
+
duplicates += dup;
|
|
6531
|
+
divergent += entry.divergent;
|
|
6532
|
+
if (dup > 0 || entry.divergent > 0) {
|
|
6533
|
+
accounts.push({ accountId, duplicates: dup, divergent: entry.divergent });
|
|
6534
|
+
}
|
|
6535
|
+
}
|
|
6536
|
+
return {
|
|
6537
|
+
ok: duplicates === 0 && divergent === 0,
|
|
6538
|
+
checked: rows.length,
|
|
6539
|
+
duplicates,
|
|
6540
|
+
divergent,
|
|
6541
|
+
accounts
|
|
6542
|
+
};
|
|
6543
|
+
}
|
|
6544
|
+
|
|
6545
|
+
// app/lib/whatsapp/notify-manager-recipients.ts
|
|
6546
|
+
function recipientsForAccount(managers, accountId) {
|
|
6547
|
+
const out = [];
|
|
6548
|
+
for (const [phone, value] of Object.entries(managers ?? {})) {
|
|
6549
|
+
let bound = null;
|
|
6550
|
+
if (typeof value === "string") {
|
|
6551
|
+
bound = value;
|
|
6552
|
+
} else if (value && typeof value === "object" && typeof value.managesAccount === "string") {
|
|
6553
|
+
bound = value.managesAccount;
|
|
6554
|
+
}
|
|
6555
|
+
if (bound !== null && bound === accountId) out.push(phone);
|
|
6556
|
+
}
|
|
6557
|
+
return out;
|
|
6558
|
+
}
|
|
6559
|
+
|
|
6302
6560
|
// app/lib/whatsapp/self-phone-admin-reconcile.ts
|
|
6303
6561
|
var HOUSE_CREDENTIAL_DIRNAME = "default";
|
|
6304
6562
|
function reconcileSelfPhoneAdmins(input) {
|
|
@@ -6712,6 +6970,22 @@ function authorizeRecallRead(input) {
|
|
|
6712
6970
|
};
|
|
6713
6971
|
}
|
|
6714
6972
|
|
|
6973
|
+
// app/lib/whatsapp/registered-party-reconcile.ts
|
|
6974
|
+
function buildRegisteredPartyReconcile(params) {
|
|
6975
|
+
const rows = [];
|
|
6976
|
+
const entries2 = Object.entries(params.registeredParties);
|
|
6977
|
+
for (const [phone, entry] of entries2) {
|
|
6978
|
+
const shadowedBy = isAdminPhone(phone, params.adminPhones) ? "adminPhones" : managedAccountFor(params.accountManagers, phone) ? "accountManagers" : null;
|
|
6979
|
+
const unresolved2 = !params.isValidAccount(entry.account);
|
|
6980
|
+
if (shadowedBy || unresolved2) {
|
|
6981
|
+
rows.push({ phone, account: entry.account, party: entry.party, shadowedBy, unresolved: unresolved2 });
|
|
6982
|
+
}
|
|
6983
|
+
}
|
|
6984
|
+
const shadowed = rows.filter((r) => r.shadowedBy !== null).length;
|
|
6985
|
+
const unresolved = rows.filter((r) => r.unresolved).length;
|
|
6986
|
+
return { ok: shadowed === 0 && unresolved === 0, checked: entries2.length, shadowed, unresolved, rows };
|
|
6987
|
+
}
|
|
6988
|
+
|
|
6715
6989
|
// server/routes/whatsapp.ts
|
|
6716
6990
|
var TAG15 = "[whatsapp:api]";
|
|
6717
6991
|
var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
@@ -6748,10 +7022,11 @@ function evaluateCallerScope(c, requestedAccountId) {
|
|
|
6748
7022
|
const decision = authorizeRecallRead({ requestedAccountId, callerAccountId, houseAdminScope });
|
|
6749
7023
|
return { decision, callerAccountId, houseAdminScope };
|
|
6750
7024
|
}
|
|
6751
|
-
function callerScopeDenial(c, requestedAccountId) {
|
|
7025
|
+
function callerScopeDenial(c, requestedAccountId, targetSource) {
|
|
6752
7026
|
const { decision, callerAccountId, houseAdminScope } = evaluateCallerScope(c, requestedAccountId);
|
|
6753
7027
|
if (decision.authorized) return { denial: null, callerAccountId };
|
|
6754
|
-
|
|
7028
|
+
const source = targetSource ? ` targetSource=${targetSource}` : "";
|
|
7029
|
+
console.error(`[whatsapp-read-tool] op=authz-deny requested=${requestedAccountId}${source} caller=${callerAccountId ?? "none"} houseAdmin=${houseAdminScope ? "y" : "n"}`);
|
|
6755
7030
|
return { denial: c.json({ error: decision.reason }, 403), callerAccountId };
|
|
6756
7031
|
}
|
|
6757
7032
|
function houseSocketScopeDenial(c, op) {
|
|
@@ -6860,6 +7135,44 @@ app3.get("/status", async (c) => {
|
|
|
6860
7135
|
if (!outboundSocketReconcile.ok) {
|
|
6861
7136
|
console.warn(`${TAG15} outbound-caller-unsigned-drift unsigned=${outboundSocketReconcile.unsignedFallbacks} of=${outboundSocketReconcile.checked} \u2014 at least one agent send carried no caller identity and left on the house socket; if it came from a sub-account session the recipient saw the wrong sender number`);
|
|
6862
7137
|
}
|
|
7138
|
+
const registeredPartyReconcile = buildRegisteredPartyReconcile({
|
|
7139
|
+
registeredParties: accountDir ? readRegisteredParties(accountDir) : {},
|
|
7140
|
+
adminPhones: accountDir ? readAdminPhones(accountDir) : [],
|
|
7141
|
+
accountManagers: accountDir ? readAccountManagers(accountDir) : {},
|
|
7142
|
+
isValidAccount: (id) => isProvisionedAccount(join15(MAXY_DIR, "data", "accounts"), id)
|
|
7143
|
+
});
|
|
7144
|
+
console.error(`${TAG15} op=registered-party-reconcile checked=${registeredPartyReconcile.checked} shadowed=${registeredPartyReconcile.shadowed} unresolved=${registeredPartyReconcile.unresolved} ok=${registeredPartyReconcile.ok}`);
|
|
7145
|
+
for (const row of registeredPartyReconcile.rows) {
|
|
7146
|
+
if (row.shadowedBy) {
|
|
7147
|
+
console.warn(`${TAG15} registered-party-shadowed phone=${row.phone} shadowedBy=${row.shadowedBy} \u2014 this counterparty is registered on this account but an earlier gate arm wins, so they reach the agent as an operator, not a party; remove the ${row.shadowedBy === "adminPhones" ? "admin phone" : "account-manager binding"} or the party registration`);
|
|
7148
|
+
}
|
|
7149
|
+
if (row.unresolved) {
|
|
7150
|
+
console.warn(`${TAG15} registered-party-unresolved phone=${row.phone} account=${row.account} \u2014 this registration names an account that is not in the registry, so every message from them on that number is hard-rejected`);
|
|
7151
|
+
}
|
|
7152
|
+
}
|
|
7153
|
+
let personIdentityReconcile = { ok: true, checked: 0, duplicates: 0, divergent: 0, accounts: [] };
|
|
7154
|
+
try {
|
|
7155
|
+
const identitySession = getSession();
|
|
7156
|
+
try {
|
|
7157
|
+
const identityRes = await identitySession.run(
|
|
7158
|
+
`MATCH (p:Person) WHERE p.accountId IS NOT NULL
|
|
7159
|
+
RETURN p.accountId AS accountId, p.phone AS phone, p.telephone AS telephone`
|
|
7160
|
+
);
|
|
7161
|
+
const identityRows = identityRes.records.map((r) => ({
|
|
7162
|
+
accountId: String(r.get("accountId")),
|
|
7163
|
+
phone: r.get("phone") ?? null,
|
|
7164
|
+
telephone: r.get("telephone") ?? null
|
|
7165
|
+
}));
|
|
7166
|
+
personIdentityReconcile = reconcilePersonIdentities(identityRows);
|
|
7167
|
+
} finally {
|
|
7168
|
+
await identitySession.close();
|
|
7169
|
+
}
|
|
7170
|
+
} catch (err) {
|
|
7171
|
+
personIdentityReconcile = { ok: false, checked: 0, duplicates: 0, divergent: 0, accounts: [], reason: String(err) };
|
|
7172
|
+
}
|
|
7173
|
+
if (!personIdentityReconcile.ok) {
|
|
7174
|
+
console.warn(`${TAG15} person-identity-drift duplicates=${personIdentityReconcile.duplicates} divergent=${personIdentityReconcile.divergent} of=${personIdentityReconcile.checked} \u2014 a WhatsApp sender may resolve to the wrong :Person or to none`);
|
|
7175
|
+
}
|
|
6863
7176
|
let routineScopeReconcile = null;
|
|
6864
7177
|
let neo = null;
|
|
6865
7178
|
try {
|
|
@@ -6916,7 +7229,7 @@ app3.get("/status", async (c) => {
|
|
|
6916
7229
|
const inheritanceSummary = policyInheritance.rows.map((r) => `${r.accountId}:${r.dmPolicy}/${r.adminPhones}/${r.allowFrom}`).join(", ");
|
|
6917
7230
|
console.error(`${TAG15} op=policy-inheritance rows=${policyInheritance.rows.length} inheriting=${policyInheritance.inheritedCount} [${inheritanceSummary}]`);
|
|
6918
7231
|
console.error(`${TAG15} op=status-projection caller=${callerAccountId?.slice(0, 8) ?? "none"} scope=house rows=${accounts.length}`);
|
|
6919
|
-
return c.json({ accounts, storeKeyReconcile, connectionAccountReconcile, selfPhoneAdminReconcile, outboundSocketReconcile, routineScopeReconcile, bindingAccountDrift, policyInheritance });
|
|
7232
|
+
return c.json({ accounts, storeKeyReconcile, connectionAccountReconcile, selfPhoneAdminReconcile, outboundSocketReconcile, registeredPartyReconcile, routineScopeReconcile, bindingAccountDrift, policyInheritance, personIdentityReconcile });
|
|
6920
7233
|
} catch (err) {
|
|
6921
7234
|
console.error(`${TAG15} status error: ${String(err)}`);
|
|
6922
7235
|
return c.json({ error: String(err) }, 500);
|
|
@@ -6999,7 +7312,7 @@ app3.post("/reconnect", async (c) => {
|
|
|
6999
7312
|
app3.post("/config", async (c) => {
|
|
7000
7313
|
try {
|
|
7001
7314
|
const body = await c.req.json().catch(() => ({}));
|
|
7002
|
-
const { action, phone, slug, fields, accountId, managesAccount, mode, policy } = body;
|
|
7315
|
+
const { action, phone, slug, fields, accountId, managesAccount, mode, policy, party, account: partyAccount } = body;
|
|
7003
7316
|
if (!action || typeof action !== "string") {
|
|
7004
7317
|
return c.json({ ok: false, error: 'Missing required field "action".' }, 400);
|
|
7005
7318
|
}
|
|
@@ -7083,6 +7396,46 @@ app3.post("/config", async (c) => {
|
|
|
7083
7396
|
console.error(`${TAG15} config action=remove-account-manager phone=${phone} boundTo=${bound?.account ?? "none"} caller=${callerAccountId || "none"} ok=${result.ok}`);
|
|
7084
7397
|
return c.json(result, result.ok ? 200 : 400);
|
|
7085
7398
|
}
|
|
7399
|
+
if (action === "add-registered-party" || action === "remove-registered-party" || action === "list-registered-parties") {
|
|
7400
|
+
const acct = resolveAccount();
|
|
7401
|
+
if (!acct) return c.json({ ok: false, error: "No account configured." }, 500);
|
|
7402
|
+
const callerAccountId = c.req.header("x-maxy-caller-account")?.trim() || "";
|
|
7403
|
+
const houseAdminScope = c.req.header("x-maxy-house-admin-scope")?.trim() || null;
|
|
7404
|
+
const houseAdmin = !!houseAdminScope || !!callerAccountId && callerAccountId === acct.accountId;
|
|
7405
|
+
if (action === "list-registered-parties") {
|
|
7406
|
+
const listDenial = bindingScopeDenial(c, callerAccountId, "list-registered-parties");
|
|
7407
|
+
if (listDenial) return listDenial;
|
|
7408
|
+
const all = readRegisteredParties(acct.accountDir);
|
|
7409
|
+
const registeredParties = houseAdmin ? all : Object.fromEntries(Object.entries(all).filter(([, entry]) => entry.account === callerAccountId));
|
|
7410
|
+
console.error(`${TAG15} config action=list-registered-parties caller=${callerAccountId || "none"} houseAdmin=${houseAdmin ? "y" : "n"} count=${Object.keys(registeredParties).length} of=${Object.keys(all).length}`);
|
|
7411
|
+
return c.json({ ok: true, registeredParties });
|
|
7412
|
+
}
|
|
7413
|
+
if (!phone || typeof phone !== "string") {
|
|
7414
|
+
return c.json({ ok: false, error: 'Missing required field "phone" (E.164 format, e.g. +441234567890).' }, 400);
|
|
7415
|
+
}
|
|
7416
|
+
if (action === "add-registered-party") {
|
|
7417
|
+
if (!partyAccount || typeof partyAccount !== "string") {
|
|
7418
|
+
return c.json({ ok: false, error: 'Missing required field "account" (the account whose own number this phone is a counterparty on).' }, 400);
|
|
7419
|
+
}
|
|
7420
|
+
if (!party || typeof party !== "string") {
|
|
7421
|
+
return c.json({ ok: false, error: 'Missing required field "party" (the label for this counterparty, e.g. "driver").' }, 400);
|
|
7422
|
+
}
|
|
7423
|
+
const addDenial = bindingScopeDenial(c, partyAccount, "add-registered-party");
|
|
7424
|
+
if (addDenial) return addDenial;
|
|
7425
|
+
const result2 = setRegisteredParty(acct.accountDir, phone, partyAccount, party);
|
|
7426
|
+
const partyReasonTail = !result2.ok && result2.reason ? ` reason=${result2.reason}` : "";
|
|
7427
|
+
console.error(`${TAG15} config action=add-registered-party phone=${phone} account=${partyAccount} caller=${callerAccountId || "none"} ok=${result2.ok}${partyReasonTail}`);
|
|
7428
|
+
return c.json(result2, result2.ok ? 200 : 400);
|
|
7429
|
+
}
|
|
7430
|
+
const registered = registeredPartyFor(readRegisteredParties(acct.accountDir), phone);
|
|
7431
|
+
if (!houseAdmin) {
|
|
7432
|
+
const removePartyDenial = bindingScopeDenial(c, registered?.account ?? "", "remove-registered-party");
|
|
7433
|
+
if (removePartyDenial) return removePartyDenial;
|
|
7434
|
+
}
|
|
7435
|
+
const result = clearRegisteredParty(acct.accountDir, phone);
|
|
7436
|
+
console.error(`${TAG15} config action=remove-registered-party phone=${phone} registeredTo=${registered?.account ?? "none"} caller=${callerAccountId || "none"} ok=${result.ok}`);
|
|
7437
|
+
return c.json(result, result.ok ? 200 : 400);
|
|
7438
|
+
}
|
|
7086
7439
|
const denial = houseSocketScopeDenial(c, `config action=${action}`);
|
|
7087
7440
|
if (denial) return denial;
|
|
7088
7441
|
const account = resolveAccount();
|
|
@@ -7245,14 +7598,15 @@ app3.post("/send-admin", async (c) => {
|
|
|
7245
7598
|
const adminPhones = readAdminPhones(account.accountDir);
|
|
7246
7599
|
const isAdmin = isAdminPhone(phone, adminPhones);
|
|
7247
7600
|
const managerMatch = isAdmin ? null : managedAccountFor(readAccountManagers(account.accountDir), phone);
|
|
7248
|
-
|
|
7601
|
+
const partyMatch = isAdmin || managerMatch ? null : registeredPartyFor(readRegisteredParties(account.accountDir), phone);
|
|
7602
|
+
if (!isAdmin && !managerMatch && !partyMatch) {
|
|
7249
7603
|
console.error(`[whatsapp:outbound] op=send-refused reason=not-admin-phone to=${phone} account=${sendAccountId}`);
|
|
7250
7604
|
return c.json(
|
|
7251
|
-
{ error: `Refused: ${phone} is not a registered admin phone
|
|
7605
|
+
{ error: `Refused: ${phone} is not a registered admin phone, account manager or registered party for this install. Only a number registered with whatsapp-config (add-admin-phone, add-account-manager or add-registered-party) can receive an agent-initiated send.` },
|
|
7252
7606
|
403
|
|
7253
7607
|
);
|
|
7254
7608
|
}
|
|
7255
|
-
const via = isAdmin ? "admin-phone" : "account-manager";
|
|
7609
|
+
const via = isAdmin ? "admin-phone" : managerMatch ? "account-manager" : "registered-party";
|
|
7256
7610
|
const res = resolveSocket(sendAccountId);
|
|
7257
7611
|
if (!res.ok) {
|
|
7258
7612
|
if (res.reason === "key-miss") {
|
|
@@ -7273,6 +7627,59 @@ app3.post("/send-admin", async (c) => {
|
|
|
7273
7627
|
return c.json({ error: String(err) }, 500);
|
|
7274
7628
|
}
|
|
7275
7629
|
});
|
|
7630
|
+
app3.post("/notify-manager", async (c) => {
|
|
7631
|
+
try {
|
|
7632
|
+
const body = await c.req.json().catch(() => ({}));
|
|
7633
|
+
const { senderDisplay, summary, draftLocation } = body;
|
|
7634
|
+
for (const [field2, value] of [["senderDisplay", senderDisplay], ["summary", summary], ["draftLocation", draftLocation]]) {
|
|
7635
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
7636
|
+
return c.json({ error: `Missing required field "${field2}".` }, 400);
|
|
7637
|
+
}
|
|
7638
|
+
}
|
|
7639
|
+
const account = resolveAccount();
|
|
7640
|
+
if (!account) return c.json({ error: "No account configured." }, 500);
|
|
7641
|
+
const callerAccountId = c.req.header("x-maxy-caller-account")?.trim() || null;
|
|
7642
|
+
if (!callerAccountId) {
|
|
7643
|
+
console.error(`[whatsapp:outbound] op=notify-manager-refused reason=caller-unsigned`);
|
|
7644
|
+
return c.json({ error: "Refused: this request carried no caller identity, so there is no account whose managers to notify.", reason: "caller-unsigned" }, 403);
|
|
7645
|
+
}
|
|
7646
|
+
const notifyAccountId = callerAccountId;
|
|
7647
|
+
const recipients = recipientsForAccount(readAccountManagers(account.accountDir), notifyAccountId);
|
|
7648
|
+
if (recipients.length === 0) {
|
|
7649
|
+
console.error(`[whatsapp:outbound] op=notify-manager-refused reason=no-registered-manager account=${notifyAccountId}`);
|
|
7650
|
+
return c.json({ error: `Refused: no account manager is registered for account "${notifyAccountId}". Register one with whatsapp-config add-account-manager.`, reason: "no-registered-manager" }, 403);
|
|
7651
|
+
}
|
|
7652
|
+
const res = resolveSocket(notifyAccountId);
|
|
7653
|
+
if (!res.ok) {
|
|
7654
|
+
if (res.reason === "key-miss") {
|
|
7655
|
+
console.error(`[whatsapp:outbound] op=socket-key-miss accountId=${notifyAccountId} presentKeys=${res.presentKeys.join(",") || "none"}`);
|
|
7656
|
+
return c.json({ error: `No WhatsApp socket is registered for account "${notifyAccountId}" (present: ${res.presentKeys.join(", ") || "none"}). This is an account-keying mismatch, not a dropped connection.` }, 503);
|
|
7657
|
+
}
|
|
7658
|
+
return c.json({ error: `WhatsApp account "${notifyAccountId}" is registered but its socket is disconnected.` }, 503);
|
|
7659
|
+
}
|
|
7660
|
+
const text = `New WhatsApp message from ${senderDisplay}.
|
|
7661
|
+
|
|
7662
|
+
${summary}
|
|
7663
|
+
|
|
7664
|
+
A reply has been drafted: ${draftLocation}`;
|
|
7665
|
+
const delivered = [];
|
|
7666
|
+
const failed = [];
|
|
7667
|
+
for (const phone of recipients) {
|
|
7668
|
+
const sent = await sendTextMessage(res.sock, normalizeE164(phone), text, { accountId: res.accountId });
|
|
7669
|
+
if (sent.success) delivered.push(phone);
|
|
7670
|
+
else failed.push(phone);
|
|
7671
|
+
}
|
|
7672
|
+
recordOutboundSend(callerAccountId, notifyAccountId);
|
|
7673
|
+
console.error(`[whatsapp:outbound] op=notify-manager-ok account=${notifyAccountId} socketAccount=${res.accountId} delivered=${delivered.length} failed=${failed.length}`);
|
|
7674
|
+
if (delivered.length === 0) {
|
|
7675
|
+
return c.json({ error: "Notification failed for every registered manager.", failed }, 502);
|
|
7676
|
+
}
|
|
7677
|
+
return c.json({ success: true, delivered, failed });
|
|
7678
|
+
} catch (err) {
|
|
7679
|
+
console.error(`${TAG15} notify-manager error: ${String(err)}`);
|
|
7680
|
+
return c.json({ error: String(err) }, 500);
|
|
7681
|
+
}
|
|
7682
|
+
});
|
|
7276
7683
|
app3.post("/reply", async (c) => {
|
|
7277
7684
|
const sendId = process.hrtime.bigint().toString(36).slice(-8);
|
|
7278
7685
|
try {
|
|
@@ -7349,8 +7756,8 @@ app3.get("/activity", (c) => {
|
|
|
7349
7756
|
});
|
|
7350
7757
|
app3.get("/conversations", (c) => {
|
|
7351
7758
|
try {
|
|
7352
|
-
const requestedAccountId =
|
|
7353
|
-
const { denial, callerAccountId } = callerScopeDenial(c, requestedAccountId);
|
|
7759
|
+
const { accountId: requestedAccountId, targetSource } = resolveLifecycleAccountId(c, c.req.query("accountId") ?? null);
|
|
7760
|
+
const { denial, callerAccountId } = callerScopeDenial(c, requestedAccountId, targetSource);
|
|
7354
7761
|
if (denial) return denial;
|
|
7355
7762
|
const projection = projectionFor(requestedAccountId);
|
|
7356
7763
|
const storeAccountId = resolveReadStoreKey(projection.storeAccountId);
|
|
@@ -7371,7 +7778,7 @@ app3.get("/conversations", (c) => {
|
|
|
7371
7778
|
};
|
|
7372
7779
|
});
|
|
7373
7780
|
conversations.sort((a, b) => (b.lastMessageTimestamp ?? 0) - (a.lastMessageTimestamp ?? 0));
|
|
7374
|
-
console.error(`${TAG15} conversations requested=${requestedAccountId} caller=${callerAccountId ?? "none"} account=${storeAccountId} projected=${projecting} storeAccount=${projecting ? storeAccountId : "self"} managerPhonesBound=${managerPhonesBound} storeKeyFound=${allConversations.size > 0} count=${conversations.length}`);
|
|
7781
|
+
console.error(`${TAG15} conversations requested=${requestedAccountId} targetSource=${targetSource} caller=${callerAccountId ?? "none"} account=${storeAccountId} projected=${projecting} storeAccount=${projecting ? storeAccountId : "self"} managerPhonesBound=${managerPhonesBound} storeKeyFound=${allConversations.size > 0} count=${conversations.length}`);
|
|
7375
7782
|
return c.json({ conversations });
|
|
7376
7783
|
} catch (err) {
|
|
7377
7784
|
console.error(`${TAG15} conversations error: ${String(err)}`);
|
|
@@ -7380,8 +7787,8 @@ app3.get("/conversations", (c) => {
|
|
|
7380
7787
|
});
|
|
7381
7788
|
app3.get("/messages", (c) => {
|
|
7382
7789
|
try {
|
|
7383
|
-
const requestedAccountId =
|
|
7384
|
-
const { denial, callerAccountId } = callerScopeDenial(c, requestedAccountId);
|
|
7790
|
+
const { accountId: requestedAccountId, targetSource } = resolveLifecycleAccountId(c, c.req.query("accountId") ?? null);
|
|
7791
|
+
const { denial, callerAccountId } = callerScopeDenial(c, requestedAccountId, targetSource);
|
|
7385
7792
|
if (denial) return denial;
|
|
7386
7793
|
const jid = c.req.query("jid");
|
|
7387
7794
|
if (!jid) {
|
|
@@ -7427,7 +7834,7 @@ app3.get("/messages", (c) => {
|
|
|
7427
7834
|
...reactions && reactions.length > 0 ? { reactions } : {}
|
|
7428
7835
|
};
|
|
7429
7836
|
});
|
|
7430
|
-
console.error(`${TAG15} messages requested=${requestedAccountId} caller=${callerAccountId ?? "none"} account=${storeAccountId} jid=${jid} projected=${projecting} storeAccount=${projecting ? storeAccountId : "self"} managerPhonesBound=${managerPhonesBound} storeKeyFound=${storeMessages.length > 0} limit=${effectiveLimit ?? "all"} returned=${messages.length} withAttachments=${withAttachments}`);
|
|
7837
|
+
console.error(`${TAG15} messages requested=${requestedAccountId} targetSource=${targetSource} caller=${callerAccountId ?? "none"} account=${storeAccountId} jid=${jid} projected=${projecting} storeAccount=${projecting ? storeAccountId : "self"} managerPhonesBound=${managerPhonesBound} storeKeyFound=${storeMessages.length > 0} limit=${effectiveLimit ?? "all"} returned=${messages.length} withAttachments=${withAttachments}`);
|
|
7431
7838
|
return c.json({ messages });
|
|
7432
7839
|
} catch (err) {
|
|
7433
7840
|
console.error(`${TAG15} messages error: ${String(err)}`);
|
|
@@ -7537,19 +7944,21 @@ app3.get("/conversation-graph-state", async (c) => {
|
|
|
7537
7944
|
}
|
|
7538
7945
|
});
|
|
7539
7946
|
app3.get("/group-info", async (c) => {
|
|
7540
|
-
const accountId =
|
|
7947
|
+
const { accountId, targetSource } = resolveLifecycleAccountId(c, c.req.query("accountId") ?? null);
|
|
7948
|
+
const callerAccountId = c.req.header("x-maxy-caller-account")?.trim() || null;
|
|
7949
|
+
const scope = `account=${accountId} targetSource=${targetSource} caller=${callerAccountId ?? "none"}`;
|
|
7541
7950
|
const jid = c.req.query("jid");
|
|
7542
7951
|
try {
|
|
7543
7952
|
if (!jid) {
|
|
7544
7953
|
return c.json({ error: "Missing required parameter: jid" }, 400);
|
|
7545
7954
|
}
|
|
7546
7955
|
if (!isGroupJid(jid)) {
|
|
7547
|
-
console.error(`${TAG15} group-info error="not a group JID" jid=${jid}
|
|
7956
|
+
console.error(`${TAG15} group-info error="not a group JID" jid=${jid} ${scope}`);
|
|
7548
7957
|
return c.json({ error: `"${jid}" is not a group JID. Group JIDs end with @g.us.` }, 400);
|
|
7549
7958
|
}
|
|
7550
7959
|
const res = resolveSocket(accountId);
|
|
7551
7960
|
if (!res.ok) {
|
|
7552
|
-
console.error(`${TAG15} group-info error="${formatSocketResolutionReason(res)}"
|
|
7961
|
+
console.error(`${TAG15} group-info error="${formatSocketResolutionReason(res)}" ${scope}`);
|
|
7553
7962
|
return c.json({ error: formatSocketResolutionMessage(res, accountId) }, 400);
|
|
7554
7963
|
}
|
|
7555
7964
|
const sock = res.sock;
|
|
@@ -7563,10 +7972,10 @@ app3.get("/group-info", async (c) => {
|
|
|
7563
7972
|
participantCount: meta.participants.length,
|
|
7564
7973
|
participants: meta.participants.map((p) => ({ jid: p.id, admin: p.admin ?? null }))
|
|
7565
7974
|
};
|
|
7566
|
-
console.error(`${TAG15} group-info jid=${jid} subject="${meta.subject}" participants=${meta.participants.length}
|
|
7975
|
+
console.error(`${TAG15} group-info jid=${jid} subject="${meta.subject}" participants=${meta.participants.length} ${scope}`);
|
|
7567
7976
|
return c.json(result);
|
|
7568
7977
|
} catch (err) {
|
|
7569
|
-
console.error(`${TAG15} group-info error="${String(err)}" jid=${jid}
|
|
7978
|
+
console.error(`${TAG15} group-info error="${String(err)}" jid=${jid} ${scope}`);
|
|
7570
7979
|
return c.json({ error: `Failed to fetch group info: ${String(err)}` }, 500);
|
|
7571
7980
|
}
|
|
7572
7981
|
});
|
|
@@ -9437,8 +9846,8 @@ var MAX_FILE_BYTES = 50 * 1024 * 1024;
|
|
|
9437
9846
|
|
|
9438
9847
|
// app/lib/audio-attachment-mime.ts
|
|
9439
9848
|
function isMimeAdmitted(mimeType, supportedSet) {
|
|
9440
|
-
const
|
|
9441
|
-
return supportedSet.has(
|
|
9849
|
+
const bare2 = mimeType.split(";")[0].trim().toLowerCase();
|
|
9850
|
+
return supportedSet.has(bare2);
|
|
9442
9851
|
}
|
|
9443
9852
|
|
|
9444
9853
|
// server/lib/stream-offset.ts
|
|
@@ -16305,7 +16714,19 @@ var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
|
|
|
16305
16714
|
"device-url:malformed",
|
|
16306
16715
|
// Task 2074 — the client receipt proving a notification card actually
|
|
16307
16716
|
// painted. The only signal that distinguishes "delivered" from "rendered".
|
|
16308
|
-
"wa-notify:rendered"
|
|
16717
|
+
"wa-notify:rendered",
|
|
16718
|
+
// Task 2138 — the desktop-notification surface.
|
|
16719
|
+
//
|
|
16720
|
+
// `desktop-capability` is posted once per AdminShell mount and is the only
|
|
16721
|
+
// signal that does not need a message to arrive: on a plain-HTTP LAN origin
|
|
16722
|
+
// the Notification API is unavailable and `requestPermission()` returns
|
|
16723
|
+
// "denied" with no prompt, so without this line that install reports nothing
|
|
16724
|
+
// and reads as "no messages yet".
|
|
16725
|
+
//
|
|
16726
|
+
// `desktop-outcome` carries one verdict per message — `shown`, a skip reason,
|
|
16727
|
+
// or `construct-failed` — so a missing banner is always explained.
|
|
16728
|
+
"wa-notify:desktop-capability",
|
|
16729
|
+
"wa-notify:desktop-outcome"
|
|
16309
16730
|
]);
|
|
16310
16731
|
var app24 = new Hono();
|
|
16311
16732
|
app24.post("/", async (c) => {
|
|
@@ -21797,7 +22218,8 @@ async function managerRcSpawn(opts) {
|
|
|
21797
22218
|
role: opts.role,
|
|
21798
22219
|
channel: opts.channel,
|
|
21799
22220
|
senderId: opts.senderId,
|
|
21800
|
-
targetAccountId: opts.targetAccountId
|
|
22221
|
+
targetAccountId: opts.targetAccountId,
|
|
22222
|
+
party: opts.party
|
|
21801
22223
|
})
|
|
21802
22224
|
}).catch((err) => ({ __throw: err instanceof Error ? err.message : String(err) }));
|
|
21803
22225
|
if ("__throw" in res) {
|
|
@@ -28034,7 +28456,13 @@ var WaGateway = class {
|
|
|
28034
28456
|
serverPath: this.deps.serverPath,
|
|
28035
28457
|
// Task 1575 — the gate's owner-self verdict; drives owner attribution
|
|
28036
28458
|
// for an unbound owner self-phone at the spawn site.
|
|
28037
|
-
ownerSelfPhone: input.ownerSelfPhone
|
|
28459
|
+
ownerSelfPhone: input.ownerSelfPhone,
|
|
28460
|
+
// Task 2234 — the registered-party label, forwarded verbatim so the
|
|
28461
|
+
// spawn env carries WA_SENDER_PARTY. Undefined for every other sender.
|
|
28462
|
+
// Only this call site takes it: the passive branch above hardcodes
|
|
28463
|
+
// role:'admin', and a registered party is public, so the combination
|
|
28464
|
+
// cannot arise there.
|
|
28465
|
+
party: input.party
|
|
28038
28466
|
});
|
|
28039
28467
|
} finally {
|
|
28040
28468
|
this.spawning.delete(senderId);
|
|
@@ -30549,7 +30977,7 @@ var waGateway = new WaGateway({
|
|
|
30549
30977
|
});
|
|
30550
30978
|
return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
|
|
30551
30979
|
},
|
|
30552
|
-
ensureChannelSession: async ({ accountId, effectiveAccountId, senderId, role, personId, gatewayUrl, serverPath, passive, text, msgId, ownerSelfPhone, replyContext }) => {
|
|
30980
|
+
ensureChannelSession: async ({ accountId, effectiveAccountId, senderId, role, personId, gatewayUrl, serverPath, passive, text, msgId, ownerSelfPhone, replyContext, party }) => {
|
|
30553
30981
|
warnNonUuidAccount("channel-spawn", effectiveAccountId);
|
|
30554
30982
|
if (passive) {
|
|
30555
30983
|
const phoneHash = createHash7("sha256").update(senderId).digest("hex").slice(0, 12);
|
|
@@ -30634,7 +31062,7 @@ var waGateway = new WaGateway({
|
|
|
30634
31062
|
const sourceSuffix = graphOwnerResolved ? " source=graph-owner" : "";
|
|
30635
31063
|
console.error(`[whatsapp-native] op=admin-identity senderId=${senderId} userId=${attribution}${sourceSuffix}`);
|
|
30636
31064
|
}
|
|
30637
|
-
const result = await managerRcSpawn({ ...req, userId });
|
|
31065
|
+
const result = await managerRcSpawn({ ...req, userId, party });
|
|
30638
31066
|
if ("error" in result) {
|
|
30639
31067
|
console.error(`[whatsapp-native] spawn-failed senderId=${senderId} role=${role} error=${result.error} status=${result.status}`);
|
|
30640
31068
|
return;
|
|
@@ -32209,6 +32637,7 @@ for (const acct of listValidAccounts()) {
|
|
|
32209
32637
|
}
|
|
32210
32638
|
purgeNonSocketAdminPhonesAtBoot();
|
|
32211
32639
|
purgeNonSocketAccountManagersAtBoot();
|
|
32640
|
+
purgeNonSocketRegisteredPartiesAtBoot();
|
|
32212
32641
|
void preloadEmbedModel();
|
|
32213
32642
|
warnOnChannelAdminBindingDrift();
|
|
32214
32643
|
var bootEnabled = Array.isArray(bootAccountConfig?.enabledPlugins) ? bootAccountConfig.enabledPlugins : [];
|
|
@@ -32337,7 +32766,10 @@ init({
|
|
|
32337
32766
|
// always captured it and manager.ts assigns it here, but this argument
|
|
32338
32767
|
// omitted it, so the agent received "this booking" with nothing to
|
|
32339
32768
|
// resolve it against.
|
|
32340
|
-
replyContext: msg.replyContext
|
|
32769
|
+
replyContext: msg.replyContext,
|
|
32770
|
+
// Task 2234 — the gate's registered-party label, carried to the spawn so
|
|
32771
|
+
// the session env has WA_SENDER_PARTY. Undefined for every other sender.
|
|
32772
|
+
party: msg.party
|
|
32341
32773
|
});
|
|
32342
32774
|
} catch (err) {
|
|
32343
32775
|
console.error(
|