@rubytech/create-sitedesk-code 0.1.551 → 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.
Files changed (82) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +5 -0
  3. package/payload/platform/plugins/scheduling/PLUGIN.md +22 -0
  4. package/payload/platform/plugins/scheduling/mcp/dist/index.js +107 -6
  5. package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
  6. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/connector-freebusy.test.d.ts +2 -0
  7. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/connector-freebusy.test.d.ts.map +1 -0
  8. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/connector-freebusy.test.js +216 -0
  9. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/connector-freebusy.test.js.map +1 -0
  10. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate-audits.test.d.ts +2 -0
  11. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate-audits.test.d.ts.map +1 -0
  12. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate-audits.test.js +128 -0
  13. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate-audits.test.js.map +1 -0
  14. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate.test.d.ts +2 -0
  15. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate.test.d.ts.map +1 -0
  16. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate.test.js +135 -0
  17. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate.test.js.map +1 -0
  18. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/schedule-audit.test.js +132 -0
  19. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/schedule-audit.test.js.map +1 -1
  20. package/payload/platform/plugins/scheduling/mcp/dist/lib/connector-freebusy.d.ts +86 -0
  21. package/payload/platform/plugins/scheduling/mcp/dist/lib/connector-freebusy.d.ts.map +1 -0
  22. package/payload/platform/plugins/scheduling/mcp/dist/lib/connector-freebusy.js +240 -0
  23. package/payload/platform/plugins/scheduling/mcp/dist/lib/connector-freebusy.js.map +1 -0
  24. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.d.ts +92 -0
  25. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.d.ts.map +1 -0
  26. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.js +193 -0
  27. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.js.map +1 -0
  28. package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.d.ts +8 -1
  29. package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.d.ts.map +1 -1
  30. package/payload/platform/plugins/scheduling/mcp/dist/lib/routine-run.js.map +1 -1
  31. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.d.ts +94 -1
  32. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.d.ts.map +1 -1
  33. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.js +299 -0
  34. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.js.map +1 -1
  35. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/gate-dispatch-wiring.test.d.ts +2 -0
  36. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/gate-dispatch-wiring.test.d.ts.map +1 -0
  37. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/gate-dispatch-wiring.test.js +146 -0
  38. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/gate-dispatch-wiring.test.js.map +1 -0
  39. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts +27 -0
  40. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts.map +1 -1
  41. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js +18 -0
  42. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js.map +1 -1
  43. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +119 -64
  44. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
  45. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-connector-busy-sync.test.js +84 -1
  46. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-connector-busy-sync.test.js.map +1 -1
  47. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-gate-write.test.d.ts +2 -0
  48. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-gate-write.test.d.ts.map +1 -0
  49. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-gate-write.test.js +218 -0
  50. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-gate-write.test.js.map +1 -0
  51. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/scheduling-gate.test.d.ts +2 -0
  52. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/scheduling-gate.test.d.ts.map +1 -0
  53. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/scheduling-gate.test.js +291 -0
  54. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/scheduling-gate.test.js.map +1 -0
  55. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-connector-busy-sync.d.ts +26 -2
  56. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-connector-busy-sync.d.ts.map +1 -1
  57. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-connector-busy-sync.js +51 -2
  58. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-connector-busy-sync.js.map +1 -1
  59. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts +31 -0
  60. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts.map +1 -1
  61. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js +44 -1
  62. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js.map +1 -1
  63. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.d.ts +3 -1
  64. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.d.ts.map +1 -1
  65. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js +57 -3
  66. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js.map +1 -1
  67. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.d.ts +148 -0
  68. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.d.ts.map +1 -0
  69. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.js +410 -0
  70. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.js.map +1 -0
  71. package/payload/platform/plugins/scheduling/mcp/package.json +1 -0
  72. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  73. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +1 -0
  74. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  75. package/payload/platform/services/claude-session-manager/dist/pty-census.d.ts +51 -4
  76. package/payload/platform/services/claude-session-manager/dist/pty-census.d.ts.map +1 -1
  77. package/payload/platform/services/claude-session-manager/dist/pty-census.js +60 -10
  78. package/payload/platform/services/claude-session-manager/dist/pty-census.js.map +1 -1
  79. package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.d.ts +64 -1
  80. package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.d.ts.map +1 -1
  81. package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.js +189 -6
  82. package/payload/platform/services/claude-session-manager/dist/slice-memory-policy.js.map +1 -1
