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