@rubytech/create-sitedesk-code 0.1.550 → 0.1.552
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/lib/agent-dispatch-rule/dist/index.d.ts +22 -8
- package/payload/platform/lib/agent-dispatch-rule/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/agent-dispatch-rule/dist/index.js +47 -13
- package/payload/platform/lib/agent-dispatch-rule/dist/index.js.map +1 -1
- package/payload/platform/lib/agent-dispatch-rule/src/__tests__/rule.test.ts +68 -9
- package/payload/platform/lib/agent-dispatch-rule/src/index.ts +60 -16
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +3 -3
- package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +9 -0
- package/payload/platform/plugins/docs/references/admin-ui.md +2 -2
- package/payload/platform/plugins/scheduling/PLUGIN.md +22 -0
- package/payload/platform/plugins/scheduling/mcp/dist/index.js +107 -6
- package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/agent-dispatch.test.js +2 -2
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/agent-dispatch.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/connector-freebusy.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/connector-freebusy.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/connector-freebusy.test.js +216 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/connector-freebusy.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/connector-sync-provision.test.js +2 -2
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/connector-sync-provision.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate-audits.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate-audits.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate-audits.test.js +128 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate-audits.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate.test.js +135 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/schedule-audit.test.js +132 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/schedule-audit.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/connector-freebusy.d.ts +86 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/connector-freebusy.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/connector-freebusy.js +240 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/connector-freebusy.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/connector-sync-provision.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/connector-sync-provision.js +21 -8
- package/payload/platform/plugins/scheduling/mcp/dist/lib/connector-sync-provision.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.d.ts +92 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.js +193 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.d.ts +8 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.d.ts +94 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.js +299 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/gate-dispatch-wiring.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/gate-dispatch-wiring.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/gate-dispatch-wiring.test.js +146 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/gate-dispatch-wiring.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts +27 -0
- 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 +18 -0
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +119 -64
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-connector-busy-sync.test.js +84 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-connector-busy-sync.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-gate-write.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-gate-write.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-gate-write.test.js +218 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-gate-write.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/scheduling-gate.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/scheduling-gate.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/scheduling-gate.test.js +291 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/scheduling-gate.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-connector-busy-sync.d.ts +26 -2
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-connector-busy-sync.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-connector-busy-sync.js +51 -2
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-connector-busy-sync.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts +31 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js +44 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.d.ts +3 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js +57 -3
- package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.d.ts +148 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.js +410 -0
- package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/package.json +1 -0
- package/payload/platform/plugins/telegram/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/telegram/PLUGIN.md +6 -4
- package/payload/platform/plugins/telegram/mcp/dist/index.js +62 -38
- package/payload/platform/plugins/telegram/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/lib/account-token.d.ts +18 -5
- package/payload/platform/plugins/telegram/mcp/dist/lib/account-token.d.ts.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/lib/account-token.js +34 -6
- package/payload/platform/plugins/telegram/mcp/dist/lib/account-token.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/lib/account-write.d.ts +25 -8
- package/payload/platform/plugins/telegram/mcp/dist/lib/account-write.d.ts.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/lib/account-write.js +28 -16
- package/payload/platform/plugins/telegram/mcp/dist/lib/account-write.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/lib/agent-active.d.ts +12 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/agent-active.d.ts.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/agent-active.js +26 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/agent-active.js.map +1 -0
- package/payload/platform/plugins/telegram/mcp/dist/lib/secret-path.d.ts +16 -13
- package/payload/platform/plugins/telegram/mcp/dist/lib/secret-path.d.ts.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/lib/secret-path.js +19 -23
- package/payload/platform/plugins/telegram/mcp/dist/lib/secret-path.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/lib/webhook-url.d.ts +9 -7
- package/payload/platform/plugins/telegram/mcp/dist/lib/webhook-url.d.ts.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/lib/webhook-url.js +11 -8
- package/payload/platform/plugins/telegram/mcp/dist/lib/webhook-url.js.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/tools/message.d.ts +4 -0
- package/payload/platform/plugins/telegram/mcp/dist/tools/message.d.ts.map +1 -1
- package/payload/platform/plugins/telegram/mcp/dist/tools/message.js +13 -3
- package/payload/platform/plugins/telegram/mcp/dist/tools/message.js.map +1 -1
- package/payload/platform/plugins/telegram/references/setup-guide.md +16 -8
- package/payload/platform/plugins/telegram/skills/configure/SKILL.md +30 -32
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +1 -0
- package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-census.d.ts +51 -4
- package/payload/platform/services/claude-session-manager/dist/pty-census.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/pty-census.js +60 -10
- package/payload/platform/services/claude-session-manager/dist/pty-census.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.d.ts +64 -1
- package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.js +189 -6
- package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.js.map +1 -1
- package/payload/server/server.js +510 -240
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Task 2378 — the precondition a gated routine calls before the model is
|
|
3
|
+
* invoked.
|
|
4
|
+
*
|
|
5
|
+
* One tool serves every routine because the dispatcher evaluates exactly one
|
|
6
|
+
* gate per event, and each binding names its own checks in `gateArgs`. Four
|
|
7
|
+
* check types, each drawn from what the surveyed routines already do the
|
|
8
|
+
* expensive way — after a full model turn had already been paid for.
|
|
9
|
+
*
|
|
10
|
+
* Every check reads a STRUCTURED source. None parses another tool's prose:
|
|
11
|
+
* deciding whether to spend a model turn by matching words in a third-party
|
|
12
|
+
* tool's wording is exactly what the plugin doctrine forbids, and it is why the
|
|
13
|
+
* D1 check reads the query envelope and the WhatsApp check reads the status
|
|
14
|
+
* JSON rather than either tool's rendered text.
|
|
15
|
+
*
|
|
16
|
+
* Direction of failure is uniform and deliberate: any check that errors, or
|
|
17
|
+
* whose source is unreachable, contributes `hasWork: true` and its own name in
|
|
18
|
+
* `reasons`. A gate that fails closed silently mutes a routine and reads healthy
|
|
19
|
+
* on every surface, which is the Task 2313 class this must not recreate.
|
|
20
|
+
*/
|
|
21
|
+
import { createHash } from "node:crypto";
|
|
22
|
+
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
23
|
+
import { dirname, resolve } from "node:path";
|
|
24
|
+
import { getSession } from "../lib/neo4j.js";
|
|
25
|
+
/**
|
|
26
|
+
* Rooted at the INSTALL, not at the account.
|
|
27
|
+
*
|
|
28
|
+
* `eventId` is a UUID, so the filename is already unique across accounts and no
|
|
29
|
+
* account segment adds anything. What an account segment would add is a way for
|
|
30
|
+
* the two callers to disagree: the gate runs under the dispatcher's
|
|
31
|
+
* `ACCOUNT_ID` (the event's account) and the commit runs inside the agent turn,
|
|
32
|
+
* whose account is resolved by the channel. A path that depended on that
|
|
33
|
+
* resolution would let the commit write one file while the next gate read
|
|
34
|
+
* another, and the symptom would be a routine that never stops reporting work.
|
|
35
|
+
*/
|
|
36
|
+
export function watermarkPath(eventId, platformRoot) {
|
|
37
|
+
return resolve(platformRoot, "..", "state", "scheduling-gate", `${eventId}.json`);
|
|
38
|
+
}
|
|
39
|
+
export function readWatermarks(eventId, platformRoot) {
|
|
40
|
+
try {
|
|
41
|
+
const raw = readFileSync(watermarkPath(eventId, platformRoot), "utf-8");
|
|
42
|
+
const parsed = JSON.parse(raw);
|
|
43
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// A first run has no file, and an unreadable one must not be a hard error:
|
|
47
|
+
// an empty watermark set makes every watermarked check report work, which
|
|
48
|
+
// is the fail-open direction the whole tool holds to.
|
|
49
|
+
return {};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function writeWatermarks(eventId, platformRoot, marks) {
|
|
53
|
+
const path = watermarkPath(eventId, platformRoot);
|
|
54
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
55
|
+
writeFileSync(path, JSON.stringify(marks, null, 2));
|
|
56
|
+
}
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// Real sources
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
function loopbackBase() {
|
|
61
|
+
// PLATFORM_PORT is what every plugin resolves its loopback base from. The
|
|
62
|
+
// dispatcher stamps it from MAXY_UI_INTERNAL_PORT when the cron env carries
|
|
63
|
+
// only the latter (lib/gate.ts), so both names are accepted here rather than
|
|
64
|
+
// making this tool the one place that disagrees.
|
|
65
|
+
const port = process.env.PLATFORM_PORT ?? process.env.MAXY_UI_INTERNAL_PORT;
|
|
66
|
+
if (!port) {
|
|
67
|
+
throw new Error("neither PLATFORM_PORT nor MAXY_UI_INTERNAL_PORT is set — no loopback base URL");
|
|
68
|
+
}
|
|
69
|
+
return `http://127.0.0.1:${port}`;
|
|
70
|
+
}
|
|
71
|
+
async function getJson(path, accountId) {
|
|
72
|
+
const res = await fetch(`${loopbackBase()}${path}`, {
|
|
73
|
+
headers: { "x-maxy-caller-account": accountId },
|
|
74
|
+
});
|
|
75
|
+
if (!res.ok)
|
|
76
|
+
throw new Error(`HTTP ${res.status} on GET ${path}`);
|
|
77
|
+
return (await res.json());
|
|
78
|
+
}
|
|
79
|
+
async function postJson(path, accountId, body) {
|
|
80
|
+
const res = await fetch(`${loopbackBase()}${path}`, {
|
|
81
|
+
method: "POST",
|
|
82
|
+
headers: { "Content-Type": "application/json", "x-maxy-caller-account": accountId },
|
|
83
|
+
body: JSON.stringify(body),
|
|
84
|
+
});
|
|
85
|
+
if (!res.ok)
|
|
86
|
+
throw new Error(`HTTP ${res.status} on POST ${path}`);
|
|
87
|
+
return (await res.json());
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* D1: the first column of the first row of a single count query.
|
|
91
|
+
*
|
|
92
|
+
* The envelope is `[{results: [...]}]`, unwrapped the same way
|
|
93
|
+
* `parsePendingRows` (ui/server/portal-uploads-pull.ts:458) does, because both
|
|
94
|
+
* read the same `d1Query` return. Getting the shape wrong yields zero rows,
|
|
95
|
+
* which is indistinguishable from an empty result and would make this check
|
|
96
|
+
* report "no work" for ever — so an envelope this cannot understand throws and
|
|
97
|
+
* is counted as work rather than parsed optimistically.
|
|
98
|
+
*/
|
|
99
|
+
export function parseD1Count(payload) {
|
|
100
|
+
const result = payload?.result;
|
|
101
|
+
const envelope = Array.isArray(result) ? result[0] : null;
|
|
102
|
+
const rows = envelope?.results;
|
|
103
|
+
if (!Array.isArray(rows)) {
|
|
104
|
+
throw new Error("unexpected d1 payload: envelope carries no results array");
|
|
105
|
+
}
|
|
106
|
+
if (rows.length === 0)
|
|
107
|
+
return 0;
|
|
108
|
+
const first = rows[0];
|
|
109
|
+
const values = Object.values(first);
|
|
110
|
+
if (values.length === 0)
|
|
111
|
+
return 0;
|
|
112
|
+
const n = Number(values[0]);
|
|
113
|
+
if (Number.isNaN(n))
|
|
114
|
+
throw new Error(`d1 first column is not a number: ${String(values[0])}`);
|
|
115
|
+
return n;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* The calendar digest.
|
|
119
|
+
*
|
|
120
|
+
* The task specified a Google sync token. No sync token exists anywhere in this
|
|
121
|
+
* tree — `plugins/google/mcp/src/tools/calendar-list.ts` queries with `timeMin`
|
|
122
|
+
* and stores nothing — so there is no token to compare. What DOES exist, inside
|
|
123
|
+
* this plugin's own graph, is the `:ConnectorBusy` set that
|
|
124
|
+
* `schedule-connector-busy-sync` wholesale-replaces on every connector sync. A
|
|
125
|
+
* digest over that set changes exactly when the connector calendar changes,
|
|
126
|
+
* which is the signal the sync token was wanted for, and it needs no credential
|
|
127
|
+
* and no cross-plugin call.
|
|
128
|
+
*
|
|
129
|
+
* The connector name is inside the digest so that re-authorising against a
|
|
130
|
+
* different provider reads as a change even when the busy blocks happen to
|
|
131
|
+
* coincide.
|
|
132
|
+
*/
|
|
133
|
+
export function digestBusy(connector, intervals) {
|
|
134
|
+
const sorted = [...intervals].sort((a, b) => a.start === b.start ? a.end.localeCompare(b.end) : a.start.localeCompare(b.start));
|
|
135
|
+
const body = sorted.map((iv) => `${iv.start}/${iv.end}`).join(",");
|
|
136
|
+
return createHash("sha256").update(`${connector}\n${body}`).digest("hex");
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* IMAP config for a mailbox, read from the `:EmailAccount` node the email plugin
|
|
140
|
+
* writes at setup. Read rather than imported: pulling the email plugin's
|
|
141
|
+
* `credentials.ts` across the plugin boundary would make scheduling's build
|
|
142
|
+
* depend on email's, and the node is the same source that module reads.
|
|
143
|
+
*/
|
|
144
|
+
async function readMailboxConfig(accountId, mailbox) {
|
|
145
|
+
const session = getSession();
|
|
146
|
+
try {
|
|
147
|
+
const res = await session.run(`MATCH (e:EmailAccount {accountId: $accountId, email: $mailbox})
|
|
148
|
+
RETURN e.imapHost AS host, e.imapPort AS port, e.imapSecurity AS security LIMIT 1`, { accountId, mailbox });
|
|
149
|
+
if (res.records.length === 0)
|
|
150
|
+
return null;
|
|
151
|
+
const r = res.records[0];
|
|
152
|
+
const rawPort = r.get("port");
|
|
153
|
+
const port = typeof rawPort === "number"
|
|
154
|
+
? rawPort
|
|
155
|
+
: rawPort && typeof rawPort.toNumber === "function"
|
|
156
|
+
? rawPort.toNumber()
|
|
157
|
+
: Number(rawPort ?? 993);
|
|
158
|
+
const host = r.get("host");
|
|
159
|
+
if (!host)
|
|
160
|
+
return null;
|
|
161
|
+
return { host, port, secure: r.get("security") === "tls" };
|
|
162
|
+
}
|
|
163
|
+
finally {
|
|
164
|
+
await session.close();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/** The brand-rooted store the email plugin writes at `config/email-credentials.json`. */
|
|
168
|
+
function readMailboxPassword(mailbox) {
|
|
169
|
+
const path = resolve(process.env.PLATFORM_ROOT, "config", "email-credentials.json");
|
|
170
|
+
const store = JSON.parse(readFileSync(path, "utf-8"));
|
|
171
|
+
const pass = store[mailbox];
|
|
172
|
+
if (!pass || pass.trim() === "") {
|
|
173
|
+
throw new Error(`no stored credential for ${mailbox} at ${path}`);
|
|
174
|
+
}
|
|
175
|
+
return pass;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* The mail probe: is there a UID above the watermark, optionally with a matching
|
|
179
|
+
* subject.
|
|
180
|
+
*
|
|
181
|
+
* It opens IMAP itself rather than reading `(:EmailAccount).lastFetchedUid`,
|
|
182
|
+
* which the email plugin already maintains. That watermark is advanced by the
|
|
183
|
+
* agent's own `email-fetch` calls, so a gate reading it would mute itself
|
|
184
|
+
* permanently at its first skip: no turn, no fetch, no movement, no work ever
|
|
185
|
+
* reported again. The probe has to be independent of the thing it gates.
|
|
186
|
+
*/
|
|
187
|
+
const realMail = async (check, sinceUid, accountId) => {
|
|
188
|
+
const config = await readMailboxConfig(accountId, check.mailbox);
|
|
189
|
+
if (!config)
|
|
190
|
+
throw new Error(`no :EmailAccount node for ${check.mailbox}`);
|
|
191
|
+
const password = readMailboxPassword(check.mailbox);
|
|
192
|
+
const { ImapFlow } = await import("imapflow");
|
|
193
|
+
const client = new ImapFlow({
|
|
194
|
+
host: config.host,
|
|
195
|
+
port: config.port,
|
|
196
|
+
secure: config.secure,
|
|
197
|
+
auth: { user: check.mailbox, pass: password },
|
|
198
|
+
logger: false,
|
|
199
|
+
tls: { rejectUnauthorized: true },
|
|
200
|
+
connectionTimeout: 30_000,
|
|
201
|
+
greetingTimeout: 30_000,
|
|
202
|
+
socketTimeout: 30_000,
|
|
203
|
+
});
|
|
204
|
+
await client.connect();
|
|
205
|
+
try {
|
|
206
|
+
const lock = await client.getMailboxLock(check.folder ?? "INBOX");
|
|
207
|
+
try {
|
|
208
|
+
const box = client.mailbox;
|
|
209
|
+
const uidValidity = box && typeof box !== "boolean" ? Number(box.uidValidity) : null;
|
|
210
|
+
// `UID SEARCH UID <n+1>:*` is the whole probe. Nothing is fetched: the
|
|
211
|
+
// question is only whether anything arrived, and a body fetch here would
|
|
212
|
+
// cost what the gate exists to save.
|
|
213
|
+
const uids = await client.search({ uid: `${sinceUid + 1}:*` }, { uid: true });
|
|
214
|
+
let matched = Array.isArray(uids) ? uids : [];
|
|
215
|
+
if (check.subjectPrefixes && check.subjectPrefixes.length > 0 && matched.length > 0) {
|
|
216
|
+
const perPrefix = await Promise.all(check.subjectPrefixes.map((p) => client.search({ uid: `${sinceUid + 1}:*`, header: { subject: p } }, { uid: true })));
|
|
217
|
+
const union = new Set();
|
|
218
|
+
for (const set of perPrefix)
|
|
219
|
+
for (const u of Array.isArray(set) ? set : [])
|
|
220
|
+
union.add(u);
|
|
221
|
+
matched = [...union];
|
|
222
|
+
}
|
|
223
|
+
// The highest UID SEEN, not the highest MATCHED: committing a matched-only
|
|
224
|
+
// high-water mark would re-examine every non-matching message on the next
|
|
225
|
+
// fire for ever, and committing below what was seen would replay them.
|
|
226
|
+
const seen = Array.isArray(uids) ? uids : [];
|
|
227
|
+
const highestUid = seen.length > 0 ? Math.max(...seen) : sinceUid;
|
|
228
|
+
return { matched: matched.length > 0, highestUid, uidValidity };
|
|
229
|
+
}
|
|
230
|
+
finally {
|
|
231
|
+
lock.release();
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
finally {
|
|
235
|
+
try {
|
|
236
|
+
await client.logout();
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
try {
|
|
240
|
+
client.close();
|
|
241
|
+
}
|
|
242
|
+
catch {
|
|
243
|
+
// best effort
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
export const REAL_SOURCES = {
|
|
249
|
+
mail: realMail,
|
|
250
|
+
d1: async (check, accountId) => {
|
|
251
|
+
const body = await postJson("/api/storage/d1/query", accountId, {
|
|
252
|
+
name: check.database,
|
|
253
|
+
sql: check.sql,
|
|
254
|
+
});
|
|
255
|
+
if (typeof body.error === "string")
|
|
256
|
+
throw new Error(body.error);
|
|
257
|
+
return parseD1Count(body);
|
|
258
|
+
},
|
|
259
|
+
whatsappStatus: async (accountId) => {
|
|
260
|
+
const body = await getJson("/api/whatsapp/status", accountId);
|
|
261
|
+
const accounts = Array.isArray(body.accounts) ? body.accounts : [];
|
|
262
|
+
// Connected means EVERY linked account's socket is up. An install with one
|
|
263
|
+
// disconnected socket among several has a channel that cannot deliver, and
|
|
264
|
+
// reporting that as connected is the case the check exists to catch.
|
|
265
|
+
const connected = accounts.length > 0 && accounts.every((a) => a.connected === true);
|
|
266
|
+
return { connected };
|
|
267
|
+
},
|
|
268
|
+
whatsappInbound: async (accountId, sinceEpochSec) => {
|
|
269
|
+
const convBody = await getJson("/api/whatsapp/conversations", accountId);
|
|
270
|
+
const conversations = Array.isArray(convBody.conversations) ? convBody.conversations : [];
|
|
271
|
+
let latest = sinceEpochSec;
|
|
272
|
+
let newer = false;
|
|
273
|
+
// The conversation list carries the last message of each thread whichever
|
|
274
|
+
// direction it went, so it narrows the candidate set and cannot answer on
|
|
275
|
+
// its own — our own outbound reply moves the same field. Only threads that
|
|
276
|
+
// moved are opened, so a quiet quarter-hour costs exactly one request.
|
|
277
|
+
for (const conv of conversations) {
|
|
278
|
+
const c = conv;
|
|
279
|
+
const ts = typeof c.lastMessageTimestamp === "number" ? c.lastMessageTimestamp : 0;
|
|
280
|
+
if (typeof c.jid !== "string" || ts <= sinceEpochSec)
|
|
281
|
+
continue;
|
|
282
|
+
const msgBody = await getJson(`/api/whatsapp/messages?jid=${encodeURIComponent(c.jid)}&limit=50`, accountId);
|
|
283
|
+
const messages = Array.isArray(msgBody.messages) ? msgBody.messages : [];
|
|
284
|
+
for (const m of messages) {
|
|
285
|
+
const msg = m;
|
|
286
|
+
const mts = typeof msg.timestamp === "number" ? msg.timestamp : 0;
|
|
287
|
+
if (mts > latest)
|
|
288
|
+
latest = mts;
|
|
289
|
+
if (msg.fromMe !== true && mts > sinceEpochSec)
|
|
290
|
+
newer = true;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
return { latest, newer };
|
|
294
|
+
},
|
|
295
|
+
calendarDigest: async (check, accountId) => {
|
|
296
|
+
const session = getSession();
|
|
297
|
+
try {
|
|
298
|
+
const res = await session.run(`MATCH (b:ConnectorBusy {accountId: $accountId})
|
|
299
|
+
RETURN toString(b.startsAt) AS start, toString(b.endsAt) AS end`, { accountId });
|
|
300
|
+
return digestBusy(check.connector, res.records.map((r) => ({ start: r.get("start"), end: r.get("end") })));
|
|
301
|
+
}
|
|
302
|
+
finally {
|
|
303
|
+
await session.close();
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
// ---------------------------------------------------------------------------
|
|
308
|
+
// Evaluation
|
|
309
|
+
// ---------------------------------------------------------------------------
|
|
310
|
+
async function probe(check, marks, accountId, sources) {
|
|
311
|
+
switch (check.type) {
|
|
312
|
+
case "mail": {
|
|
313
|
+
const key = `${check.mailbox}|${check.folder ?? "INBOX"}`;
|
|
314
|
+
const stored = marks.mail?.[key];
|
|
315
|
+
const since = stored?.uid ?? 0;
|
|
316
|
+
const res = await sources.mail(check, since, accountId);
|
|
317
|
+
// A UIDVALIDITY change means the server renumbered the folder, so the
|
|
318
|
+
// stored UID names a different message. Treating that as work is the only
|
|
319
|
+
// honest answer: the alternative is comparing against a number that no
|
|
320
|
+
// longer refers to anything.
|
|
321
|
+
const renumbered = stored != null && stored.uidValidity != null && res.uidValidity !== stored.uidValidity;
|
|
322
|
+
return {
|
|
323
|
+
hasWork: res.matched || renumbered,
|
|
324
|
+
mark: (m) => {
|
|
325
|
+
m.mail = { ...(m.mail ?? {}), [key]: { uid: res.highestUid, uidValidity: res.uidValidity } };
|
|
326
|
+
return key;
|
|
327
|
+
},
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
case "d1": {
|
|
331
|
+
const count = await sources.d1(check, accountId);
|
|
332
|
+
return { hasWork: count > 0, mark: null };
|
|
333
|
+
}
|
|
334
|
+
case "whatsapp": {
|
|
335
|
+
if (check.scope === "status") {
|
|
336
|
+
const { connected } = await sources.whatsappStatus(accountId);
|
|
337
|
+
return { hasWork: !connected, mark: null };
|
|
338
|
+
}
|
|
339
|
+
const since = marks.whatsapp?.inbound ?? 0;
|
|
340
|
+
const res = await sources.whatsappInbound(accountId, since);
|
|
341
|
+
return {
|
|
342
|
+
hasWork: res.newer,
|
|
343
|
+
mark: (m) => {
|
|
344
|
+
m.whatsapp = { ...(m.whatsapp ?? {}), inbound: res.latest };
|
|
345
|
+
return "whatsapp";
|
|
346
|
+
},
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
case "calendar": {
|
|
350
|
+
const digest = await sources.calendarDigest(check, accountId);
|
|
351
|
+
const stored = marks.calendar?.digest;
|
|
352
|
+
return {
|
|
353
|
+
hasWork: stored !== digest,
|
|
354
|
+
mark: (m) => {
|
|
355
|
+
m.calendar = { ...(m.calendar ?? {}), digest };
|
|
356
|
+
return "calendar";
|
|
357
|
+
},
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Run every check and answer whether the routine has work.
|
|
364
|
+
*
|
|
365
|
+
* `hasWork` is true when ANY check is true, which is what makes one tool serve
|
|
366
|
+
* every binding: a routine names the sources it cares about and the disjunction
|
|
367
|
+
* is the routine's precondition.
|
|
368
|
+
*
|
|
369
|
+
* On a commit, every named check is re-probed and its current position written.
|
|
370
|
+
* Re-probing rather than reusing an earlier read is the point — the commit runs
|
|
371
|
+
* at the END of the agent turn, and anything that arrived while the turn ran has
|
|
372
|
+
* not been seen by the model, so it must stay above the watermark.
|
|
373
|
+
*/
|
|
374
|
+
export async function runSchedulingGate(params, accountId, platformRoot, sources = REAL_SOURCES) {
|
|
375
|
+
const started = Date.now();
|
|
376
|
+
const marks = readWatermarks(params.eventId, platformRoot);
|
|
377
|
+
const reasons = [];
|
|
378
|
+
const advanced = [];
|
|
379
|
+
let hasWork = false;
|
|
380
|
+
for (const check of params.checks) {
|
|
381
|
+
let outcome;
|
|
382
|
+
try {
|
|
383
|
+
outcome = await probe(check, marks, accountId, sources);
|
|
384
|
+
}
|
|
385
|
+
catch (err) {
|
|
386
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
387
|
+
console.error(`[scheduling-gate] op=check-error kind=${check.type} detail=${detail} treatedAs=has-work`);
|
|
388
|
+
hasWork = true;
|
|
389
|
+
if (!reasons.includes(check.type))
|
|
390
|
+
reasons.push(check.type);
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
if (outcome.hasWork) {
|
|
394
|
+
hasWork = true;
|
|
395
|
+
if (!reasons.includes(check.type))
|
|
396
|
+
reasons.push(check.type);
|
|
397
|
+
}
|
|
398
|
+
if (params.commit && outcome.mark)
|
|
399
|
+
advanced.push(outcome.mark(marks));
|
|
400
|
+
}
|
|
401
|
+
if (params.commit) {
|
|
402
|
+
marks.lastCommitAt = new Date().toISOString();
|
|
403
|
+
writeWatermarks(params.eventId, platformRoot, marks);
|
|
404
|
+
console.error(`[scheduling-gate] op=commit eventId=${params.eventId.slice(0, 8)} advanced=${advanced.join(",")}`);
|
|
405
|
+
}
|
|
406
|
+
console.error(`[scheduling-gate] op=checked hasWork=${hasWork} reasons=${reasons.join(",")} ` +
|
|
407
|
+
`checks=${params.checks.length} ms=${Date.now() - started}`);
|
|
408
|
+
return params.commit ? { hasWork, reasons, advanced } : { hasWork, reasons };
|
|
409
|
+
}
|
|
410
|
+
//# sourceMappingURL=scheduling-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduling-gate.js","sourceRoot":"","sources":["../../src/tools/scheduling-gate.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "telegram",
|
|
3
|
-
"description": "Guide users through connecting
|
|
3
|
+
"description": "Guide users through connecting Telegram bots — creating a bot via BotFather, finding user IDs, and registering any number of bots, each with its own role, access policy and agent.",
|
|
4
4
|
"version": "0.1.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Rubytech LLC"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: telegram
|
|
3
|
-
description: Guide users through connecting
|
|
3
|
+
description: Guide users through connecting Telegram bots — creating a bot via BotFather, finding user IDs, and registering any number of bots, each with its own role, access policy and agent.
|
|
4
4
|
metadata: {"platform":{"optional":true}}
|
|
5
5
|
tools:
|
|
6
6
|
- name: message
|
|
@@ -44,14 +44,16 @@ Walks users through connecting Telegram bots. Covers bot creation, token configu
|
|
|
44
44
|
## What it unlocks
|
|
45
45
|
|
|
46
46
|
- Telegram as a messaging channel — DMs and group conversations routed to the AI assistant
|
|
47
|
-
-
|
|
47
|
+
- Any number of bots per account, each with its own role, its own access policy and its own agent
|
|
48
48
|
- Admin routing — restrict a bot to admin-only access via numeric user ID bindings
|
|
49
49
|
|
|
50
50
|
## Setup
|
|
51
51
|
|
|
52
|
-
Connecting a Telegram bot runs through the `telegram:configure` skill (`skills/configure/SKILL.md`) — the brand-aware, PTY-only setup flow. It is the maxy `telegram:configure`; on `-code` brands the upstream `claude-plugins-official/telegram` plugin is not installed, so there is no shadowing. The skill stores the BotFather token under the brand install, registers the webhook on the `/api/telegram/` edge path, sets access policy
|
|
52
|
+
Connecting a Telegram bot runs through the `telegram:configure` skill (`skills/configure/SKILL.md`) — the brand-aware, PTY-only setup flow. It is the maxy `telegram:configure`; on `-code` brands the upstream `claude-plugins-official/telegram` plugin is not installed, so there is no shadowing. The skill stores the BotFather token under the brand install, registers the webhook on the `/api/telegram/` edge path, and sets the bot's role, access policy and agent.
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
An account holds `telegram.bots[]`, an array keyed on each bot's own Telegram id (the token's `:`-prefix). Each entry carries its own `token`, an explicit `role` (`admin` or `public`, never derived from a slug, because it selects the spawn path), the policy for that bot alone, and — on a public entry only — the `agent` it answers as. An admin entry names no agent: it reads `agents/admin` by construction. `telegram-webhook-register` derives the bot id from the token, stamps `?bot=<botId>` onto the registered URL, and replaces only the entry with that id, so registering one bot cannot disturb another in any account. Secrets live at `~/.<brand>/telegram-secrets/<botId>` (directory 700, file 600).
|
|
55
|
+
|
|
56
|
+
A bot registered before the registry answers `reason=unknown-bot` until it is registered once more; the boot converter rewrites the old two-slot config in place but makes no outbound call, and the standing `[telegram-audit]` line names every bot that has not re-registered.
|
|
55
57
|
|
|
56
58
|
## References
|
|
57
59
|
|