@@ -0,0 +1,128 @@
1
+ // @vitest-environment node
2
+ // Task 2378 — the two standing checks that bound the same 24-hour window from
3
+ // opposite ends: a gate that never passes, and a gate that never stops passing.
4
+ //
5
+ // Both emit on every tick including zero. A check that goes quiet when healthy
6
+ // cannot be distinguished from one that has stopped running, so the zero case is
7
+ // asserted as hard as the alarm.
8
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
9
+ import { mkdtempSync, mkdirSync, writeFileSync } from "node:fs";
10
+ import { tmpdir } from "node:os";
11
+ import { join } from "node:path";
12
+ import { auditGateAlwaysSkipping, auditGateWatermarkAge } from "../schedule-audit.js";
13
+ import { watermarkPath } from "../../tools/scheduling-gate.js";
14
+ const ACCT = "20028fb5-0000-4000-8000-000000000000";
15
+ const NOW = Date.parse("2026-08-02T20:00:00.000Z");
16
+ let lines;
17
+ let platformRoot;
18
+ /** A driver returning one fixed record set, with the session close asserted. */
19
+ function driverOf(rows, throws = false) {
20
+ return {
21
+ session: () => ({
22
+ run: async () => {
23
+ if (throws)
24
+ throw new Error("neo4j down");
25
+ return { records: rows.map((r) => ({ get: (k) => r[k] })) };
26
+ },
27
+ close: async () => { },
28
+ }),
29
+ };
30
+ }
31
+ beforeEach(() => {
32
+ lines = [];
33
+ vi.spyOn(console, "error").mockImplementation((...args) => {
34
+ lines.push(args.join(" "));
35
+ });
36
+ const root = mkdtempSync(join(tmpdir(), "gate-audit-"));
37
+ platformRoot = join(root, "platform");
38
+ mkdirSync(platformRoot, { recursive: true });
39
+ });
40
+ afterEach(() => vi.restoreAllMocks());
41
+ function seedMarks(eventId, marks) {
42
+ const path = watermarkPath(eventId, platformRoot);
43
+ mkdirSync(join(path, ".."), { recursive: true });
44
+ writeFileSync(path, JSON.stringify(marks));
45
+ }
46
+ describe("auditGateAlwaysSkipping", () => {
47
+ it("emits the census with checked=0 on an install holding no gated routines", async () => {
48
+ await auditGateAlwaysSkipping(driverOf([]), [ACCT], NOW);
49
+ expect(lines).toContain("[schedule-audit] op=gate-census checked=0 alwaysSkipping=0 ok=true");
50
+ });
51
+ it("emits nothing at all when there are no local accounts", async () => {
52
+ await auditGateAlwaysSkipping(driverOf([]), [], NOW);
53
+ expect(lines).toEqual([]);
54
+ });
55
+ it("a gate that skipped every one of 24 runs is reported", async () => {
56
+ await auditGateAlwaysSkipping(driverOf([{ eventId: "65ef758c-aaaa", accountId: ACCT, runs: 24, gatedRuns: 24 }]), [ACCT], NOW);
57
+ expect(lines[0]).toBe("[schedule-audit] op=gate-census checked=1 alwaysSkipping=1 ok=false");
58
+ expect(lines[1]).toContain("op=gate-always-skipping eventId=65ef758c");
59
+ expect(lines[1]).toContain("gatedRuns=24 sinceHours=24");
60
+ });
61
+ it("a gate that dispatched once in the window is healthy", async () => {
62
+ await auditGateAlwaysSkipping(driverOf([{ eventId: "65ef758c-aaaa", accountId: ACCT, runs: 24, gatedRuns: 23 }]), [ACCT], NOW);
63
+ expect(lines).toEqual(["[schedule-audit] op=gate-census checked=1 alwaysSkipping=0 ok=true"]);
64
+ });
65
+ it("under 24 runs is below the threshold, so a freshly bound gate is not accused", async () => {
66
+ await auditGateAlwaysSkipping(driverOf([{ eventId: "65ef758c-aaaa", accountId: ACCT, runs: 23, gatedRuns: 23 }]), [ACCT], NOW);
67
+ expect(lines).toEqual(["[schedule-audit] op=gate-census checked=1 alwaysSkipping=0 ok=true"]);
68
+ });
69
+ it("normalises Neo4j integer objects rather than comparing them as objects", async () => {
70
+ const int = (n) => ({ toNumber: () => n });
71
+ await auditGateAlwaysSkipping(driverOf([{ eventId: "65ef758c-aaaa", accountId: ACCT, runs: int(30), gatedRuns: int(30) }]), [ACCT], NOW);
72
+ expect(lines[0]).toContain("alwaysSkipping=1 ok=false");
73
+ });
74
+ it("a query failure reports itself instead of a false green", async () => {
75
+ await auditGateAlwaysSkipping(driverOf([], true), [ACCT], NOW);
76
+ expect(lines.join("\n")).toContain("op=gate-census-error");
77
+ expect(lines.join("\n")).not.toContain("ok=true");
78
+ });
79
+ it("never throws", async () => {
80
+ await expect(auditGateAlwaysSkipping(driverOf([], true), [ACCT], NOW)).resolves.toBeUndefined();
81
+ });
82
+ });
83
+ describe("auditGateWatermarkAge", () => {
84
+ it("emits a line per account even when nothing is gated", async () => {
85
+ await auditGateWatermarkAge(driverOf([]), [ACCT], platformRoot, NOW);
86
+ expect(lines).toEqual([
87
+ "[schedule-audit] op=gate-watermark-age acct=20028fb5 lastCommitAgeHours=- ok=true",
88
+ ]);
89
+ });
90
+ it("a recent commit is ok", async () => {
91
+ seedMarks("e-1", { lastCommitAt: "2026-08-02T19:00:00.000Z" });
92
+ await auditGateWatermarkAge(driverOf([{ eventId: "e-1", accountId: ACCT }]), [ACCT], platformRoot, NOW);
93
+ expect(lines[0]).toContain("lastCommitAgeHours=1 ok=true");
94
+ });
95
+ it("a commit older than 24 hours is not ok", async () => {
96
+ seedMarks("e-1", { lastCommitAt: "2026-07-31T19:00:00.000Z" });
97
+ await auditGateWatermarkAge(driverOf([{ eventId: "e-1", accountId: ACCT }]), [ACCT], platformRoot, NOW);
98
+ expect(lines[0]).toContain("lastCommitAgeHours=49 ok=false");
99
+ });
100
+ it("a routine that has fired and never committed reads never, not ok", async () => {
101
+ // Never having committed is the state this check exists to find, so it is
102
+ // maximally late rather than exempt.
103
+ await auditGateWatermarkAge(driverOf([{ eventId: "e-never", accountId: ACCT }]), [ACCT], platformRoot, NOW);
104
+ expect(lines[0]).toContain("lastCommitAgeHours=never ok=false");
105
+ });
106
+ it("reports the WORST age on an account, so one blind gate is not hidden by a healthy sibling", async () => {
107
+ seedMarks("e-1", { lastCommitAt: "2026-08-02T19:00:00.000Z" });
108
+ seedMarks("e-2", { lastCommitAt: "2026-07-30T19:00:00.000Z" });
109
+ await auditGateWatermarkAge(driverOf([
110
+ { eventId: "e-1", accountId: ACCT },
111
+ { eventId: "e-2", accountId: ACCT },
112
+ ]), [ACCT], platformRoot, NOW);
113
+ expect(lines[0]).toContain("lastCommitAgeHours=73 ok=false");
114
+ });
115
+ it("an unparseable lastCommitAt is treated as never, not as zero", async () => {
116
+ seedMarks("e-1", { lastCommitAt: "not a date" });
117
+ await auditGateWatermarkAge(driverOf([{ eventId: "e-1", accountId: ACCT }]), [ACCT], platformRoot, NOW);
118
+ expect(lines[0]).toContain("lastCommitAgeHours=never ok=false");
119
+ });
120
+ it("a query failure reports itself", async () => {
121
+ await auditGateWatermarkAge(driverOf([], true), [ACCT], platformRoot, NOW);
122
+ expect(lines.join("\n")).toContain("op=gate-watermark-age-error");
123
+ });
124
+ it("never throws", async () => {
125
+ await expect(auditGateWatermarkAge(driverOf([], true), [ACCT], platformRoot, NOW)).resolves.toBeUndefined();
126
+ });
127
+ });
128
+ //# sourceMappingURL=gate-audits.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate-audits.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/gate-audits.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=gate.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.test.d.ts","sourceRoot":"","sources":["../../../src/lib/__tests__/gate.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,135 @@
1
+ // @vitest-environment node
2
+ // Task 2378 — the gate decision.
3
+ //
4
+ // Fail-open is the branch most likely to be inverted by a later edit, and
5
+ // inverting it silently mutes a routine that reads healthy on every surface, so
6
+ // every unreadable outcome gets its own test rather than sharing one.
7
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
8
+ import { evaluateGate, PluginToolError, } from "../gate.js";
9
+ import { chooseGate } from "../../scripts/agent-turn-dispatch.js";
10
+ const GATE = { plugin: "scheduling", tool: "scheduling-gate", args: '{"eventId":"e1"}' };
11
+ const EVENT = "65ef758c-0000-4000-8000-000000000000";
12
+ const ACCOUNT = "20028fb5-0000-4000-8000-000000000000";
13
+ const ok = (structuredContent) => ({
14
+ result: { content: [], structuredContent },
15
+ childStderr: "",
16
+ });
17
+ let lines;
18
+ beforeEach(() => {
19
+ lines = [];
20
+ vi.spyOn(console, "error").mockImplementation((...args) => {
21
+ lines.push(args.join(" "));
22
+ });
23
+ });
24
+ afterEach(() => vi.restoreAllMocks());
25
+ describe("chooseGate", () => {
26
+ it("resolves a gate carrying plugin and tool", () => {
27
+ expect(chooseGate({ gatePlugin: "scheduling", gateTool: "scheduling-gate", gateArgs: "{}" })).toEqual({
28
+ plugin: "scheduling",
29
+ tool: "scheduling-gate",
30
+ args: "{}",
31
+ });
32
+ });
33
+ it("resolves a gate with no args — a check taking no arguments is legitimate", () => {
34
+ expect(chooseGate({ gatePlugin: "scheduling", gateTool: "scheduling-gate", gateArgs: null })).toEqual({
35
+ plugin: "scheduling",
36
+ tool: "scheduling-gate",
37
+ args: null,
38
+ });
39
+ });
40
+ it.each([
41
+ ["plugin only", { gatePlugin: "scheduling", gateTool: null, gateArgs: null }],
42
+ ["tool only", { gatePlugin: null, gateTool: "scheduling-gate", gateArgs: null }],
43
+ ["args only", { gatePlugin: null, gateTool: null, gateArgs: "{}" }],
44
+ ["nothing", { gatePlugin: null, gateTool: null, gateArgs: null }],
45
+ ])("is null on a partial gate: %s", (_label, fields) => {
46
+ expect(chooseGate(fields)).toBeNull();
47
+ });
48
+ });
49
+ describe("evaluateGate", () => {
50
+ it("hasWork true dispatches and logs op=evaluated", async () => {
51
+ const call = async () => ok({ hasWork: true, reasons: ["d1"] });
52
+ expect(await evaluateGate(GATE, EVENT, ACCOUNT, call)).toEqual({ kind: "has-work" });
53
+ expect(lines.join("\n")).toContain("[schedule-gate] op=evaluated");
54
+ expect(lines.join("\n")).toContain("hasWork=true");
55
+ });
56
+ it("hasWork false skips and logs op=evaluated", async () => {
57
+ const call = async () => ok({ hasWork: false, reasons: [] });
58
+ expect(await evaluateGate(GATE, EVENT, ACCOUNT, call)).toEqual({ kind: "no-work" });
59
+ expect(lines.join("\n")).toContain("hasWork=false");
60
+ });
61
+ it("passes the stored gateArgs to the tool verbatim", async () => {
62
+ const seen = [];
63
+ const call = async (p, t, a, acct) => {
64
+ seen.push([p, t, a, acct]);
65
+ return ok({ hasWork: false });
66
+ };
67
+ await evaluateGate(GATE, EVENT, ACCOUNT, call);
68
+ expect(seen[0]).toEqual(["scheduling", "scheduling-gate", '{"eventId":"e1"}', ACCOUNT]);
69
+ });
70
+ it("fails open when the call throws", async () => {
71
+ const call = async () => {
72
+ throw new PluginToolError("boom", "error", "");
73
+ };
74
+ expect(await evaluateGate(GATE, EVENT, ACCOUNT, call)).toMatchObject({
75
+ kind: "fail-open",
76
+ reason: "error",
77
+ });
78
+ expect(lines.join("\n")).toContain("op=fail-open");
79
+ expect(lines.join("\n")).not.toContain("op=evaluated");
80
+ });
81
+ it("fails open with reason=timeout when the call timed out", async () => {
82
+ const call = async () => {
83
+ throw new PluginToolError("closed", "timeout", "");
84
+ };
85
+ expect(await evaluateGate(GATE, EVENT, ACCOUNT, call)).toMatchObject({
86
+ kind: "fail-open",
87
+ reason: "timeout",
88
+ });
89
+ expect(lines.join("\n")).toContain("reason=timeout");
90
+ });
91
+ it("fails open when the tool returns text but no structuredContent", async () => {
92
+ const call = async () => ok(undefined);
93
+ expect(await evaluateGate(GATE, EVENT, ACCOUNT, call)).toMatchObject({
94
+ kind: "fail-open",
95
+ reason: "no-structured-result",
96
+ });
97
+ });
98
+ it("fails open when hasWork is a string — a truthiness read would skip every fire", async () => {
99
+ const call = async () => ok({ hasWork: "false" });
100
+ expect(await evaluateGate(GATE, EVENT, ACCOUNT, call)).toMatchObject({
101
+ kind: "fail-open",
102
+ reason: "no-structured-result",
103
+ });
104
+ });
105
+ it("fails open when the tool reports isError, even carrying a structuredContent", async () => {
106
+ const call = async () => ({
107
+ result: { isError: true, structuredContent: { hasWork: false } },
108
+ childStderr: "",
109
+ });
110
+ expect(await evaluateGate(GATE, EVENT, ACCOUNT, call)).toMatchObject({
111
+ kind: "fail-open",
112
+ reason: "error",
113
+ });
114
+ });
115
+ it("never throws — an unexpected non-Error rejection still fails open", async () => {
116
+ const call = async () => {
117
+ throw "a bare string";
118
+ };
119
+ await expect(evaluateGate(GATE, EVENT, ACCOUNT, call)).resolves.toMatchObject({
120
+ kind: "fail-open",
121
+ });
122
+ });
123
+ it("emits exactly one line per evaluation, never both op= tags", async () => {
124
+ for (const call of [
125
+ (async () => ok({ hasWork: true })),
126
+ (async () => ok(undefined)),
127
+ ]) {
128
+ lines = [];
129
+ await evaluateGate(GATE, EVENT, ACCOUNT, call);
130
+ const tagged = lines.filter((l) => l.includes("[schedule-gate]"));
131
+ expect(tagged).toHaveLength(1);
132
+ }
133
+ });
134
+ });
135
+ //# sourceMappingURL=gate.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gate.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/gate.test.ts"],"names":[],"mappings":"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"}
@@ -45,4 +45,136 @@ describe("auditScheduleVisibility (Task 2100)", () => {
45
45
  expect(list).toHaveBeenCalledWith({ accountId: "acct-1", limit: 11 });
46
46
  });
