@rubytech/create-maxy-code 0.1.409 → 0.1.410
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/scheduling/PLUGIN.md +2 -2
- package/payload/platform/plugins/scheduling/mcp/dist/__tests__/agent-dispatch-manager-grounding.test.js +5 -5
- package/payload/platform/plugins/scheduling/mcp/dist/__tests__/agent-dispatch-manager-grounding.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/agent-dispatch.test.js +103 -34
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/agent-dispatch.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/agent-dispatch.d.ts +45 -11
- package/payload/platform/plugins/scheduling/mcp/dist/lib/agent-dispatch.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/agent-dispatch.js +74 -23
- package/payload/platform/plugins/scheduling/mcp/dist/lib/agent-dispatch.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/standing-audit.test.js +48 -9
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/standing-audit.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts +9 -5
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js +53 -18
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-event-agent.test.js +24 -4
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-event-agent.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-update-agent.test.js +6 -4
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-update-agent.test.js.map +1 -1
- package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +2 -0
- package/payload/server/server.js +22 -4
package/package.json
CHANGED
|
@@ -124,14 +124,14 @@ An `action` invokes one pre-encoded MCP tool with no reasoning. When a due event
|
|
|
124
124
|
```
|
|
125
125
|
|
|
126
126
|
- `channel` — `whatsapp` or `telegram`.
|
|
127
|
-
- `destination` — the E.164 phone (WhatsApp) or chat id (Telegram) to run the turn for and reply to. It **must already be a registered admin or account-manager** on that channel; a destination that is not is rejected when the event is created.
|
|
127
|
+
- `destination` — the E.164 phone (WhatsApp) or chat id (Telegram) to run the turn for and reply to. It **must already be a registered admin or account-manager** on that channel; a destination that is not is rejected when the event is created. For WhatsApp the authoritative admin/manager lists are read from the socket-owning **house** account (never the event's own account), and the destination's registered account must match the account that created the schedule: an admin belongs to the house, a manager to its bound sub-account. The house/owner account may target any registered admin or manager; a sub-account is locked to its own manager(s). A refusal names the true reason — `not-registered` (in no house list) or `cross-account` (registered to a different account, naming both accounts).
|
|
128
128
|
- `prompt` — the instruction the agent runs.
|
|
129
129
|
|
|
130
130
|
An event carries **either** an `action` **or** an `agentDispatch`, never both — setting one clears the other. Agent dispatch is stored as `agentChannel`, `agentDestination`, `agentPrompt`. Use `schedule-update` with `agentDispatch` to modify or `agentDispatch: null` to clear. On WhatsApp, a destination bound as an account-manager scopes the spawned session to its sub-account; on Telegram the session runs on the house account.
|
|
131
131
|
|
|
132
132
|
A WhatsApp `agentDispatch` destination may therefore be an account manager, not only an admin phone — this is the supported way to send a manager a proactive or scheduled WhatsApp message. The reply is delivered over the house's paired line regardless of which sub-account the session is scoped to; socket ownership never restricts which number the dispatch can reach.
|
|
133
133
|
|
|
134
|
-
Dispatch results are recorded on the Event node (`lastDispatchResult`, `lastDispatchAt`) exactly as for tool actions. Every heartbeat tick also audits stored agent dispatches and logs `[schedule-audit] op=stale-dispatch`
|
|
134
|
+
Dispatch results are recorded on the Event node (`lastDispatchResult`, `lastDispatchAt`) exactly as for tool actions. Every heartbeat tick also audits stored agent dispatches against the same house-authority rule used at creation, and logs `[schedule-audit] op=stale-dispatch` with the identical reason codes — `not-registered` (destination left the house lists), `cross-account` (rebound to a different account than the one that created the schedule), or `sub-account-missing` (bound sub-account deleted) — so a binding that rots between scheduling and firing is surfaced with its true cause before it mis-dispatches.
|
|
135
135
|
|
|
136
136
|
## Reconciling public bookings (D1 → graph)
|
|
137
137
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// Task 1435 — content-presence gate for the scheduling side of the WhatsApp
|
|
2
|
-
// deliverability grounding. `
|
|
3
|
-
// manager (adminPhones OR accountManagers), so scheduled dispatch is
|
|
4
|
-
// supported way to reach a manager. PLUGIN.md must say so plainly, and
|
|
5
|
-
// over-the-house-line must be stated, so the agent cites it instead of
|
|
6
|
-
// a line-ownership restriction. Mirrors time-resolve-description.test.ts.
|
|
2
|
+
// deliverability grounding. `validateAgentDestination` accepts an account
|
|
3
|
+
// manager (the house's adminPhones OR accountManagers), so scheduled dispatch is
|
|
4
|
+
// the supported way to reach a manager. PLUGIN.md must say so plainly, and
|
|
5
|
+
// deliver-over-the-house-line must be stated, so the agent cites it instead of
|
|
6
|
+
// inventing a line-ownership restriction. Mirrors time-resolve-description.test.ts.
|
|
7
7
|
import { describe, it, expect } from "vitest";
|
|
8
8
|
import { readFileSync } from "fs";
|
|
9
9
|
import { resolve } from "path";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-dispatch-manager-grounding.test.js","sourceRoot":"","sources":["../../src/__tests__/agent-dispatch-manager-grounding.test.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,
|
|
1
|
+
{"version":3,"file":"agent-dispatch-manager-grounding.test.js","sourceRoot":"","sources":["../../src/__tests__/agent-dispatch-manager-grounding.test.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,0EAA0E;AAC1E,iFAAiF;AACjF,2EAA2E;AAC3E,+EAA+E;AAC/E,oFAAoF;AACpF,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAE/B,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,SAAS,EAAE,oBAAoB,CAAC,EAAE,MAAM,CAAC,CAAC;AAE9E,QAAQ,CAAC,0CAA0C,EAAE,GAAG,EAAE;IACxD,EAAE,CAAC,4EAA4E,EAAE,GAAG,EAAE;QACpF,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CACtB,4EAA4E,CAC7E,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sEAAsE,EAAE,GAAG,EAAE;QAC9E,MAAM,CAAC,MAAM,CAAC,CAAC,SAAS,CACtB,sEAAsE,CACvE,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1,10 +1,27 @@
|
|
|
1
1
|
import { describe, it, expect, afterEach } from "vitest";
|
|
2
|
-
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { mkdtempSync, rmSync, mkdirSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
-
import { normalizeE164, phonesMatch,
|
|
6
|
-
|
|
5
|
+
import { normalizeE164, phonesMatch, validateAgentDestination, boundSubAccountFor, } from "../agent-dispatch.js";
|
|
6
|
+
// The house-authority validator resolves the socket-owning house from the
|
|
7
|
+
// PARENT accounts dir (via account-enumeration), so fixtures must use
|
|
8
|
+
// UUID-named account dirs carrying role:"house" (the resolver's contract).
|
|
9
|
+
const HOUSE = "11111111-1111-4111-8111-111111111111";
|
|
10
|
+
const SUB_A = "22222222-2222-4222-8222-222222222222";
|
|
11
|
+
const SUB_B = "33333333-3333-4333-8333-333333333333";
|
|
12
|
+
function makeAccountsDir(seed) {
|
|
7
13
|
const dir = mkdtempSync(join(tmpdir(), "agent-dispatch-"));
|
|
14
|
+
seed(dir);
|
|
15
|
+
return dir;
|
|
16
|
+
}
|
|
17
|
+
function seedAccount(accountsDir, id, cfg) {
|
|
18
|
+
const d = join(accountsDir, id);
|
|
19
|
+
mkdirSync(d, { recursive: true });
|
|
20
|
+
writeFileSync(join(d, "account.json"), JSON.stringify(cfg), "utf-8");
|
|
21
|
+
}
|
|
22
|
+
// A single account dir (for the direct-read helpers boundSubAccountFor tests).
|
|
23
|
+
function makeAccountDir(config) {
|
|
24
|
+
const dir = mkdtempSync(join(tmpdir(), "agent-dispatch-one-"));
|
|
8
25
|
writeFileSync(join(dir, "account.json"), JSON.stringify(config), "utf-8");
|
|
9
26
|
return dir;
|
|
10
27
|
}
|
|
@@ -23,62 +40,114 @@ describe("normalizeE164 / phonesMatch (replica of platform normalize.ts)", () =>
|
|
|
23
40
|
expect(phonesMatch("", "")).toBe(false);
|
|
24
41
|
});
|
|
25
42
|
});
|
|
26
|
-
describe("
|
|
43
|
+
describe("validateAgentDestination — whatsapp house-authority + creating-account match", () => {
|
|
27
44
|
let dirs = [];
|
|
28
45
|
afterEach(() => {
|
|
29
46
|
for (const d of dirs)
|
|
30
47
|
rmSync(d, { recursive: true, force: true });
|
|
31
48
|
dirs = [];
|
|
32
49
|
});
|
|
33
|
-
it("
|
|
34
|
-
const
|
|
35
|
-
dirs.push(
|
|
36
|
-
|
|
37
|
-
expect(isValidAgentDestination(dir, "whatsapp", "+44 7504 472444")).toEqual({ valid: true });
|
|
50
|
+
it("house-created dispatch to a house admin is valid (spaced form matches stored bare form)", () => {
|
|
51
|
+
const acc = makeAccountsDir((a) => seedAccount(a, HOUSE, { role: "house", whatsapp: { adminPhones: ["+447504472444"] } }));
|
|
52
|
+
dirs.push(acc);
|
|
53
|
+
expect(validateAgentDestination(acc, HOUSE, "whatsapp", "+44 7504 472444")).toEqual({ valid: true });
|
|
38
54
|
});
|
|
39
|
-
it("
|
|
40
|
-
const
|
|
41
|
-
|
|
55
|
+
it("sub-account-created dispatch to its own manager is valid", () => {
|
|
56
|
+
const acc = makeAccountsDir((a) => {
|
|
57
|
+
seedAccount(a, HOUSE, { role: "house", whatsapp: { accountManagers: { "447958391734": SUB_A } } });
|
|
58
|
+
seedAccount(a, SUB_A, {});
|
|
42
59
|
});
|
|
43
|
-
dirs.push(
|
|
44
|
-
expect(
|
|
60
|
+
dirs.push(acc);
|
|
61
|
+
expect(validateAgentDestination(acc, SUB_A, "whatsapp", "+44 7958 391734")).toEqual({ valid: true });
|
|
45
62
|
});
|
|
46
|
-
it("
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
63
|
+
it("sub-account-created dispatch to ANOTHER sub-account's manager is cross-account", () => {
|
|
64
|
+
const acc = makeAccountsDir((a) => {
|
|
65
|
+
seedAccount(a, HOUSE, { role: "house", whatsapp: { accountManagers: { "447958391734": SUB_A } } });
|
|
66
|
+
seedAccount(a, SUB_A, {});
|
|
67
|
+
seedAccount(a, SUB_B, {});
|
|
68
|
+
});
|
|
69
|
+
dirs.push(acc);
|
|
70
|
+
expect(validateAgentDestination(acc, SUB_B, "whatsapp", "+447958391734")).toEqual({
|
|
50
71
|
valid: false,
|
|
51
|
-
reason: "
|
|
72
|
+
reason: "cross-account",
|
|
73
|
+
destinationAccount: SUB_A,
|
|
74
|
+
scheduleAccount: SUB_B,
|
|
52
75
|
});
|
|
53
76
|
});
|
|
54
|
-
it("
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
77
|
+
it("sub-account-created dispatch to a house admin is cross-account (sub locked to own managers)", () => {
|
|
78
|
+
const acc = makeAccountsDir((a) => {
|
|
79
|
+
seedAccount(a, HOUSE, { role: "house", whatsapp: { adminPhones: ["+447504472444"] } });
|
|
80
|
+
seedAccount(a, SUB_A, {});
|
|
81
|
+
});
|
|
82
|
+
dirs.push(acc);
|
|
83
|
+
expect(validateAgentDestination(acc, SUB_A, "whatsapp", "+447504472444")).toEqual({
|
|
84
|
+
valid: false,
|
|
85
|
+
reason: "cross-account",
|
|
86
|
+
destinationAccount: HOUSE,
|
|
87
|
+
scheduleAccount: SUB_A,
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
it("house-created dispatch to any sub-account manager is valid (owner exception)", () => {
|
|
91
|
+
const acc = makeAccountsDir((a) => {
|
|
92
|
+
seedAccount(a, HOUSE, { role: "house", whatsapp: { accountManagers: { "447958391734": SUB_A } } });
|
|
93
|
+
seedAccount(a, SUB_A, {});
|
|
94
|
+
});
|
|
95
|
+
dirs.push(acc);
|
|
96
|
+
expect(validateAgentDestination(acc, HOUSE, "whatsapp", "+447958391734")).toEqual({ valid: true });
|
|
97
|
+
});
|
|
98
|
+
it("unregistered number is not-registered", () => {
|
|
99
|
+
const acc = makeAccountsDir((a) => seedAccount(a, HOUSE, { role: "house", whatsapp: { adminPhones: ["+447504472444"] } }));
|
|
100
|
+
dirs.push(acc);
|
|
101
|
+
expect(validateAgentDestination(acc, HOUSE, "whatsapp", "+447999999999")).toEqual({
|
|
102
|
+
valid: false,
|
|
103
|
+
reason: "not-registered",
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
it("reads the HOUSE list, not the creating sub-account's own account.json (the 1439 bug)", () => {
|
|
107
|
+
// The manager map lives ONLY on the house; the sub-account carries none.
|
|
108
|
+
// Pre-fix this read the sub-account and returned not-registered.
|
|
109
|
+
const acc = makeAccountsDir((a) => {
|
|
110
|
+
seedAccount(a, HOUSE, { role: "house", whatsapp: { accountManagers: { "447958391734": SUB_A } } });
|
|
111
|
+
seedAccount(a, SUB_A, { whatsapp: {} });
|
|
112
|
+
});
|
|
113
|
+
dirs.push(acc);
|
|
114
|
+
expect(validateAgentDestination(acc, SUB_A, "whatsapp", "+447958391734")).toEqual({ valid: true });
|
|
115
|
+
});
|
|
116
|
+
it("a number that is both a house admin and a sub-account's manager binds to the sub-account (manager wins)", () => {
|
|
117
|
+
const acc = makeAccountsDir((a) => {
|
|
118
|
+
seedAccount(a, HOUSE, {
|
|
119
|
+
role: "house",
|
|
120
|
+
whatsapp: { adminPhones: ["+447958391734"], accountManagers: { "447958391734": SUB_A } },
|
|
121
|
+
});
|
|
122
|
+
seedAccount(a, SUB_A, {});
|
|
123
|
+
});
|
|
124
|
+
dirs.push(acc);
|
|
125
|
+
// created in SUB_A → its own manager → valid
|
|
126
|
+
expect(validateAgentDestination(acc, SUB_A, "whatsapp", "+447958391734")).toEqual({ valid: true });
|
|
58
127
|
});
|
|
59
128
|
});
|
|
60
|
-
describe("
|
|
129
|
+
describe("validateAgentDestination — telegram unchanged (creating-account dir, house-only)", () => {
|
|
61
130
|
let dirs = [];
|
|
62
131
|
afterEach(() => {
|
|
63
132
|
for (const d of dirs)
|
|
64
133
|
rmSync(d, { recursive: true, force: true });
|
|
65
134
|
dirs = [];
|
|
66
135
|
});
|
|
67
|
-
it("accepts a chat id in telegram.adminUsers", () => {
|
|
68
|
-
const
|
|
69
|
-
dirs.push(
|
|
70
|
-
expect(
|
|
136
|
+
it("accepts a chat id in the creating account's telegram.adminUsers", () => {
|
|
137
|
+
const acc = makeAccountsDir((a) => seedAccount(a, HOUSE, { role: "house", telegram: { adminUsers: [12345678] } }));
|
|
138
|
+
dirs.push(acc);
|
|
139
|
+
expect(validateAgentDestination(acc, HOUSE, "telegram", "12345678")).toEqual({ valid: true });
|
|
71
140
|
});
|
|
72
|
-
it("rejects a chat id
|
|
73
|
-
const
|
|
74
|
-
dirs.push(
|
|
75
|
-
expect(
|
|
141
|
+
it("rejects a chat id absent from adminUsers as not-registered", () => {
|
|
142
|
+
const acc = makeAccountsDir((a) => seedAccount(a, HOUSE, { role: "house", telegram: { adminUsers: [12345678] } }));
|
|
143
|
+
dirs.push(acc);
|
|
144
|
+
expect(validateAgentDestination(acc, HOUSE, "telegram", "99999999")).toEqual({
|
|
76
145
|
valid: false,
|
|
77
|
-
reason: "
|
|
146
|
+
reason: "not-registered",
|
|
78
147
|
});
|
|
79
148
|
});
|
|
80
149
|
});
|
|
81
|
-
describe("boundSubAccountFor — whatsapp accountManagers only", () => {
|
|
150
|
+
describe("boundSubAccountFor — whatsapp accountManagers only (direct single-dir read)", () => {
|
|
82
151
|
let dirs = [];
|
|
83
152
|
afterEach(() => {
|
|
84
153
|
for (const d of dirs)
|
package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/agent-dispatch.test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-dispatch.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/agent-dispatch.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"agent-dispatch.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/agent-dispatch.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EACL,aAAa,EACb,WAAW,EACX,wBAAwB,EACxB,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAE9B,0EAA0E;AAC1E,sEAAsE;AACtE,2EAA2E;AAC3E,MAAM,KAAK,GAAG,sCAAsC,CAAC;AACrD,MAAM,KAAK,GAAG,sCAAsC,CAAC;AACrD,MAAM,KAAK,GAAG,sCAAsC,CAAC;AAErD,SAAS,eAAe,CAAC,IAAmC;IAC1D,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAC3D,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,WAAW,CAAC,WAAmB,EAAE,EAAU,EAAE,GAA4B;IAChF,MAAM,CAAC,GAAG,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IAChC,SAAS,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAClC,aAAa,CAAC,IAAI,CAAC,CAAC,EAAE,cAAc,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,CAAC;AACvE,CAAC;AAED,+EAA+E;AAC/E,SAAS,cAAc,CAAC,MAA+B;IACrD,MAAM,GAAG,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,qBAAqB,CAAC,CAAC,CAAC;IAC/D,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC;IAC1E,OAAO,GAAG,CAAC;AACb,CAAC;AAED,QAAQ,CAAC,gEAAgE,EAAE,GAAG,EAAE;IAC9E,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,CAAC,aAAa,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC9D,MAAM,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;QAC7D,MAAM,CAAC,aAAa,CAAC,wBAAwB,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,GAAG,EAAE;QAC1C,MAAM,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,CAAC,WAAW,CAAC,gBAAgB,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjE,MAAM,CAAC,WAAW,CAAC,eAAe,EAAE,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClE,MAAM,CAAC,WAAW,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,8EAA8E,EAAE,GAAG,EAAE;IAC5F,IAAI,IAAI,GAAa,EAAE,CAAC;IACxB,SAAS,CAAC,GAAG,EAAE;QACb,KAAK,MAAM,CAAC,IAAI,IAAI;YAAE,MAAM,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,IAAI,GAAG,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yFAAyF,EAAE,GAAG,EAAE;QACjG,MAAM,GAAG,GAAG,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE,CAChC,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,CAAC,eAAe,CAAC,EAAE,EAAE,CAAC,CACvF,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,MAAM,CAAC,wBAAwB,CAAC,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,iBAAiB,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACvG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;QAClE,MAAM,GAAG,GAAG,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE;YAChC,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,eAAe,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;YACnG,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,MAAM,CAAC,wBAAwB,CAAC,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,iBAAiB,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACvG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gFAAgF,EAAE,GAAG,EAAE;QACxF,MAAM,GAAG,GAAG,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE;YAChC,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,eAAe,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;YACnG,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;YAC1B,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,MAAM,CAAC,wBAAwB,CAAC,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC;YAChF,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,eAAe;YACvB,kBAAkB,EAAE,KAAK;YACzB,eAAe,EAAE,KAAK;SACvB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6FAA6F,EAAE,GAAG,EAAE;QACrG,MAAM,GAAG,GAAG,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE;YAChC,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,CAAC,eAAe,CAAC,EAAE,EAAE,CAAC,CAAC;YACvF,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,MAAM,CAAC,wBAAwB,CAAC,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC;YAChF,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,eAAe;YACvB,kBAAkB,EAAE,KAAK;YACzB,eAAe,EAAE,KAAK;SACvB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8EAA8E,EAAE,GAAG,EAAE;QACtF,MAAM,GAAG,GAAG,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE;YAChC,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,eAAe,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;YACnG,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,MAAM,CAAC,wBAAwB,CAAC,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACrG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,MAAM,GAAG,GAAG,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE,CAChC,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,CAAC,eAAe,CAAC,EAAE,EAAE,CAAC,CACvF,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,MAAM,CAAC,wBAAwB,CAAC,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC;YAChF,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,gBAAgB;SACzB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sFAAsF,EAAE,GAAG,EAAE;QAC9F,yEAAyE;QACzE,iEAAiE;QACjE,MAAM,GAAG,GAAG,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE;YAChC,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,eAAe,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;YACnG,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QAC1C,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,MAAM,CAAC,wBAAwB,CAAC,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACrG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yGAAyG,EAAE,GAAG,EAAE;QACjH,MAAM,GAAG,GAAG,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE;YAChC,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE;gBACpB,IAAI,EAAE,OAAO;gBACb,QAAQ,EAAE,EAAE,WAAW,EAAE,CAAC,eAAe,CAAC,EAAE,eAAe,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,EAAE;aACzF,CAAC,CAAC;YACH,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;QAC5B,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,6CAA6C;QAC7C,MAAM,CAAC,wBAAwB,CAAC,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACrG,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,kFAAkF,EAAE,GAAG,EAAE;IAChG,IAAI,IAAI,GAAa,EAAE,CAAC;IACxB,SAAS,CAAC,GAAG,EAAE;QACb,KAAK,MAAM,CAAC,IAAI,IAAI;YAAE,MAAM,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,IAAI,GAAG,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,MAAM,GAAG,GAAG,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,UAAU,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QACnH,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,MAAM,CAAC,wBAAwB,CAAC,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAChG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,GAAG,GAAG,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,UAAU,EAAE,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QACnH,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,MAAM,CAAC,wBAAwB,CAAC,GAAG,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC;YAC3E,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,gBAAgB;SACzB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,6EAA6E,EAAE,GAAG,EAAE;IAC3F,IAAI,IAAI,GAAa,EAAE,CAAC;IACxB,SAAS,CAAC,GAAG,EAAE;QACb,KAAK,MAAM,CAAC,IAAI,IAAI;YAAE,MAAM,CAAC,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,IAAI,GAAG,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,GAAG,GAAG,cAAc,CAAC;YACzB,QAAQ,EAAE,EAAE,eAAe,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,EAAE;SAC7D,CAAC,CAAC;QACH,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,MAAM,CAAC,kBAAkB,CAAC,GAAG,EAAE,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;QAC3D,MAAM,GAAG,GAAG,cAAc,CAAC,EAAE,QAAQ,EAAE,EAAE,WAAW,EAAE,CAAC,eAAe,CAAC,EAAE,eAAe,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QAClG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACf,MAAM,CAAC,kBAAkB,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC9D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
export type AgentChannel = "whatsapp" | "telegram";
|
|
2
|
+
/**
|
|
3
|
+
* Reason codes are the true cause, not a symptom (Task 1439 observability):
|
|
4
|
+
* - `not-registered`: the destination is in no authoritative list (house's
|
|
5
|
+
* `adminPhones`/`accountManagers` for WhatsApp; the creating account's
|
|
6
|
+
* `telegram.adminUsers` for Telegram).
|
|
7
|
+
* - `cross-account`: the destination IS registered, but to an account other
|
|
8
|
+
* than the one that created the schedule (and the creator is not the house).
|
|
9
|
+
* Names both accounts so the mismatch is explained, not guessed.
|
|
10
|
+
*/
|
|
2
11
|
export type DestinationValidity = {
|
|
3
12
|
valid: true;
|
|
4
13
|
} | {
|
|
5
14
|
valid: false;
|
|
6
|
-
reason: "
|
|
15
|
+
reason: "not-registered";
|
|
16
|
+
} | {
|
|
17
|
+
valid: false;
|
|
18
|
+
reason: "cross-account";
|
|
19
|
+
destinationAccount: string;
|
|
20
|
+
scheduleAccount: string;
|
|
7
21
|
};
|
|
8
22
|
/**
|
|
9
23
|
* Replica of `normalize.ts:normalizeE164` — strip whitespace/dashes/parens, a
|
|
@@ -24,20 +38,40 @@ export declare function readChannelAdmins(accountDir: string, channel: "telegram
|
|
|
24
38
|
adminUsers: number[];
|
|
25
39
|
};
|
|
26
40
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
41
|
+
* The account a WhatsApp `destination` is registered to on the HOUSE's lists,
|
|
42
|
+
* or null if it is in neither list. A manager binding wins over an admin match:
|
|
43
|
+
* if the number is an `accountManagers` key its registered account is the bound
|
|
44
|
+
* sub-account (the isolation-relevant fact); otherwise if it is an `adminPhones`
|
|
45
|
+
* entry its registered account is the house. `houseDir` is the house account's
|
|
46
|
+
* directory; `houseAccountId` its id.
|
|
47
|
+
*/
|
|
48
|
+
export declare function registeredAccountForDestination(houseDir: string, houseAccountId: string, destination: string): string | null;
|
|
49
|
+
/**
|
|
50
|
+
* Validate a dispatch destination under the Task 1439 two-part rule.
|
|
51
|
+
* `accountsDir` is the PARENT accounts directory
|
|
52
|
+
* (`$PLATFORM_ROOT/../data/accounts`); `creatingAccountId` is the account that
|
|
53
|
+
* created (or owns) the schedule.
|
|
54
|
+
*
|
|
55
|
+
* - whatsapp: resolve the socket-owning house, read ITS authoritative lists,
|
|
56
|
+
* and compute the destination's registered account. `not-registered` if it
|
|
57
|
+
* is in no house list. Otherwise valid iff the creator IS the house (owner
|
|
58
|
+
* exception: it owns the single line and may target any registered
|
|
59
|
+
* admin/manager) OR the registered account equals the creating account.
|
|
60
|
+
* Any other case is `cross-account`.
|
|
61
|
+
* - telegram: house-only (Task 1378; 1380 deferred). Reads the creating
|
|
62
|
+
* account's own `telegram.adminUsers`; there is no manager surface and thus
|
|
63
|
+
* no cross-account concept — an integer chat id present in the list is valid,
|
|
64
|
+
* anything else is `not-registered`.
|
|
32
65
|
*/
|
|
33
|
-
export declare function
|
|
66
|
+
export declare function validateAgentDestination(accountsDir: string, creatingAccountId: string, channel: AgentChannel, destination: string): DestinationValidity;
|
|
34
67
|
/**
|
|
35
68
|
* Validate an `agentDispatch` at write time (shared by `schedule-event` and
|
|
36
69
|
* `schedule-update` so the two write paths cannot drift on the rule): reject an
|
|
37
|
-
* empty prompt, and reject a destination that
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* `$PLATFORM_ROOT/../data/accounts
|
|
70
|
+
* empty prompt, and reject a destination that fails the two-part house-authority
|
|
71
|
+
* rule. Throws with a caller-facing message naming the true reason; returns on
|
|
72
|
+
* success. `accountId` is the account that created the schedule — the authority
|
|
73
|
+
* lists are read from the house under `$PLATFORM_ROOT/../data/accounts`, and the
|
|
74
|
+
* destination's registered account must match `accountId` (house/owner aside).
|
|
41
75
|
*/
|
|
42
76
|
export declare function validateAgentDispatch(accountId: string, agentDispatch: {
|
|
43
77
|
channel: AgentChannel;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-dispatch.d.ts","sourceRoot":"","sources":["../../src/lib/agent-dispatch.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"agent-dispatch.d.ts","sourceRoot":"","sources":["../../src/lib/agent-dispatch.ts"],"names":[],"mappings":"AAqBA,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,UAAU,CAAC;AAEnD;;;;;;;;GAQG;AACH,MAAM,MAAM,mBAAmB,GAC3B;IAAE,KAAK,EAAE,IAAI,CAAA;CAAE,GACf;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAA;CAAE,GAC1C;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,eAAe,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnG;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOnD;AAED,8EAA8E;AAC9E,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAIzD;AAED,UAAU,cAAc;IACtB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzC;AAED;;uCAEuC;AACvC,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,UAAU,GAClB,cAAc,CAAC;AAClB,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,UAAU,GAClB;IAAE,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AA8B5B;;;;;;;GAOG;AACH,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,MAAM,EACtB,WAAW,EAAE,MAAM,GAClB,MAAM,GAAG,IAAI,CAOf;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,MAAM,EACnB,iBAAiB,EAAE,MAAM,EACzB,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,MAAM,GAClB,mBAAmB,CAmBrB;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE;IAAE,OAAO,EAAE,YAAY,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC5E,IAAI,CAsBN;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMzF"}
|
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Task 1378 — agent-bearing scheduled dispatch: destination validation.
|
|
3
|
+
* Task 1439 — house single-authority + creating-account match.
|
|
3
4
|
*
|
|
4
5
|
* The scheduling MCP cannot import `platform/ui` (plugin boundary), so this
|
|
5
6
|
* module replicates the minimal E.164 matching from
|
|
6
|
-
* `platform/ui/app/lib/whatsapp/normalize.ts
|
|
7
|
-
*
|
|
7
|
+
* `platform/ui/app/lib/whatsapp/normalize.ts`. For WhatsApp the authoritative
|
|
8
|
+
* `adminPhones` + `accountManagers` lists live ONLY on the socket-owning house
|
|
9
|
+
* account (Tasks 1377/1382), so the validator resolves the house via
|
|
10
|
+
* `resolveHouseOrSoleAccountId` — the same authority the inbound gate uses — and
|
|
11
|
+
* reads its config, NOT the event's own account. It then requires the
|
|
12
|
+
* destination's registered account to match the account that created the
|
|
13
|
+
* schedule (the house/owner account excepted), so a schedule created in account
|
|
14
|
+
* X cannot message another account's people. One `validateAgentDestination` is
|
|
8
15
|
* shared by `schedule-event`, `schedule-update`, and the dispatcher's standing
|
|
9
|
-
* audit so
|
|
10
|
-
* drift between the write path and the audit.
|
|
16
|
+
* audit so create-time and fire-time cannot diverge on rule or reason codes.
|
|
11
17
|
*/
|
|
12
18
|
import { readFileSync } from "node:fs";
|
|
13
19
|
import { resolve } from "node:path";
|
|
20
|
+
import { resolveHouseOrSoleAccountId } from "../../../../../lib/account-enumeration/dist/index.js";
|
|
14
21
|
/**
|
|
15
22
|
* Replica of `normalize.ts:normalizeE164` — strip whitespace/dashes/parens, a
|
|
16
23
|
* `whatsapp:` prefix, and a leading `+` or `00`; return digits-only or `''`.
|
|
@@ -60,31 +67,71 @@ export function readChannelAdmins(accountDir, channel) {
|
|
|
60
67
|
return { adminUsers };
|
|
61
68
|
}
|
|
62
69
|
/**
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
70
|
+
* The account a WhatsApp `destination` is registered to on the HOUSE's lists,
|
|
71
|
+
* or null if it is in neither list. A manager binding wins over an admin match:
|
|
72
|
+
* if the number is an `accountManagers` key its registered account is the bound
|
|
73
|
+
* sub-account (the isolation-relevant fact); otherwise if it is an `adminPhones`
|
|
74
|
+
* entry its registered account is the house. `houseDir` is the house account's
|
|
75
|
+
* directory; `houseAccountId` its id.
|
|
68
76
|
*/
|
|
69
|
-
export function
|
|
77
|
+
export function registeredAccountForDestination(houseDir, houseAccountId, destination) {
|
|
78
|
+
const { adminPhones, accountManagers } = readChannelAdmins(houseDir, "whatsapp");
|
|
79
|
+
for (const [managerPhone, subAccountId] of Object.entries(accountManagers)) {
|
|
80
|
+
if (subAccountId && phonesMatch(managerPhone, destination))
|
|
81
|
+
return subAccountId;
|
|
82
|
+
}
|
|
83
|
+
if (adminPhones.some((p) => phonesMatch(p, destination)))
|
|
84
|
+
return houseAccountId;
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Validate a dispatch destination under the Task 1439 two-part rule.
|
|
89
|
+
* `accountsDir` is the PARENT accounts directory
|
|
90
|
+
* (`$PLATFORM_ROOT/../data/accounts`); `creatingAccountId` is the account that
|
|
91
|
+
* created (or owns) the schedule.
|
|
92
|
+
*
|
|
93
|
+
* - whatsapp: resolve the socket-owning house, read ITS authoritative lists,
|
|
94
|
+
* and compute the destination's registered account. `not-registered` if it
|
|
95
|
+
* is in no house list. Otherwise valid iff the creator IS the house (owner
|
|
96
|
+
* exception: it owns the single line and may target any registered
|
|
97
|
+
* admin/manager) OR the registered account equals the creating account.
|
|
98
|
+
* Any other case is `cross-account`.
|
|
99
|
+
* - telegram: house-only (Task 1378; 1380 deferred). Reads the creating
|
|
100
|
+
* account's own `telegram.adminUsers`; there is no manager surface and thus
|
|
101
|
+
* no cross-account concept — an integer chat id present in the list is valid,
|
|
102
|
+
* anything else is `not-registered`.
|
|
103
|
+
*/
|
|
104
|
+
export function validateAgentDestination(accountsDir, creatingAccountId, channel, destination) {
|
|
70
105
|
if (channel === "whatsapp") {
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
|
|
106
|
+
const houseAccountId = resolveHouseOrSoleAccountId(accountsDir);
|
|
107
|
+
const houseDir = resolve(accountsDir, houseAccountId);
|
|
108
|
+
const registered = registeredAccountForDestination(houseDir, houseAccountId, destination);
|
|
109
|
+
if (registered === null)
|
|
110
|
+
return { valid: false, reason: "not-registered" };
|
|
111
|
+
if (creatingAccountId === houseAccountId)
|
|
112
|
+
return { valid: true };
|
|
113
|
+
if (registered === creatingAccountId)
|
|
114
|
+
return { valid: true };
|
|
115
|
+
return {
|
|
116
|
+
valid: false,
|
|
117
|
+
reason: "cross-account",
|
|
118
|
+
destinationAccount: registered,
|
|
119
|
+
scheduleAccount: creatingAccountId,
|
|
120
|
+
};
|
|
75
121
|
}
|
|
76
|
-
const { adminUsers } = readChannelAdmins(
|
|
122
|
+
const { adminUsers } = readChannelAdmins(resolve(accountsDir, creatingAccountId), "telegram");
|
|
77
123
|
const asNumber = Number(destination);
|
|
78
124
|
const ok = Number.isInteger(asNumber) && adminUsers.includes(asNumber);
|
|
79
|
-
return ok ? { valid: true } : { valid: false, reason: "
|
|
125
|
+
return ok ? { valid: true } : { valid: false, reason: "not-registered" };
|
|
80
126
|
}
|
|
81
127
|
/**
|
|
82
128
|
* Validate an `agentDispatch` at write time (shared by `schedule-event` and
|
|
83
129
|
* `schedule-update` so the two write paths cannot drift on the rule): reject an
|
|
84
|
-
* empty prompt, and reject a destination that
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* `$PLATFORM_ROOT/../data/accounts
|
|
130
|
+
* empty prompt, and reject a destination that fails the two-part house-authority
|
|
131
|
+
* rule. Throws with a caller-facing message naming the true reason; returns on
|
|
132
|
+
* success. `accountId` is the account that created the schedule — the authority
|
|
133
|
+
* lists are read from the house under `$PLATFORM_ROOT/../data/accounts`, and the
|
|
134
|
+
* destination's registered account must match `accountId` (house/owner aside).
|
|
88
135
|
*/
|
|
89
136
|
export function validateAgentDispatch(accountId, agentDispatch) {
|
|
90
137
|
const { channel, destination, prompt } = agentDispatch;
|
|
@@ -95,10 +142,14 @@ export function validateAgentDispatch(accountId, agentDispatch) {
|
|
|
95
142
|
if (!platformRoot) {
|
|
96
143
|
throw new Error("PLATFORM_ROOT environment variable is required to validate the agentDispatch destination");
|
|
97
144
|
}
|
|
98
|
-
const
|
|
99
|
-
const check =
|
|
145
|
+
const accountsDir = resolve(platformRoot, "..", "data/accounts");
|
|
146
|
+
const check = validateAgentDestination(accountsDir, accountId, channel, destination);
|
|
100
147
|
if (!check.valid) {
|
|
101
|
-
|
|
148
|
+
if (check.reason === "cross-account") {
|
|
149
|
+
throw new Error(`agentDispatch destination "${destination}" belongs to a different account on ${channel} — ` +
|
|
150
|
+
`reason=cross-account destinationAccount=${check.destinationAccount} scheduleAccount=${check.scheduleAccount}`);
|
|
151
|
+
}
|
|
152
|
+
throw new Error(`agentDispatch destination "${destination}" is not a registered admin/account-manager on ${channel} — reason=not-registered`);
|
|
102
153
|
}
|
|
103
154
|
}
|
|
104
155
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-dispatch.js","sourceRoot":"","sources":["../../src/lib/agent-dispatch.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"agent-dispatch.js","sourceRoot":"","sources":["../../src/lib/agent-dispatch.ts"],"names":[],"mappings":"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"}
|
package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/standing-audit.test.js
CHANGED
|
@@ -13,12 +13,16 @@ function driverReturning(rows) {
|
|
|
13
13
|
}),
|
|
14
14
|
};
|
|
15
15
|
}
|
|
16
|
+
// The audit now reads the authoritative lists from the socket-owning house, so
|
|
17
|
+
// the house dir must be UUID-named and carry role:"house" (resolver contract).
|
|
16
18
|
let accountsDir;
|
|
17
|
-
const HOUSE = "
|
|
19
|
+
const HOUSE = "11111111-1111-4111-8111-111111111111";
|
|
20
|
+
const SUB_OK = "44444444-4444-4444-8444-444444444444";
|
|
21
|
+
const SUB_OTHER = "55555555-5555-4555-8555-555555555555";
|
|
18
22
|
function seedHouse(waConfig) {
|
|
19
23
|
const dir = join(accountsDir, HOUSE);
|
|
20
24
|
mkdirSync(dir, { recursive: true });
|
|
21
|
-
writeFileSync(join(dir, "account.json"), JSON.stringify({ whatsapp: waConfig }), "utf-8");
|
|
25
|
+
writeFileSync(join(dir, "account.json"), JSON.stringify({ role: "house", whatsapp: waConfig }), "utf-8");
|
|
22
26
|
}
|
|
23
27
|
let errSpy;
|
|
24
28
|
function auditLines() {
|
|
@@ -36,18 +40,31 @@ afterEach(() => {
|
|
|
36
40
|
vi.clearAllMocks();
|
|
37
41
|
});
|
|
38
42
|
describe("runStandingAudit", () => {
|
|
39
|
-
it("emits
|
|
43
|
+
it("emits not-registered when the destination is in no house list", async () => {
|
|
40
44
|
seedHouse({ adminPhones: ["+447111111111"] }); // destination not present
|
|
41
45
|
const driver = driverReturning([
|
|
42
46
|
{ eventId: "e1", accountId: HOUSE, agentChannel: "whatsapp", agentDestination: "+447504472444" },
|
|
43
47
|
]);
|
|
44
48
|
await runStandingAudit(driver, [HOUSE], accountsDir);
|
|
45
49
|
expect(auditLines()).toEqual([
|
|
46
|
-
expect.stringContaining("op=stale-dispatch eventId=e1 reason=
|
|
50
|
+
expect.stringContaining("op=stale-dispatch eventId=e1 reason=not-registered"),
|
|
51
|
+
]);
|
|
52
|
+
});
|
|
53
|
+
it("emits cross-account when a house event targets a manager bound to a different sub-account", async () => {
|
|
54
|
+
// The manager is bound to SUB_OK, but the event was created in SUB_OTHER.
|
|
55
|
+
seedHouse({ accountManagers: { "447504472444": SUB_OK } });
|
|
56
|
+
mkdirSync(join(accountsDir, SUB_OK), { recursive: true });
|
|
57
|
+
mkdirSync(join(accountsDir, SUB_OTHER), { recursive: true });
|
|
58
|
+
const driver = driverReturning([
|
|
59
|
+
{ eventId: "e5", accountId: SUB_OTHER, agentChannel: "whatsapp", agentDestination: "+447504472444" },
|
|
60
|
+
]);
|
|
61
|
+
await runStandingAudit(driver, [SUB_OTHER], accountsDir);
|
|
62
|
+
expect(auditLines()).toEqual([
|
|
63
|
+
expect.stringContaining(`op=stale-dispatch eventId=e5 reason=cross-account destinationAccount=${SUB_OK} scheduleAccount=${SUB_OTHER}`),
|
|
47
64
|
]);
|
|
48
65
|
});
|
|
49
66
|
it("emits sub-account-missing when a manager binding points at a deleted sub-account", async () => {
|
|
50
|
-
seedHouse({ accountManagers: { "447504472444": "
|
|
67
|
+
seedHouse({ accountManagers: { "447504472444": "66666666-6666-4666-8666-666666666666" } }); // sub dir never created
|
|
51
68
|
const driver = driverReturning([
|
|
52
69
|
{ eventId: "e2", accountId: HOUSE, agentChannel: "whatsapp", agentDestination: "+447504472444" },
|
|
53
70
|
]);
|
|
@@ -56,16 +73,16 @@ describe("runStandingAudit", () => {
|
|
|
56
73
|
expect.stringContaining("op=stale-dispatch eventId=e2 reason=sub-account-missing"),
|
|
57
74
|
]);
|
|
58
75
|
});
|
|
59
|
-
it("emits nothing for a healthy binding (manager + present sub-account)", async () => {
|
|
60
|
-
seedHouse({ accountManagers: { "447504472444":
|
|
61
|
-
mkdirSync(join(accountsDir,
|
|
76
|
+
it("emits nothing for a healthy binding (house event, manager + present sub-account)", async () => {
|
|
77
|
+
seedHouse({ accountManagers: { "447504472444": SUB_OK } });
|
|
78
|
+
mkdirSync(join(accountsDir, SUB_OK), { recursive: true });
|
|
62
79
|
const driver = driverReturning([
|
|
63
80
|
{ eventId: "e3", accountId: HOUSE, agentChannel: "whatsapp", agentDestination: "+447504472444" },
|
|
64
81
|
]);
|
|
65
82
|
await runStandingAudit(driver, [HOUSE], accountsDir);
|
|
66
83
|
expect(auditLines()).toEqual([]);
|
|
67
84
|
});
|
|
68
|
-
it("emits nothing for a healthy plain admin destination", async () => {
|
|
85
|
+
it("emits nothing for a healthy plain admin destination (house event)", async () => {
|
|
69
86
|
seedHouse({ adminPhones: ["+447504472444"] });
|
|
70
87
|
const driver = driverReturning([
|
|
71
88
|
{ eventId: "e4", accountId: HOUSE, agentChannel: "whatsapp", agentDestination: "+447504472444" },
|
|
@@ -73,5 +90,27 @@ describe("runStandingAudit", () => {
|
|
|
73
90
|
await runStandingAudit(driver, [HOUSE], accountsDir);
|
|
74
91
|
expect(auditLines()).toEqual([]);
|
|
75
92
|
});
|
|
93
|
+
it("emits nothing when a sub-account event targets its OWN manager", async () => {
|
|
94
|
+
seedHouse({ accountManagers: { "447504472444": SUB_OK } });
|
|
95
|
+
mkdirSync(join(accountsDir, SUB_OK), { recursive: true });
|
|
96
|
+
const driver = driverReturning([
|
|
97
|
+
{ eventId: "e6", accountId: SUB_OK, agentChannel: "whatsapp", agentDestination: "+447504472444" },
|
|
98
|
+
]);
|
|
99
|
+
await runStandingAudit(driver, [SUB_OK], accountsDir);
|
|
100
|
+
expect(auditLines()).toEqual([]);
|
|
101
|
+
});
|
|
102
|
+
it("never throws on house drift (multi-house): returns with op=house-unresolved", async () => {
|
|
103
|
+
// Two role:"house" dirs — the resolver loud-fails. The audit must not throw
|
|
104
|
+
// into the sweep; it emits one diagnostic and returns.
|
|
105
|
+
seedHouse({ adminPhones: ["+447504472444"] });
|
|
106
|
+
const secondHouse = "77777777-7777-4777-8777-777777777777";
|
|
107
|
+
mkdirSync(join(accountsDir, secondHouse), { recursive: true });
|
|
108
|
+
writeFileSync(join(accountsDir, secondHouse, "account.json"), JSON.stringify({ role: "house", whatsapp: {} }), "utf-8");
|
|
109
|
+
const driver = driverReturning([
|
|
110
|
+
{ eventId: "e7", accountId: HOUSE, agentChannel: "whatsapp", agentDestination: "+447504472444" },
|
|
111
|
+
]);
|
|
112
|
+
await expect(runStandingAudit(driver, [HOUSE], accountsDir)).resolves.toBeUndefined();
|
|
113
|
+
expect(auditLines().some((l) => l.includes("op=house-unresolved"))).toBe(true);
|
|
114
|
+
});
|
|
76
115
|
});
|
|
77
116
|
//# sourceMappingURL=standing-audit.test.js.map
|
package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/standing-audit.test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"standing-audit.test.js","sourceRoot":"","sources":["../../../src/scripts/__tests__/standing-audit.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAS7D,SAAS,eAAe,CAAC,IAAgB;IACvC,OAAO;QACL,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;YACd,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;gBACtB,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAiB,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;aACjE,CAAC,CAAC;YACH,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,GAAE,CAAC,CAAC;SAC7B,CAAC;KACH,CAAC;AACJ,CAAC;AAED,IAAI,WAAmB,CAAC;AACxB,MAAM,KAAK,GAAG,
|
|
1
|
+
{"version":3,"file":"standing-audit.test.js","sourceRoot":"","sources":["../../../src/scripts/__tests__/standing-audit.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAS7D,SAAS,eAAe,CAAC,IAAgB;IACvC,OAAO;QACL,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;YACd,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;gBACtB,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAiB,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;aACjE,CAAC,CAAC;YACH,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,GAAE,CAAC,CAAC;SAC7B,CAAC;KACH,CAAC;AACJ,CAAC;AAED,+EAA+E;AAC/E,+EAA+E;AAC/E,IAAI,WAAmB,CAAC;AACxB,MAAM,KAAK,GAAG,sCAAsC,CAAC;AACrD,MAAM,MAAM,GAAG,sCAAsC,CAAC;AACtD,MAAM,SAAS,GAAG,sCAAsC,CAAC;AAEzD,SAAS,SAAS,CAAC,QAAiC;IAClD,MAAM,GAAG,GAAG,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IACrC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACpC,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,cAAc,CAAC,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC;AAC3G,CAAC;AAED,IAAI,MAAmC,CAAC;AACxC,SAAS,UAAU;IACjB,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK;SACrB,GAAG,CAAC,CAAC,CAAY,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SACnC,MAAM,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,UAAU,CAAC,GAAG,EAAE;IACd,WAAW,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAC7D,MAAM,GAAG,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;AACnE,CAAC,CAAC,CAAC;AACH,SAAS,CAAC,GAAG,EAAE;IACb,MAAM,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,MAAM,CAAC,WAAW,EAAE,CAAC;IACrB,EAAE,CAAC,aAAa,EAAE,CAAC;AACrB,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,EAAE,CAAC,+DAA+D,EAAE,KAAK,IAAI,EAAE;QAC7E,SAAS,CAAC,EAAE,WAAW,EAAE,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC,CAAC,0BAA0B;QACzE,MAAM,MAAM,GAAG,eAAe,CAAC;YAC7B,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe,EAAE;SACjG,CAAC,CAAC;QACH,MAAM,gBAAgB,CAAC,MAAe,EAAE,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,CAAC;QAC9D,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,OAAO,CAAC;YAC3B,MAAM,CAAC,gBAAgB,CAAC,oDAAoD,CAAC;SAC9E,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2FAA2F,EAAE,KAAK,IAAI,EAAE;QACzG,0EAA0E;QAC1E,SAAS,CAAC,EAAE,eAAe,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;QAC3D,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1D,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7D,MAAM,MAAM,GAAG,eAAe,CAAC;YAC7B,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe,EAAE;SACrG,CAAC,CAAC;QACH,MAAM,gBAAgB,CAAC,MAAe,EAAE,CAAC,SAAS,CAAC,EAAE,WAAW,CAAC,CAAC;QAClE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,OAAO,CAAC;YAC3B,MAAM,CAAC,gBAAgB,CACrB,wEAAwE,MAAM,oBAAoB,SAAS,EAAE,CAC9G;SACF,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kFAAkF,EAAE,KAAK,IAAI,EAAE;QAChG,SAAS,CAAC,EAAE,eAAe,EAAE,EAAE,cAAc,EAAE,sCAAsC,EAAE,EAAE,CAAC,CAAC,CAAC,wBAAwB;QACpH,MAAM,MAAM,GAAG,eAAe,CAAC;YAC7B,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe,EAAE;SACjG,CAAC,CAAC;QACH,MAAM,gBAAgB,CAAC,MAAe,EAAE,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,CAAC;QAC9D,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,OAAO,CAAC;YAC3B,MAAM,CAAC,gBAAgB,CAAC,yDAAyD,CAAC;SACnF,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kFAAkF,EAAE,KAAK,IAAI,EAAE;QAChG,SAAS,CAAC,EAAE,eAAe,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;QAC3D,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1D,MAAM,MAAM,GAAG,eAAe,CAAC;YAC7B,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe,EAAE;SACjG,CAAC,CAAC;QACH,MAAM,gBAAgB,CAAC,MAAe,EAAE,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,CAAC;QAC9D,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;QACjF,SAAS,CAAC,EAAE,WAAW,EAAE,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,eAAe,CAAC;YAC7B,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe,EAAE;SACjG,CAAC,CAAC;QACH,MAAM,gBAAgB,CAAC,MAAe,EAAE,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,CAAC;QAC9D,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;QAC9E,SAAS,CAAC,EAAE,eAAe,EAAE,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;QAC3D,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1D,MAAM,MAAM,GAAG,eAAe,CAAC;YAC7B,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe,EAAE;SAClG,CAAC,CAAC;QACH,MAAM,gBAAgB,CAAC,MAAe,EAAE,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,CAAC;QAC/D,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6EAA6E,EAAE,KAAK,IAAI,EAAE;QAC3F,4EAA4E;QAC5E,uDAAuD;QACvD,SAAS,CAAC,EAAE,WAAW,EAAE,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;QAC9C,MAAM,WAAW,GAAG,sCAAsC,CAAC;QAC3D,SAAS,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/D,aAAa,CACX,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,cAAc,CAAC,EAC9C,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,EAC/C,OAAO,CACR,CAAC;QACF,MAAM,MAAM,GAAG,eAAe,CAAC;YAC7B,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,eAAe,EAAE;SACjG,CAAC,CAAC;QACH,MAAM,MAAM,CAAC,gBAAgB,CAAC,MAAe,EAAE,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC;QAC/F,MAAM,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACjF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -48,12 +48,16 @@ export declare function dispatchAgentTurn(eventId: string, channel: string, dest
|
|
|
48
48
|
/**
|
|
49
49
|
* The no-event detector. Each heartbeat tick, over every agent-dispatch event
|
|
50
50
|
* for a local account, re-checks the destination binding against the current
|
|
51
|
-
* channel config so a destination removed from
|
|
52
|
-
* out of band —
|
|
53
|
-
*
|
|
54
|
-
* the
|
|
51
|
+
* channel config so a destination removed from the house's
|
|
52
|
+
* `adminPhones`/`accountManagers` out of band — a manager rebound to another
|
|
53
|
+
* sub-account, or a manager whose sub-account was deleted — is surfaced before
|
|
54
|
+
* the next fire silently mis-dispatches. It applies the SAME two-part rule
|
|
55
|
+
* (`validateAgentDestination`) create-time validation uses, so a schedule that
|
|
56
|
+
* passed creation but later drifts is explained with the identical reason codes
|
|
57
|
+
* rather than silently dropped (Task 1439). Independent of any inbound: it reads
|
|
58
|
+
* the node and the on-disk house config, nothing else. Emits one
|
|
55
59
|
* `[schedule-audit] op=stale-dispatch` line per invalid binding; a healthy
|
|
56
|
-
* binding emits nothing.
|
|
60
|
+
* binding emits nothing. Never throws (honours the sweep's contract).
|
|
57
61
|
*/
|
|
58
62
|
export declare function runStandingAudit(driver: Driver, localAccountIds: string[], accountsDir: string): Promise<void>;
|
|
59
63
|
//# sourceMappingURL=agent-turn-dispatch.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-turn-dispatch.d.ts","sourceRoot":"","sources":["../../src/scripts/agent-turn-dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"agent-turn-dispatch.d.ts","sourceRoot":"","sources":["../../src/scripts/agent-turn-dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,cAAc,CAAC;AAU3C,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACvE;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GACrE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErB;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE;IACzC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,YAAY,CAaf;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,MAAM,GAAG,IAAI,EACtB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CA0Df;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,MAAM,EACd,eAAe,EAAE,MAAM,EAAE,EACzB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CA8Ef"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { validateAgentDestination, boundSubAccountFor, } from "../lib/agent-dispatch.js";
|
|
4
|
+
import { resolveHouseOrSoleAccountId } from "../../../../../lib/account-enumeration/dist/index.js";
|
|
4
5
|
/**
|
|
5
6
|
* Decide how a due event dispatches from its selected node fields. An event
|
|
6
7
|
* carries at most one mode (enforced at write); agent dispatch takes precedence
|
|
@@ -83,12 +84,16 @@ export async function dispatchAgentTurn(eventId, channel, destination, prompt, e
|
|
|
83
84
|
/**
|
|
84
85
|
* The no-event detector. Each heartbeat tick, over every agent-dispatch event
|
|
85
86
|
* for a local account, re-checks the destination binding against the current
|
|
86
|
-
* channel config so a destination removed from
|
|
87
|
-
* out of band —
|
|
88
|
-
*
|
|
89
|
-
* the
|
|
87
|
+
* channel config so a destination removed from the house's
|
|
88
|
+
* `adminPhones`/`accountManagers` out of band — a manager rebound to another
|
|
89
|
+
* sub-account, or a manager whose sub-account was deleted — is surfaced before
|
|
90
|
+
* the next fire silently mis-dispatches. It applies the SAME two-part rule
|
|
91
|
+
* (`validateAgentDestination`) create-time validation uses, so a schedule that
|
|
92
|
+
* passed creation but later drifts is explained with the identical reason codes
|
|
93
|
+
* rather than silently dropped (Task 1439). Independent of any inbound: it reads
|
|
94
|
+
* the node and the on-disk house config, nothing else. Emits one
|
|
90
95
|
* `[schedule-audit] op=stale-dispatch` line per invalid binding; a healthy
|
|
91
|
-
* binding emits nothing.
|
|
96
|
+
* binding emits nothing. Never throws (honours the sweep's contract).
|
|
92
97
|
*/
|
|
93
98
|
export async function runStandingAudit(driver, localAccountIds, accountsDir) {
|
|
94
99
|
if (localAccountIds.length === 0)
|
|
@@ -111,20 +116,50 @@ export async function runStandingAudit(driver, localAccountIds, accountsDir) {
|
|
|
111
116
|
finally {
|
|
112
117
|
await session.close();
|
|
113
118
|
}
|
|
119
|
+
// The authoritative WhatsApp lists live on the socket-owning house; resolve it
|
|
120
|
+
// once up front for the sub-account-missing check. `validateAgentDestination`
|
|
121
|
+
// re-resolves the house per row, so a house that is unresolvable for the whole
|
|
122
|
+
// tick (zero/multi house — genuine drift) is caught here and the audit returns
|
|
123
|
+
// with one diagnostic. The per-row body is ALSO guarded below, so a house that
|
|
124
|
+
// becomes unresolvable mid-tick (a re-roll between this resolve and a later row)
|
|
125
|
+
// degrades that row to `op=house-unresolved` and continues — the audit never
|
|
126
|
+
// throws either way.
|
|
127
|
+
let houseDir;
|
|
128
|
+
try {
|
|
129
|
+
houseDir = join(accountsDir, resolveHouseOrSoleAccountId(accountsDir));
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
console.error(`[schedule-audit] op=house-unresolved detail=${err instanceof Error ? err.message : String(err)}`);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
114
135
|
for (const row of rows) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
console.error(`[schedule-audit] op=stale-dispatch eventId=${row.eventId} reason=sub-account-missing`);
|
|
136
|
+
try {
|
|
137
|
+
const check = validateAgentDestination(accountsDir, row.accountId, row.agentChannel, row.agentDestination);
|
|
138
|
+
if (!check.valid) {
|
|
139
|
+
if (check.reason === "cross-account") {
|
|
140
|
+
console.error(`[schedule-audit] op=stale-dispatch eventId=${row.eventId} reason=cross-account ` +
|
|
141
|
+
`destinationAccount=${check.destinationAccount} scheduleAccount=${check.scheduleAccount}`);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
console.error(`[schedule-audit] op=stale-dispatch eventId=${row.eventId} reason=not-registered`);
|
|
145
|
+
}
|
|
146
|
+
continue;
|
|
127
147
|
}
|
|
148
|
+
// A still-valid WhatsApp manager destination whose bound sub-account directory
|
|
149
|
+
// is gone would spawn against a missing account — surface it distinctly. The
|
|
150
|
+
// binding lives on the house, so read it from the house dir.
|
|
151
|
+
if (row.agentChannel === "whatsapp") {
|
|
152
|
+
const sub = boundSubAccountFor(houseDir, row.agentDestination);
|
|
153
|
+
if (sub && !existsSync(join(accountsDir, sub))) {
|
|
154
|
+
console.error(`[schedule-audit] op=stale-dispatch eventId=${row.eventId} reason=sub-account-missing`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
// A mid-tick house re-roll can make the per-row house resolution throw.
|
|
160
|
+
// Honour the never-throws contract: log this row and keep sweeping.
|
|
161
|
+
console.error(`[schedule-audit] op=house-unresolved eventId=${row.eventId} ` +
|
|
162
|
+
`detail=${err instanceof Error ? err.message : String(err)}`);
|
|
128
163
|
}
|
|
129
164
|
}
|
|
130
165
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-turn-dispatch.js","sourceRoot":"","sources":["../../src/scripts/agent-turn-dispatch.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EACL,
|
|
1
|
+
{"version":3,"file":"agent-turn-dispatch.js","sourceRoot":"","sources":["../../src/scripts/agent-turn-dispatch.ts"],"names":[],"mappings":"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"}
|
package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-event-agent.test.js
CHANGED
|
@@ -23,11 +23,19 @@ vi.mock("../../../../../../lib/graph-write/dist/index.js", () => ({
|
|
|
23
23
|
import { scheduleEvent } from "../schedule-event.js";
|
|
24
24
|
const CREATED_BY = { agent: "admin", session: "sess-1", tool: "schedule-event" };
|
|
25
25
|
let platformRoot;
|
|
26
|
-
|
|
26
|
+
// The validator resolves the socket-owning house, so the account dir must be
|
|
27
|
+
// UUID-named and carry role:"house" (account-enumeration resolver contract).
|
|
28
|
+
const ACCOUNT_ID = "11111111-1111-4111-8111-111111111111";
|
|
29
|
+
const SUB_A = "22222222-2222-4222-8222-222222222222";
|
|
27
30
|
function seedAccount(waConfig) {
|
|
28
31
|
const accountDir = join(platformRoot, "..", "data", "accounts", ACCOUNT_ID);
|
|
29
32
|
mkdirSync(accountDir, { recursive: true });
|
|
30
|
-
writeFileSync(join(accountDir, "account.json"), JSON.stringify({ whatsapp: waConfig }), "utf-8");
|
|
33
|
+
writeFileSync(join(accountDir, "account.json"), JSON.stringify({ role: "house", whatsapp: waConfig }), "utf-8");
|
|
34
|
+
}
|
|
35
|
+
function seedSub(id, cfg) {
|
|
36
|
+
const d = join(platformRoot, "..", "data", "accounts", id);
|
|
37
|
+
mkdirSync(d, { recursive: true });
|
|
38
|
+
writeFileSync(join(d, "account.json"), JSON.stringify(cfg), "utf-8");
|
|
31
39
|
}
|
|
32
40
|
beforeEach(() => {
|
|
33
41
|
writeCalls.length = 0;
|
|
@@ -74,7 +82,7 @@ describe("scheduleEvent — agentDispatch validation", () => {
|
|
|
74
82
|
agentDispatch: { channel: "whatsapp", destination: "+447504472444", prompt: " " },
|
|
75
83
|
})).rejects.toThrow(/prompt/i);
|
|
76
84
|
});
|
|
77
|
-
it("rejects a destination that is
|
|
85
|
+
it("rejects a destination that is in no house list (not-registered)", async () => {
|
|
78
86
|
seedAccount({ adminPhones: ["+447504472444"] });
|
|
79
87
|
await expect(scheduleEvent({
|
|
80
88
|
accountId: ACCOUNT_ID,
|
|
@@ -83,7 +91,19 @@ describe("scheduleEvent — agentDispatch validation", () => {
|
|
|
83
91
|
sessionKey: "s",
|
|
84
92
|
createdBy: CREATED_BY,
|
|
85
93
|
agentDispatch: { channel: "whatsapp", destination: "+447999999999", prompt: "hi" },
|
|
86
|
-
})).rejects.toThrow(/
|
|
94
|
+
})).rejects.toThrow(/not-registered/);
|
|
95
|
+
});
|
|
96
|
+
it("rejects cross-account: schedule created in a sub-account targeting a house admin", async () => {
|
|
97
|
+
seedAccount({ adminPhones: ["+447504472444"] }); // house
|
|
98
|
+
seedSub(SUB_A, {}); // creating sub-account, no managers of its own
|
|
99
|
+
await expect(scheduleEvent({
|
|
100
|
+
accountId: SUB_A,
|
|
101
|
+
name: "e",
|
|
102
|
+
startDate: "2026-07-03T09:00:00Z",
|
|
103
|
+
sessionKey: "s",
|
|
104
|
+
createdBy: CREATED_BY,
|
|
105
|
+
agentDispatch: { channel: "whatsapp", destination: "+447504472444", prompt: "hi" },
|
|
106
|
+
})).rejects.toThrow(/cross-account/);
|
|
87
107
|
});
|
|
88
108
|
it("persists agentChannel/agentDestination/agentPrompt for a valid destination", async () => {
|
|
89
109
|
// spaced form must validate against the stored bare adminPhones entry
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schedule-event-agent.test.js","sourceRoot":"","sources":["../../../src/tools/__tests__/schedule-event-agent.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAEvE,0EAA0E;AAC1E,gEAAgE;AAChE,MAAM,UAAU,GAAmC,EAAE,CAAC;AAEtD,EAAE,CAAC,IAAI,CAAC,wBAAwB,EAAE,GAAG,EAAE,CAAC,CAAC;IACvC,gBAAgB,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,EAAc,CAAC;CACpD,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,oBAAoB,EAAE,GAAG,EAAE,CAAC,CAAC;IACnC,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;QACjB,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,iBAAiB,EAAE,CAAC,EAAE,CAAC,CAAC;QACzE,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,GAAE,CAAC,CAAC;KAC7B,CAAC;CACH,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,iDAAiD,EAAE,GAAG,EAAE,CAAC,CAAC;IAChE,kBAAkB,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE,KAAK,EAAsC,EAAE,EAAE;QAChF,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC,CAAC;CACH,CAAC,CAAC,CAAC;AAEJ,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,UAAU,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC;AACjF,IAAI,YAAoB,CAAC;AACzB,MAAM,UAAU,GAAG,
|
|
1
|
+
{"version":3,"file":"schedule-event-agent.test.js","sourceRoot":"","sources":["../../../src/tools/__tests__/schedule-event-agent.test.ts"],"names":[],"mappings":"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"}
|
package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-update-agent.test.js
CHANGED
|
@@ -18,11 +18,13 @@ vi.mock("../../lib/neo4j.js", () => ({
|
|
|
18
18
|
}));
|
|
19
19
|
import { scheduleUpdate } from "../schedule-update.js";
|
|
20
20
|
let platformRoot;
|
|
21
|
-
|
|
21
|
+
// UUID-named house dir carrying role:"house" — the validator resolves the house
|
|
22
|
+
// via account-enumeration, which requires this shape.
|
|
23
|
+
const ACCOUNT_ID = "11111111-1111-4111-8111-111111111111";
|
|
22
24
|
function seedAccount(waConfig) {
|
|
23
25
|
const accountDir = join(platformRoot, "..", "data", "accounts", ACCOUNT_ID);
|
|
24
26
|
mkdirSync(accountDir, { recursive: true });
|
|
25
|
-
writeFileSync(join(accountDir, "account.json"), JSON.stringify({ whatsapp: waConfig }), "utf-8");
|
|
27
|
+
writeFileSync(join(accountDir, "account.json"), JSON.stringify({ role: "house", whatsapp: waConfig }), "utf-8");
|
|
26
28
|
}
|
|
27
29
|
function lastSet() {
|
|
28
30
|
return runCalls[runCalls.length - 1];
|
|
@@ -69,13 +71,13 @@ describe("scheduleUpdate — agentDispatch", () => {
|
|
|
69
71
|
agentDispatch: { channel: "whatsapp", destination: "+447504472444", prompt: "hi" },
|
|
70
72
|
})).rejects.toThrow(/both/i);
|
|
71
73
|
});
|
|
72
|
-
it("rejects a destination that is
|
|
74
|
+
it("rejects a destination that is in no house list (not-registered)", async () => {
|
|
73
75
|
seedAccount({ adminPhones: ["+447504472444"] });
|
|
74
76
|
await expect(scheduleUpdate({
|
|
75
77
|
accountId: ACCOUNT_ID,
|
|
76
78
|
eventId: "e1",
|
|
77
79
|
agentDispatch: { channel: "whatsapp", destination: "+447999999999", prompt: "hi" },
|
|
78
|
-
})).rejects.toThrow(/
|
|
80
|
+
})).rejects.toThrow(/not-registered/);
|
|
79
81
|
});
|
|
80
82
|
});
|
|
81
83
|
//# sourceMappingURL=schedule-update-agent.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schedule-update-agent.test.js","sourceRoot":"","sources":["../../../src/tools/__tests__/schedule-update-agent.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,sEAAsE;AACtE,MAAM,QAAQ,GAA8D,EAAE,CAAC;AAE/E,EAAE,CAAC,IAAI,CAAC,wBAAwB,EAAE,GAAG,EAAE,CAAC,CAAC;IACvC,gBAAgB,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,EAAc,CAAC;CACpD,CAAC,CAAC,CAAC;AAEJ,EAAE,CAAC,IAAI,CAAC,oBAAoB,EAAE,GAAG,EAAE,CAAC,CAAC;IACnC,UAAU,EAAE,GAAG,EAAE,CAAC,CAAC;QACjB,GAAG,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,KAAa,EAAE,MAA+B,EAAE,EAAE;YAClE,QAAQ,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;YACjC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;QAC5C,CAAC,CAAC;QACF,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,GAAE,CAAC,CAAC;KAC7B,CAAC;CACH,CAAC,CAAC,CAAC;AAEJ,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD,IAAI,YAAoB,CAAC;AACzB,MAAM,UAAU,GAAG,
|
|
1
|
+
{"version":3,"file":"schedule-update-agent.test.js","sourceRoot":"","sources":["../../../src/tools/__tests__/schedule-update-agent.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -115,6 +115,8 @@ The platform enforces this at multiple levels:
|
|
|
115
115
|
|
|
116
116
|
**The scheduler path fails closed too.** A scheduled dispatch (`POST /api/channel/schedule-inject`) reaches the same spawn machine without going through the gate, resolving its own effective account via `effectiveAccountFor`. That resolver carries the same single-source + fail-closed shape: a non-manager destination scopes to the house (an owner/admin's real scope), a valid manager scopes to the bound sub-account, and a manager whose bound sub-account is not a valid account resolves to nothing — the route **rejects** (`op=schedule-account-manager-reject … reason=unresolved-effective-account`, HTTP 403, no spawn, no reply), never routing to the house. There is no `?? accountId` fallback on this path either. Telegram scheduled dispatch is house-only by construction (no account-manager routing), so there is nothing to fail closed there.
|
|
117
117
|
|
|
118
|
+
**Schedule-time destination validation is a distinct, earlier layer.** The `effectiveAccountFor` resolution above runs at *fire* time to scope the spawn. Separately, when a schedule is *created or updated* (and re-checked every heartbeat by the standing audit), the scheduler validates the WhatsApp `agentDispatch` destination against the same house authority. It reads the authoritative `adminPhones` + `accountManagers` from the socket-owning **house** account — never the event's own `account.json`, whose manager map is inert — via the one shared `validateAgentDestination`. The destination's registered account (the house for an admin, the bound sub-account for a manager) must equal the account that created the schedule; the house/owner may target any registered admin or manager, a sub-account is locked to its own manager(s). A refusal names the true cause: `not-registered` (in no house list) or `cross-account destinationAccount=… scheduleAccount=…` (registered to a different account). Create-time and fire-time emit the identical reason codes, so a schedule that passes creation but later drifts is explained rather than silently dropped. This closes the bug where the validator read a sub-account's own (empty) manager map and refused a genuinely registered manager whose binding lives on the house.
|
|
119
|
+
|
|
118
120
|
**Socket-key resolution — the reply transport.** The account id that names a *spawn scope* (a platform account UUID) is a different identifier from the key the *socket* is stored under (the credential dirname — `"default"` on a legacy single-account install that was never re-keyed to the UUID). These coincide when the credential dir is named for the UUID and diverge otherwise. The inbound reply path never trips on this: it closes over the live connection object directly. A scheduled dispatch has no live connection to close over, so it resolves the socket by asking the manager — `resolveSocket(accountId)` accepts **either** the platform UUID or the connection map key and returns the one connection that owns the socket (the manager holds the `accountId ↔ platformAccountId` bijection). This is the single resolver both the reply path and the scheduler go through, so the id `getSocket` is called with is always a key the connection map actually holds. A null resolution is **two distinct faults, never conflated**: `op=socket-key-miss accountId=… presentKeys=…` means no connection is registered under that id — an account-keying mismatch, the socket may be alive under a different key, and re-pairing is the wrong advice; a *disconnected* result means the connection exists but its transport is down. Reporting a key miss as "WhatsApp disconnected" is the bug that once told an operator to re-sign-in a connected line.
|
|
119
121
|
|
|
120
122
|
**`dmPolicy` behaviour (per-account):**
|
package/payload/server/server.js
CHANGED
|
@@ -10520,6 +10520,7 @@ import { dirname as dirname8, join as join23 } from "path";
|
|
|
10520
10520
|
|
|
10521
10521
|
// app/lib/claude-keychain.ts
|
|
10522
10522
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
10523
|
+
import { userInfo } from "os";
|
|
10523
10524
|
var CREDS_SERVICE_PREFIX = "Claude Code-credentials";
|
|
10524
10525
|
function parseKeychainMdats(dumpOutput) {
|
|
10525
10526
|
const out = {};
|
|
@@ -10607,18 +10608,35 @@ function decodeKeychainBlob(raw) {
|
|
|
10607
10608
|
}
|
|
10608
10609
|
return raw;
|
|
10609
10610
|
}
|
|
10610
|
-
function
|
|
10611
|
+
function readOnce(args) {
|
|
10611
10612
|
try {
|
|
10612
|
-
const raw = execFileSync2("security", ["find-generic-password", "-w",
|
|
10613
|
+
const raw = execFileSync2("security", ["find-generic-password", "-w", ...args], {
|
|
10613
10614
|
encoding: "utf-8",
|
|
10614
10615
|
timeout: 1e4
|
|
10615
10616
|
}).trim();
|
|
10616
|
-
|
|
10617
|
-
return decodeKeychainBlob(raw);
|
|
10617
|
+
return raw.length > 0 ? decodeKeychainBlob(raw) : null;
|
|
10618
10618
|
} catch {
|
|
10619
10619
|
return null;
|
|
10620
10620
|
}
|
|
10621
10621
|
}
|
|
10622
|
+
function credentialReadAttempts(service, username) {
|
|
10623
|
+
const attempts = [];
|
|
10624
|
+
if (username) attempts.push(["-s", service, "-a", username]);
|
|
10625
|
+
attempts.push(["-s", service]);
|
|
10626
|
+
return attempts;
|
|
10627
|
+
}
|
|
10628
|
+
function readHostCredsBlob(service) {
|
|
10629
|
+
let username = "";
|
|
10630
|
+
try {
|
|
10631
|
+
username = userInfo().username;
|
|
10632
|
+
} catch {
|
|
10633
|
+
}
|
|
10634
|
+
for (const args of credentialReadAttempts(service, username)) {
|
|
10635
|
+
const blob = readOnce(args);
|
|
10636
|
+
if (blob !== null) return blob;
|
|
10637
|
+
}
|
|
10638
|
+
return null;
|
|
10639
|
+
}
|
|
10622
10640
|
|
|
10623
10641
|
// ../lib/admins-write/src/index.ts
|
|
10624
10642
|
import { existsSync as existsSync14, readFileSync as readFileSync23, writeFileSync as writeFileSync9, renameSync as renameSync6, mkdirSync as mkdirSync5, readdirSync as readdirSync13, statSync as statSync8, appendFileSync as appendFileSync2 } from "fs";
|