47
47
  });
48
+ // Task 2381. The connector that silently resolves to `none`: a revoked
49
+ // authorisation makes every fire sync an empty busy list, the action succeeds,
50
+ // the RoutineRun closes clean, and the booking page offers slots the operator is
51
+ // not free for.
52
+ import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, existsSync } from "node:fs";
53
+ import { tmpdir } from "node:os";
54
+ import { join } from "node:path";
55
+ import { auditConnectorResolution, nextConnectorState, noneForHours, } from "../schedule-audit.js";
56
+ const T0 = Date.parse("2026-08-02T00:00:00.000Z");
57
+ const HOUR = 3_600_000;
58
+ describe("nextConnectorState", () => {
59
+ it("starts the none clock on the first none observation", () => {
60
+ expect(nextConnectorState(undefined, "none", T0)).toEqual({ connector: "none", noneSinceMs: T0 });
61
+ });
62
+ it("keeps the original none instant across consecutive none observations", () => {
63
+ const first = nextConnectorState(undefined, "none", T0);
64
+ expect(nextConnectorState(first, "none", T0 + HOUR).noneSinceMs).toBe(T0);
65
+ });
66
+ it("clears the none clock when a connector comes back", () => {
67
+ const first = nextConnectorState(undefined, "none", T0);
68
+ expect(nextConnectorState(first, "google", T0 + HOUR)).toEqual({
69
+ connector: "google",
70
+ noneSinceMs: null,
71
+ });
72
+ });
73
+ it("restarts the clock after a recovery, so the run length is never cumulative", () => {
74
+ const none1 = nextConnectorState(undefined, "none", T0);
75
+ const back = nextConnectorState(none1, "google", T0 + HOUR);
76
+ expect(nextConnectorState(back, "none", T0 + 2 * HOUR).noneSinceMs).toBe(T0 + 2 * HOUR);
77
+ });
78
+ });
79
+ describe("noneForHours", () => {
80
+ it("is 0 while a connector is resolved", () => {
81
+ expect(noneForHours({ connector: "google", noneSinceMs: null }, T0)).toBe(0);
82
+ });
83
+ it("counts whole hours since the none run began", () => {
84
+ expect(noneForHours({ connector: "none", noneSinceMs: T0 }, T0 + 25 * HOUR)).toBe(25);
85
+ });
86
+ });
87
+ /** An accounts directory whose named accounts carry the given connectors. */
88
+ function accountsDir(spec) {
89
+ const dir = mkdtempSync(join(tmpdir(), "audit-accts-"));
90
+ for (const [accountId, connectors] of Object.entries(spec)) {
91
+ mkdirSync(join(dir, accountId), { recursive: true });
92
+ for (const c of connectors) {
93
+ const rel = c === "google" ? ["google", "accounts"] : ["outlook", "mailboxes"];
94
+ const idDir = join(dir, accountId, "secrets", ...rel, "id-1");
95
+ mkdirSync(idDir, { recursive: true });
96
+ writeFileSync(join(idDir, "tokens.enc"), "x");
97
+ }
98
+ }
99
+ return dir;
100
+ }
101
+ /** A driver whose one query returns the accounts holding a scheduled busy-sync
102
+ * routine. */
103
+ function driverWithSyncRoutines(accountIds) {
104
+ const session = {
105
+ run: vi.fn(async () => ({
106
+ records: accountIds.map((id) => ({ get: () => id })),
107
+ })),
108
+ close: vi.fn(async () => { }),
109
+ };
110
+ return { driver: { session: () => session }, session };
111
+ }
112
+ describe("auditConnectorResolution", () => {
113
+ it("emits one line per local account per tick, including a healthy one", async () => {
114
+ const logs = [];
115
+ const spy = vi.spyOn(console, "error").mockImplementation((m) => void logs.push(String(m)));
116
+ const { driver } = driverWithSyncRoutines(["acct-1", "acct-2"]);
117
+ const statePath = join(mkdtempSync(join(tmpdir(), "audit-state-")), "s.json");
118
+ await auditConnectorResolution(driver, ["acct-1", "acct-2"], accountsDir({ "acct-1": ["google"], "acct-2": [] }), statePath, T0);
119
+ spy.mockRestore();
120
+ const lines = logs.filter((l) => l.includes("op=connector-resolution"));
121
+ expect(lines).toHaveLength(2);
122
+ expect(lines[0]).toContain("connector=google");
123
+ expect(lines[0]).toContain("noneForHours=0");
124
+ expect(lines[0]).toContain("ok=true");
125
+ // A fresh `none` is not yet an alarm — it is one tick old, not 24 hours.
126
+ expect(lines[1]).toContain("connector=none");
127
+ expect(lines[1]).toContain("ok=true");
128
+ });
129
+ it("reports ok=false once an account has resolved none for over 24 hours", async () => {
130
+ const dir = accountsDir({ "acct-1": [] });
131
+ const statePath = join(mkdtempSync(join(tmpdir(), "audit-state-")), "s.json");
132
+ const { driver } = driverWithSyncRoutines(["acct-1"]);
133
+ await auditConnectorResolution(driver, ["acct-1"], dir, statePath, T0);
134
+ const logs = [];
135
+ const spy = vi.spyOn(console, "error").mockImplementation((m) => void logs.push(String(m)));
136
+ await auditConnectorResolution(driver, ["acct-1"], dir, statePath, T0 + 25 * HOUR);
137
+ spy.mockRestore();
138
+ const line = logs.find((l) => l.includes("op=connector-resolution"));
139
+ expect(line).toContain("noneForHours=25");
140
+ expect(line).toContain("ok=false");
141
+ });
142
+ it("does not alarm on an account with no scheduled busy-sync routine", async () => {
143
+ const dir = accountsDir({ "acct-1": [] });
144
+ const statePath = join(mkdtempSync(join(tmpdir(), "audit-state-")), "s.json");
145
+ const { driver } = driverWithSyncRoutines([]);
146
+ await auditConnectorResolution(driver, ["acct-1"], dir, statePath, T0);
147
+ const logs = [];
148
+ const spy = vi.spyOn(console, "error").mockImplementation((m) => void logs.push(String(m)));
149
+ await auditConnectorResolution(driver, ["acct-1"], dir, statePath, T0 + 25 * HOUR);
150
+ spy.mockRestore();
151
+ const line = logs.find((l) => l.includes("op=connector-resolution"));
152
+ expect(line).toContain("noneForHours=25");
153
+ expect(line).toContain("ok=true");
154
+ });
155
+ it("persists state so the run length survives the next heartbeat process", async () => {
156
+ const dir = accountsDir({ "acct-1": [] });
157
+ const statePath = join(mkdtempSync(join(tmpdir(), "audit-state-")), "s.json");
158
+ const { driver } = driverWithSyncRoutines(["acct-1"]);
159
+ await auditConnectorResolution(driver, ["acct-1"], dir, statePath, T0);
160
+ expect(existsSync(statePath)).toBe(true);
161
+ expect(JSON.parse(readFileSync(statePath, "utf-8"))["acct-1"]).toEqual({
162
+ connector: "none",
163
+ noneSinceMs: T0,
164
+ });
165
+ });
166
+ it("never throws — a failed query costs one line, not the tick", async () => {
167
+ const session = {
168
+ run: vi.fn(async () => {
169
+ throw new Error("neo4j down");
170
+ }),
171
+ close: vi.fn(async () => { }),
172
+ };
173
+ const logs = [];
174
+ const spy = vi.spyOn(console, "error").mockImplementation((m) => void logs.push(String(m)));
175
+ await expect(auditConnectorResolution({ session: () => session }, ["acct-1"], accountsDir({ "acct-1": [] }), join(mkdtempSync(join(tmpdir(), "audit-state-")), "s.json"), T0)).resolves.toBeUndefined();
176
+ spy.mockRestore();
177
+ expect(logs.some((l) => l.includes("op=connector-resolution-error"))).toBe(true);
178
+ });
179
+ });
48
180
  //# sourceMappingURL=schedule-audit.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"schedule-audit.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/schedule-audit.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schedule-audit.test.js","sourceRoot":"","sources":["../../../src/lib/__tests__/schedule-audit.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ import { type CalendarConnector } from "./connector-sync-provision.js";
2
+ export type ConnectorChoice = CalendarConnector | "none";
3
+ export interface ResolvedConnector {
4
+ connector: ConnectorChoice;
5
+ /** Named on the resolution log line when two connectors are authorised and one
6
+ * is unused. Null when there is nothing to skip. */
7
+ skipped: CalendarConnector | null;
8
+ }
9
+ /**
10
+ * The account's calendar connector, decided rather than chosen by a model.
11
+ *
12
+ * `detectCalendarConnectors` returns a sorted list, so google precedes m365 and
13
+ * the pick is deterministic. The stored `connector` is a single enum; an account
14
+ * holding both is real, and the skipped one is named on the resolution line so an
15
+ * ignored calendar is visible rather than silent.
16
+ */
17
+ export declare function resolveConnector(accountDir: string): ResolvedConnector;
18
+ /**
19
+ * The environment a provider MCP needs, assembled for the action rail.
20
+ *
21
+ * The heartbeat's cron line sets only `PLATFORM_ROOT`, `NEO4J_URI` and
22
+ * `MAXY_UI_INTERNAL_PORT` (create-maxy-code/src/cron-registration.ts), so a child
23
+ * spawned from it inherits none of the per-plugin credentials a session-manager
24
+ * spawn would stamp from the `PLUGIN.md` `mcp.env` block. They are read from
25
+ * `brand.json` instead, which is the same source those placeholders resolve from.
26
+ *
27
+ * A missing client id throws. Returning an env without one would let the provider
28
+ * boot, refuse the call for want of a vendor app, and be indistinguishable
29
+ * downstream from an account whose calendar is genuinely empty.
30
+ */
31
+ export declare function providerEnv(platformRoot: string, accountId: string, connector: CalendarConnector): Record<string, string>;
32
+ export interface BusyInterval {
33
+ start: string;
34
+ end: string;
35
+ }
36
+ /**
37
+ * Calls one tool on one provider plugin and returns its parsed payload.
38
+ *
39
+ * Injected rather than imported so the read path is testable without spawning a
40
+ * child process; `spawnProviderCall` below is the real implementation.
41
+ */
42
+ export type ProviderCall = (plugin: "google" | "outlook", tool: string, args: Record<string, unknown>) => Promise<unknown>;
43
+ export interface ReadBusyOptions {
44
+ connector: CalendarConnector;
45
+ platformRoot: string;
46
+ accountId: string;
47
+ /** Window start, canonical UTC ISO. */
48
+ from: string;
49
+ /** Window end, canonical UTC ISO. */
50
+ to: string;
51
+ call: ProviderCall;
52
+ }
53
+ /**
54
+ * Graph returns `scheduleItems[].start.dateTime` with a sibling `timeZone` field
55
+ * that the outlook plugin's tool drops, so what arrives here carries no zone
56
+ * designator. The window is requested in UTC, so a zone-less instant is UTC and
57
+ * is stamped as such. A string already carrying `Z` or an offset is left alone —
58
+ * re-stamping it would move the interval.
59
+ */
60
+ export declare function normaliseGraphInstant(dateTime: string): string;
61
+ /**
62
+ * The account's busy intervals over the window, read from its own connector.
63
+ *
64
+ * Every failure throws. Returning `[]` on a failed read would clear the stored
65
+ * busy set and publish the operator's booked time as free, and a booking page
66
+ * serving its last successful write looks correct either way — so an empty list
67
+ * must only ever mean the calendar is genuinely free.
68
+ */
69
+ export declare function readConnectorBusy(opts: ReadBusyOptions): Promise<BusyInterval[]>;
70
+ /**
71
+ * A `ProviderCall` that reaches the provider plugin's own MCP server.
72
+ *
73
+ * The transport is `callPluginTool`, the one Task 2378 extracted from
74
+ * `dispatchAction`. Sharing it rather than writing a second one is the point:
75
+ * this call is spawned from the same heartbeat, so it needs the same timeout,
76
+ * the same `ACCOUNT_ID` scoping, the same captured child stderr, and above all
77
+ * the same `PLATFORM_PORT` bridge — the cron line does not export that name and
78
+ * every plugin resolves its loopback base URL from it, so a transport without it
79
+ * throws on port resolution before the tool runs.
80
+ *
81
+ * It is a spawn rather than an import because no plugin in this tree imports
82
+ * another plugin's source, and because the provider owns the decryption of its
83
+ * own tokens.
84
+ */
85
+ export declare function spawnProviderCall(platformRoot: string, accountId: string, connector: CalendarConnector): ProviderCall;
86
+ //# sourceMappingURL=connector-freebusy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connector-freebusy.d.ts","sourceRoot":"","sources":["../../src/lib/connector-freebusy.ts"],"names":[],"mappings":"AAiBA,OAAO,EAA4B,KAAK,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAGjG,MAAM,MAAM,eAAe,GAAG,iBAAiB,GAAG,MAAM,CAAC;AAEzD,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,eAAe,CAAC;IAC3B;yDACqD;IACrD,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAAC;CACnC;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,iBAAiB,CAItE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CACzB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,iBAAiB,GAC3B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CA2BxB;AAMD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,CACzB,MAAM,EAAE,QAAQ,GAAG,SAAS,EAC5B,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC1B,OAAO,CAAC,OAAO,CAAC,CAAC;AAEtB,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,iBAAiB,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,YAAY,CAAC;CACpB;AAoCD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG9D;AAsED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAGtF;AAMD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,iBAAiB,GAC3B,YAAY,CA4Bd"}