@rubytech/create-realagent-code 0.1.604 → 0.1.605

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 (53) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/routine-templates/dist/roster.d.ts +30 -2
  3. package/payload/platform/lib/routine-templates/dist/roster.d.ts.map +1 -1
  4. package/payload/platform/lib/routine-templates/dist/roster.js +76 -32
  5. package/payload/platform/lib/routine-templates/dist/roster.js.map +1 -1
  6. package/payload/platform/lib/routine-templates/src/__tests__/roster.test.ts +0 -0
  7. package/payload/platform/lib/routine-templates/src/__tests__/seed.test.ts +22 -1
  8. package/payload/platform/lib/routine-templates/src/roster.ts +108 -35
  9. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +3 -3
  10. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +5 -0
  11. package/payload/platform/plugins/connector/PLUGIN.md +9 -1
  12. package/payload/platform/plugins/connector/mcp/dist/__tests__/queue-descriptor.test.d.ts +2 -0
  13. package/payload/platform/plugins/connector/mcp/dist/__tests__/queue-descriptor.test.d.ts.map +1 -0
  14. package/payload/platform/plugins/connector/mcp/dist/__tests__/queue-descriptor.test.js +67 -0
  15. package/payload/platform/plugins/connector/mcp/dist/__tests__/queue-descriptor.test.js.map +1 -0
  16. package/payload/platform/plugins/connector/mcp/dist/index.js +36 -5
  17. package/payload/platform/plugins/connector/mcp/dist/index.js.map +1 -1
  18. package/payload/platform/plugins/connector/mcp/dist/lib/store.d.ts +35 -0
  19. package/payload/platform/plugins/connector/mcp/dist/lib/store.d.ts.map +1 -1
  20. package/payload/platform/plugins/connector/mcp/dist/lib/store.js +33 -0
  21. package/payload/platform/plugins/connector/mcp/dist/lib/store.js.map +1 -1
  22. package/payload/platform/plugins/connector/mcp/package.json +4 -2
  23. package/payload/platform/plugins/connector/mcp/vitest.config.ts +8 -0
  24. package/payload/platform/plugins/docs/references/admin-ui.md +2 -2
  25. package/payload/platform/plugins/email/PLUGIN.md +4 -0
  26. package/payload/platform/plugins/email/mcp/dist/sampler/__tests__/retention.test.d.ts +2 -0
  27. package/payload/platform/plugins/email/mcp/dist/sampler/__tests__/retention.test.d.ts.map +1 -0
  28. package/payload/platform/plugins/email/mcp/dist/sampler/__tests__/retention.test.js +233 -0
  29. package/payload/platform/plugins/email/mcp/dist/sampler/__tests__/retention.test.js.map +1 -0
  30. package/payload/platform/plugins/email/mcp/dist/sampler/main.js +19 -0
  31. package/payload/platform/plugins/email/mcp/dist/sampler/main.js.map +1 -1
  32. package/payload/platform/plugins/email/mcp/dist/sampler/retention-sources.d.ts +3 -0
  33. package/payload/platform/plugins/email/mcp/dist/sampler/retention-sources.d.ts.map +1 -0
  34. package/payload/platform/plugins/email/mcp/dist/sampler/retention-sources.js +146 -0
  35. package/payload/platform/plugins/email/mcp/dist/sampler/retention-sources.js.map +1 -0
  36. package/payload/platform/plugins/email/mcp/dist/sampler/retention.d.ts +150 -0
  37. package/payload/platform/plugins/email/mcp/dist/sampler/retention.d.ts.map +1 -0
  38. package/payload/platform/plugins/email/mcp/dist/sampler/retention.js +199 -0
  39. package/payload/platform/plugins/email/mcp/dist/sampler/retention.js.map +1 -0
  40. package/payload/platform/plugins/scheduling/PLUGIN.md +8 -1
  41. package/payload/platform/plugins/scheduling/mcp/dist/index.js +1 -1
  42. package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
  43. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-gate-write.test.js +40 -0
  44. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-gate-write.test.js.map +1 -1
  45. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/scheduling-gate.test.js +322 -4
  46. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/scheduling-gate.test.js.map +1 -1
  47. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts.map +1 -1
  48. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js +21 -1
  49. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js.map +1 -1
  50. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.d.ts +236 -6
  51. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.d.ts.map +1 -1
  52. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.js +452 -7
  53. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.js.map +1 -1
@@ -0,0 +1,233 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { resolveRetentionDays, cutoffIso, interestedSets, partitionAged, runRetention, sweepAccount, RETENTION_CANDIDATE_CAP, RETENTION_MAX_DAYS, } from "../retention.js";
3
+ describe("resolveRetentionDays", () => {
4
+ it("returns null when unset", () => {
5
+ expect(resolveRetentionDays({})).toBeNull();
6
+ });
7
+ it("returns null for zero, negative, fractional and unparseable values", () => {
8
+ for (const v of ["0", "-1", "30.5", "abc", "", " "]) {
9
+ expect(resolveRetentionDays({ EMAIL_RETENTION_DAYS: v })).toBeNull();
10
+ }
11
+ });
12
+ it("returns the positive integer day count", () => {
13
+ expect(resolveRetentionDays({ EMAIL_RETENTION_DAYS: "30" })).toBe(30);
14
+ });
15
+ it("refuses a period large enough to overflow the cutoff date", () => {
16
+ expect(resolveRetentionDays({ EMAIL_RETENTION_DAYS: "100000000000" })).toBeNull();
17
+ expect(resolveRetentionDays({ EMAIL_RETENTION_DAYS: String(RETENTION_MAX_DAYS + 1) })).toBeNull();
18
+ expect(resolveRetentionDays({ EMAIL_RETENTION_DAYS: String(RETENTION_MAX_DAYS) })).toBe(RETENTION_MAX_DAYS);
19
+ });
20
+ it("keeps every accepted period inside the range cutoffIso can express", () => {
21
+ expect(() => cutoffIso(new Date("2026-08-13T09:00:00.000Z"), RETENTION_MAX_DAYS)).not.toThrow();
22
+ });
23
+ });
24
+ describe("cutoffIso", () => {
25
+ it("subtracts whole days from the clock in the ISO shape createdAt uses", () => {
26
+ expect(cutoffIso(new Date("2026-08-13T09:00:00.000Z"), 30)).toBe("2026-07-14T09:00:00.000Z");
27
+ });
28
+ });
29
+ describe("interestedSets", () => {
30
+ const args = (checks) => JSON.stringify({ eventId: "ignored", checks });
31
+ it("splits routines by the label their graph-unflagged check names", () => {
32
+ const rows = [
33
+ { eventId: "ev-sec", gateArgs: args([{ type: "graph-unflagged", label: "Section" }]) },
34
+ { eventId: "ev-arc", gateArgs: args([{ type: "graph-unflagged", label: "ConversationArchive" }]) },
35
+ ];
36
+ expect(interestedSets(rows)).toEqual({ section: ["ev-sec"], archive: ["ev-arc"] });
37
+ });
38
+ it("puts a routine holding both checks in both sets", () => {
39
+ const rows = [
40
+ {
41
+ eventId: "ev-both",
42
+ gateArgs: args([
43
+ { type: "graph-unflagged", label: "Section" },
44
+ { type: "graph-unflagged", label: "ConversationArchive" },
45
+ ]),
46
+ },
47
+ ];
48
+ expect(interestedSets(rows)).toEqual({ section: ["ev-both"], archive: ["ev-both"] });
49
+ });
50
+ it("ignores checks of other types and labels outside the accepted pair", () => {
51
+ const rows = [
52
+ { eventId: "ev-mail", gateArgs: args([{ type: "mail", mailbox: "a@b.c" }]) },
53
+ { eventId: "ev-odd", gateArgs: args([{ type: "graph-unflagged", label: "Person" }]) },
54
+ ];
55
+ expect(interestedSets(rows)).toEqual({ section: [], archive: [] });
56
+ });
57
+ it("contributes nothing and does not throw on absent or malformed gateArgs", () => {
58
+ const rows = [
59
+ { eventId: "ev-null", gateArgs: null },
60
+ { eventId: "ev-bad", gateArgs: "{not json" },
61
+ { eventId: "ev-nochecks", gateArgs: JSON.stringify({ eventId: "x" }) },
62
+ { eventId: "ev-checksnotarray", gateArgs: JSON.stringify({ checks: "Section" }) },
63
+ ];
64
+ expect(interestedSets(rows)).toEqual({ section: [], archive: [] });
65
+ });
66
+ it("names a routine once per set even when it repeats the same check", () => {
67
+ const rows = [
68
+ {
69
+ eventId: "ev-dup",
70
+ gateArgs: args([
71
+ { type: "graph-unflagged", label: "Section" },
72
+ { type: "graph-unflagged", label: "Section" },
73
+ ]),
74
+ },
75
+ ];
76
+ expect(interestedSets(rows)).toEqual({ section: ["ev-dup"], archive: [] });
77
+ });
78
+ });
79
+ describe("partitionAged", () => {
80
+ const aged = (over = {}) => ({
81
+ id: "s1",
82
+ processedBy: [],
83
+ parentProcessedBy: [],
84
+ ...over,
85
+ });
86
+ it("prunes a section every interested routine has flagged", () => {
87
+ const rows = [aged({ processedBy: ["ev-a", "ev-b"], parentProcessedBy: ["ev-c"] })];
88
+ expect(partitionAged(rows, { section: ["ev-a", "ev-b"], archive: ["ev-c"] })).toEqual({
89
+ prunable: ["s1"],
90
+ keptOutstanding: 0,
91
+ });
92
+ });
93
+ it("keeps a section with one section-gated routine still outstanding", () => {
94
+ const rows = [aged({ processedBy: ["ev-a"] })];
95
+ expect(partitionAged(rows, { section: ["ev-a", "ev-b"], archive: [] })).toEqual({
96
+ prunable: [],
97
+ keptOutstanding: 1,
98
+ });
99
+ });
100
+ it("keeps a fully section-flagged node whose parent archive is outstanding", () => {
101
+ const rows = [aged({ processedBy: ["ev-a"], parentProcessedBy: [] })];
102
+ expect(partitionAged(rows, { section: ["ev-a"], archive: ["ev-c"] })).toEqual({
103
+ prunable: [],
104
+ keptOutstanding: 1,
105
+ });
106
+ });
107
+ it("keeps a section that has no parent archive", () => {
108
+ const rows = [aged({ processedBy: ["ev-a"], parentProcessedBy: null })];
109
+ expect(partitionAged(rows, { section: ["ev-a"], archive: [] })).toEqual({
110
+ prunable: [],
111
+ keptOutstanding: 1,
112
+ });
113
+ });
114
+ it("ignores a flag from a routine that is not interested", () => {
115
+ const rows = [aged({ processedBy: ["ev-a", "ev-stale"] })];
116
+ expect(partitionAged(rows, { section: ["ev-a"], archive: [] })).toEqual({
117
+ prunable: ["s1"],
118
+ keptOutstanding: 0,
119
+ });
120
+ });
121
+ });
122
+ const NOW = new Date("2026-08-13T09:00:00.000Z");
123
+ function deps(over = {}) {
124
+ return {
125
+ listEmailAccountIds: vi.fn(async () => ["acc-1"]),
126
+ listGateArgs: vi.fn(async () => []),
127
+ listAgedSections: vi.fn(async () => []),
128
+ pruneSections: vi.fn(async () => ({ repaired: 0, pruned: 0 })),
129
+ now: () => NOW,
130
+ log: vi.fn(),
131
+ ...over,
132
+ };
133
+ }
134
+ const lines = (d) => d.log.mock.calls.map((c) => c[0]);
135
+ const sectionGate = (eventId) => ({
136
+ eventId,
137
+ gateArgs: JSON.stringify({ checks: [{ type: "graph-unflagged", label: "Section" }] }),
138
+ });
139
+ describe("runRetention", () => {
140
+ it("prunes nothing and names the period unset when EMAIL_RETENTION_DAYS is absent", async () => {
141
+ const d = deps();
142
+ await runRetention(d, {});
143
+ expect(d.listEmailAccountIds).not.toHaveBeenCalled();
144
+ expect(lines(d)).toEqual([
145
+ "op=email-retention account=- interested=0 pruned=0 kept-outstanding=0 repaired=0 ageDays=unset",
146
+ ]);
147
+ });
148
+ it("sweeps every account holding email sections when a period is set", async () => {
149
+ const d = deps({ listEmailAccountIds: vi.fn(async () => ["acc-1", "acc-2"]) });
150
+ await runRetention(d, { EMAIL_RETENTION_DAYS: "30" });
151
+ expect(d.listGateArgs).toHaveBeenCalledTimes(2);
152
+ });
153
+ it("reports one account's failure and continues to the next", async () => {
154
+ const d = deps({
155
+ listEmailAccountIds: vi.fn(async () => ["acc-1", "acc-2"]),
156
+ listGateArgs: vi.fn(async (accountId) => {
157
+ if (accountId === "acc-1")
158
+ throw new Error("neo4j down");
159
+ return [];
160
+ }),
161
+ });
162
+ await runRetention(d, { EMAIL_RETENTION_DAYS: "30" });
163
+ expect(lines(d)[0]).toBe("op=email-retention-error account=acc-1 detail=neo4j down");
164
+ expect(lines(d)[1]).toBe("op=email-retention account=acc-2 interested=0 pruned=0 kept-outstanding=0 repaired=0 ageDays=30");
165
+ });
166
+ });
167
+ describe("sweepAccount", () => {
168
+ it("deletes nothing and reports interested=0 when no routine holds the check", async () => {
169
+ const d = deps({ listGateArgs: vi.fn(async () => [{ eventId: "ev-1", gateArgs: null }]) });
170
+ await sweepAccount(d, "acc-1", 30);
171
+ expect(d.listAgedSections).not.toHaveBeenCalled();
172
+ expect(d.pruneSections).not.toHaveBeenCalled();
173
+ expect(lines(d)).toEqual([
174
+ "op=email-retention account=acc-1 interested=0 pruned=0 kept-outstanding=0 repaired=0 ageDays=30",
175
+ ]);
176
+ });
177
+ it("prunes only the flagged section and reports every count", async () => {
178
+ const d = deps({
179
+ listGateArgs: vi.fn(async () => [sectionGate("ev-a")]),
180
+ listAgedSections: vi.fn(async () => [
181
+ { id: "s1", processedBy: ["ev-a"], parentProcessedBy: [] },
182
+ { id: "s2", processedBy: [], parentProcessedBy: [] },
183
+ ]),
184
+ pruneSections: vi.fn(async () => ({ repaired: 2, pruned: 1 })),
185
+ });
186
+ await sweepAccount(d, "acc-1", 30);
187
+ expect(d.listAgedSections.mock.calls[0]).toEqual([
188
+ "acc-1",
189
+ "2026-07-14T09:00:00.000Z",
190
+ RETENTION_CANDIDATE_CAP,
191
+ ]);
192
+ expect(d.pruneSections).toHaveBeenCalledWith("acc-1", ["s1"]);
193
+ expect(lines(d)).toEqual([
194
+ "op=email-retention account=acc-1 interested=1 pruned=1 kept-outstanding=1 repaired=2 ageDays=30",
195
+ ]);
196
+ });
197
+ it("reports repaired=0 beside a non-zero prune, the broken-chain signature", async () => {
198
+ const d = deps({
199
+ listGateArgs: vi.fn(async () => [sectionGate("ev-a")]),
200
+ listAgedSections: vi.fn(async () => [
201
+ { id: "s1", processedBy: ["ev-a"], parentProcessedBy: [] },
202
+ ]),
203
+ pruneSections: vi.fn(async () => ({ repaired: 0, pruned: 1 })),
204
+ });
205
+ await sweepAccount(d, "acc-1", 30);
206
+ expect(lines(d)[0]).toContain("pruned=1 kept-outstanding=0 repaired=0");
207
+ });
208
+ it("skips the repair and the delete when nothing is prunable", async () => {
209
+ const d = deps({
210
+ listGateArgs: vi.fn(async () => [sectionGate("ev-a")]),
211
+ listAgedSections: vi.fn(async () => [{ id: "s1", processedBy: [], parentProcessedBy: [] }]),
212
+ });
213
+ await sweepAccount(d, "acc-1", 30);
214
+ expect(d.pruneSections).not.toHaveBeenCalled();
215
+ expect(lines(d)).toEqual([
216
+ "op=email-retention account=acc-1 interested=1 pruned=0 kept-outstanding=1 repaired=0 ageDays=30",
217
+ ]);
218
+ });
219
+ it("names a capped candidate batch so a truncated sweep is never silent", async () => {
220
+ const rows = Array.from({ length: RETENTION_CANDIDATE_CAP }, (_, i) => ({
221
+ id: `s${i}`,
222
+ processedBy: [],
223
+ parentProcessedBy: [],
224
+ }));
225
+ const d = deps({
226
+ listGateArgs: vi.fn(async () => [sectionGate("ev-a")]),
227
+ listAgedSections: vi.fn(async () => rows),
228
+ });
229
+ await sweepAccount(d, "acc-1", 30);
230
+ expect(lines(d)[0]).toContain("capped=true");
231
+ });
232
+ });
233
+ //# sourceMappingURL=retention.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retention.test.js","sourceRoot":"","sources":["../../../src/sampler/__tests__/retention.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAClD,OAAO,EACL,oBAAoB,EACpB,SAAS,EACT,cAAc,EACd,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,uBAAuB,EACvB,kBAAkB,GAInB,MAAM,iBAAiB,CAAC;AAEzB,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,MAAM,CAAC,oBAAoB,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC5E,KAAK,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,CAAC;YACpD,MAAM,CAAC,oBAAoB,CAAC,EAAE,oBAAoB,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QACvE,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,CAAC,oBAAoB,CAAC,EAAE,oBAAoB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,MAAM,CAAC,oBAAoB,CAAC,EAAE,oBAAoB,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QAClF,MAAM,CAAC,oBAAoB,CAAC,EAAE,oBAAoB,EAAE,MAAM,CAAC,kBAAkB,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QAClG,MAAM,CAAC,oBAAoB,CAAC,EAAE,oBAAoB,EAAE,MAAM,CAAC,kBAAkB,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CACrF,kBAAkB,CACnB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC5E,MAAM,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,0BAA0B,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IAClG,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,MAAM,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC,0BAA0B,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;IAC/F,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,MAAM,IAAI,GAAG,CAAC,MAAiB,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC,CAAC;IAEnF,EAAE,CAAC,gEAAgE,EAAE,GAAG,EAAE;QACxE,MAAM,IAAI,GAAc;YACtB,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE;YACtF,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC,CAAC,EAAE;SACnG,CAAC;QACF,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACrF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;QACzD,MAAM,IAAI,GAAc;YACtB;gBACE,OAAO,EAAE,SAAS;gBAClB,QAAQ,EAAE,IAAI,CAAC;oBACb,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,SAAS,EAAE;oBAC7C,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,qBAAqB,EAAE;iBAC1D,CAAC;aACH;SACF,CAAC;QACF,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IACvF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oEAAoE,EAAE,GAAG,EAAE;QAC5E,MAAM,IAAI,GAAc;YACtB,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE;YAC5E,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE;SACtF,CAAC;QACF,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wEAAwE,EAAE,GAAG,EAAE;QAChF,MAAM,IAAI,GAAc;YACtB,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE;YACtC,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE;YAC5C,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE;YACtE,EAAE,OAAO,EAAE,mBAAmB,EAAE,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE;SAClF,CAAC;QACF,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,IAAI,GAAc;YACtB;gBACE,OAAO,EAAE,QAAQ;gBACjB,QAAQ,EAAE,IAAI,CAAC;oBACb,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,SAAS,EAAE;oBAC7C,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,SAAS,EAAE;iBAC9C,CAAC;aACH;SACF,CAAC;QACF,MAAM,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC,QAAQ,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;IAC7E,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,MAAM,IAAI,GAAG,CAAC,OAA6B,EAAE,EAAe,EAAE,CAAC,CAAC;QAC9D,EAAE,EAAE,IAAI;QACR,WAAW,EAAE,EAAE;QACf,iBAAiB,EAAE,EAAE;QACrB,GAAG,IAAI;KACR,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,iBAAiB,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QACpF,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;YACpF,QAAQ,EAAE,CAAC,IAAI,CAAC;YAChB,eAAe,EAAE,CAAC;SACnB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,GAAG,EAAE;QAC1E,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QAC/C,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;YAC9E,QAAQ,EAAE,EAAE;YACZ,eAAe,EAAE,CAAC;SACnB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wEAAwE,EAAE,GAAG,EAAE;QAChF,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,CAAC,MAAM,CAAC,EAAE,iBAAiB,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QACtE,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;YAC5E,QAAQ,EAAE,EAAE;YACZ,eAAe,EAAE,CAAC;SACnB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,CAAC,MAAM,CAAC,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACxE,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;YACtE,QAAQ,EAAE,EAAE;YACZ,eAAe,EAAE,CAAC;SACnB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE,CAAC,CAAC,CAAC;QAC3D,MAAM,CAAC,aAAa,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;YACtE,QAAQ,EAAE,CAAC,IAAI,CAAC;YAChB,eAAe,EAAE,CAAC;SACnB,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,MAAM,GAAG,GAAG,IAAI,IAAI,CAAC,0BAA0B,CAAC,CAAC;AAEjD,SAAS,IAAI,CAAC,OAA+B,EAAE;IAC7C,OAAO;QACL,mBAAmB,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;QACjD,YAAY,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC;QACnC,gBAAgB,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC;QACvC,aAAa,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;QAC9D,GAAG,EAAE,GAAG,EAAE,CAAC,GAAG;QACd,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE;QACZ,GAAG,IAAI;KACR,CAAC;AACJ,CAAC;AAED,MAAM,KAAK,GAAG,CAAC,CAAgB,EAAE,EAAE,CAAE,CAAC,CAAC,GAAW,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAW,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACzF,MAAM,WAAW,GAAG,CAAC,OAAe,EAAE,EAAE,CAAC,CAAC;IACxC,OAAO;IACP,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;CACtF,CAAC,CAAC;AAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,+EAA+E,EAAE,KAAK,IAAI,EAAE;QAC7F,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;QACjB,MAAM,YAAY,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC1B,MAAM,CAAC,CAAC,CAAC,mBAA0B,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QAC5D,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YACvB,gGAAgG;SACjG,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kEAAkE,EAAE,KAAK,IAAI,EAAE;QAChF,MAAM,CAAC,GAAG,IAAI,CAAC,EAAE,mBAAmB,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;QAC/E,MAAM,YAAY,CAAC,CAAC,EAAE,EAAE,oBAAoB,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,MAAM,CAAC,CAAC,CAAC,YAAmB,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,KAAK,IAAI,EAAE;QACvE,MAAM,CAAC,GAAG,IAAI,CAAC;YACb,mBAAmB,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YAC1D,YAAY,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,SAAiB,EAAE,EAAE;gBAC9C,IAAI,SAAS,KAAK,OAAO;oBAAE,MAAM,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC;gBACzD,OAAO,EAAE,CAAC;YACZ,CAAC,CAAC;SACH,CAAC,CAAC;QACH,MAAM,YAAY,CAAC,CAAC,EAAE,EAAE,oBAAoB,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,0DAA0D,CAAC,CAAC;QACrF,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CACtB,iGAAiG,CAClG,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,0EAA0E,EAAE,KAAK,IAAI,EAAE;QACxF,MAAM,CAAC,GAAG,IAAI,CAAC,EAAE,YAAY,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QAC3F,MAAM,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;QACnC,MAAM,CAAC,CAAC,CAAC,gBAAuB,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QACzD,MAAM,CAAC,CAAC,CAAC,aAAoB,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QACtD,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YACvB,iGAAiG;SAClG,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,KAAK,IAAI,EAAE;QACvE,MAAM,CAAC,GAAG,IAAI,CAAC;YACb,YAAY,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;YACtD,gBAAgB,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC;gBAClC,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,MAAM,CAAC,EAAE,iBAAiB,EAAE,EAAE,EAAE;gBAC1D,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,iBAAiB,EAAE,EAAE,EAAE;aACrD,CAAC;YACF,aAAa,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;SAC/D,CAAC,CAAC;QACH,MAAM,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;QACnC,MAAM,CAAE,CAAC,CAAC,gBAAwB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YACxD,OAAO;YACP,0BAA0B;YAC1B,uBAAuB;SACxB,CAAC,CAAC;QACH,MAAM,CAAC,CAAC,CAAC,aAAoB,CAAC,CAAC,oBAAoB,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;QACrE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YACvB,iGAAiG;SAClG,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wEAAwE,EAAE,KAAK,IAAI,EAAE;QACtF,MAAM,CAAC,GAAG,IAAI,CAAC;YACb,YAAY,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;YACtD,gBAAgB,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC;gBAClC,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,MAAM,CAAC,EAAE,iBAAiB,EAAE,EAAE,EAAE;aAC3D,CAAC;YACF,aAAa,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC;SAC/D,CAAC,CAAC;QACH,MAAM,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;QACnC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,wCAAwC,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0DAA0D,EAAE,KAAK,IAAI,EAAE;QACxE,MAAM,CAAC,GAAG,IAAI,CAAC;YACb,YAAY,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;YACtD,gBAAgB,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,iBAAiB,EAAE,EAAE,EAAE,CAAC,CAAC;SAC5F,CAAC,CAAC;QACH,MAAM,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;QACnC,MAAM,CAAC,CAAC,CAAC,aAAoB,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QACtD,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;YACvB,iGAAiG;SAClG,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,KAAK,IAAI,EAAE;QACnF,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,uBAAuB,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;YACtE,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,WAAW,EAAE,EAAc;YAC3B,iBAAiB,EAAE,EAAc;SAClC,CAAC,CAAC,CAAC;QACJ,MAAM,CAAC,GAAG,IAAI,CAAC;YACb,YAAY,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;YACtD,gBAAgB,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC;SAC1C,CAAC,CAAC;QACH,MAAM,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,CAAC,CAAC;QACnC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -5,6 +5,8 @@ import { fetchSinceUid, resolveDefaultRecipients } from "../lib/imap.js";
5
5
  import { dispatchEmailMessagesToConversationArchive } from "../lib/conversation-archive-dispatch.js";
6
6
  import { listAllImapAccounts, readSamplerCursor, advanceSamplerCursor } from "../lib/credentials.js";
7
7
  import { sampleMailbox } from "./sample-mailbox.js";
8
+ import { runRetention } from "./retention.js";
9
+ import { retentionSources } from "./retention-sources.js";
8
10
  // The standing email-sampler (Task 2682): a per-brand systemd user unit that
9
11
  // each cycle enumerates every IMAP mailbox on the install and writes new
10
12
  // inbound messages to the graph via the plugin's own dispatch, independent of
@@ -46,6 +48,23 @@ const deps = {
46
48
  async function cycle() {
47
49
  await imapArm();
48
50
  await outlookArm();
51
+ await retentionPass();
52
+ }
53
+ /**
54
+ * The retention sweep (Task 2685). Runs AFTER both arms so a cycle never deletes
55
+ * before it has written, and in its own try for the reason each arm has one: a
56
+ * retention failure must cost no ingest.
57
+ *
58
+ * `runRetention` reads `EMAIL_RETENTION_DAYS` itself and prunes nothing when it
59
+ * is unset, which is every install until an operator sets it.
60
+ */
61
+ async function retentionPass() {
62
+ try {
63
+ await runRetention(retentionSources(() => new Date(), logLine), process.env);
64
+ }
65
+ catch (e) {
66
+ logLine(`op=email-retention-error account=<sweep> detail=${e instanceof Error ? e.message : String(e)}`);
67
+ }
49
68
  }
50
69
  /** The IMAP arm (Task 2682). An enumeration failure ends this arm only; the
51
70
  * Outlook arm reads a different store and must still run. */
@@ -1 +1 @@
1
- {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/sampler/main.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/sampler/main.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { RetentionDeps } from "./retention.js";
2
+ export declare function retentionSources(now: () => Date, log: (line: string) => void): RetentionDeps;
3
+ //# sourceMappingURL=retention-sources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retention-sources.d.ts","sourceRoot":"","sources":["../../src/sampler/retention-sources.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAwB,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAuB1E,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,aAAa,CAkJ5F"}
@@ -0,0 +1,146 @@
1
+ import neo4j from "neo4j-driver";
2
+ import { getSession } from "../lib/neo4j.js";
3
+ // The real graph behind `RetentionDeps` (Task 2685). Split from `retention.ts`
4
+ // so the predicate, the parsing and the sweep stay testable without Neo4j —
5
+ // the same division `scheduling-gate.ts` draws between its probe and
6
+ // `REAL_SOURCES`.
7
+ /**
8
+ * The longest deleted span the chain repair walks. A bound rather than `*0..`
9
+ * because an unbounded variable-length match over a long conversation is the one
10
+ * statement here that can run away; a span longer than this leaves the chain
11
+ * broken, which costs a later ingest a wrong tail and never costs data.
12
+ */
13
+ const RETENTION_CHAIN_HOPS = 500;
14
+ /** Neo4j returns list properties as-is; this narrows a `gateProcessedBy` value
15
+ * to the string list the predicate expects, treating an absent or wrong-typed
16
+ * property as "flagged by nobody". */
17
+ function flagList(value) {
18
+ return Array.isArray(value) ? value.filter((v) => typeof v === "string") : [];
19
+ }
20
+ export function retentionSources(now, log) {
21
+ return {
22
+ now,
23
+ log,
24
+ listEmailAccountIds: async () => {
25
+ const session = getSession();
26
+ try {
27
+ const res = await session.run(`MATCH (s:Section)
28
+ WHERE s.source = 'email' AND s.accountId IS NOT NULL
29
+ RETURN DISTINCT s.accountId AS accountId`);
30
+ return res.records.map((r) => r.get("accountId"));
31
+ }
32
+ finally {
33
+ await session.close();
34
+ }
35
+ },
36
+ // Every gate-bearing :Event on the account, enabled or suspended. A
37
+ // suspended routine counts as interested (operator decision 2026-08-13), so
38
+ // enabling a parked routine never finds its backlog already deleted — hence
39
+ // no `eventStatus` predicate here.
40
+ //
41
+ // `e.eventId`, never `elementId(e)`: the dispatcher passes the eventId
42
+ // PROPERTY as the flag key (agent-turn-dispatch.ts:506), so joining on the
43
+ // element id would compare against a value `gateProcessedBy` never holds and
44
+ // every section would look outstanding for ever.
45
+ listGateArgs: async (accountId) => {
46
+ const session = getSession();
47
+ try {
48
+ const res = await session.run(`MATCH (e:Event)
49
+ WHERE e.accountId = $accountId AND e.gateArgs IS NOT NULL
50
+ RETURN e.eventId AS eventId, e.gateArgs AS gateArgs`, { accountId });
51
+ return res.records
52
+ .filter((r) => typeof r.get("eventId") === "string")
53
+ .map((r) => ({
54
+ eventId: r.get("eventId"),
55
+ gateArgs: r.get("gateArgs") ?? null,
56
+ }));
57
+ }
58
+ finally {
59
+ await session.close();
60
+ }
61
+ },
62
+ // `s.createdAt < $cutoff` is lexical, which equals chronological because
63
+ // every value on both sides is a `toISOString()` string of fixed width. A
64
+ // createdAt of any other type compares as null in Cypher, so the row drops
65
+ // out of the candidate set and the section is KEPT.
66
+ listAgedSections: async (accountId, cutoff, limit) => {
67
+ const session = getSession();
68
+ try {
69
+ const res = await session.run(`MATCH (s:Section)
70
+ WHERE s.accountId = $accountId AND s.source = 'email'
71
+ AND s.createdAt < $cutoff
72
+ OPTIONAL MATCH (a:ConversationArchive)-[:HAS_SECTION]->(s)
73
+ RETURN elementId(s) AS id,
74
+ s.gateProcessedBy AS processedBy,
75
+ CASE WHEN a IS NULL THEN NULL ELSE coalesce(a.gateProcessedBy, []) END
76
+ AS parentProcessedBy
77
+ ORDER BY s.createdAt ASC
78
+ LIMIT $limit`, { accountId, cutoff, limit: neo4j.int(limit) });
79
+ return res.records.map((r) => {
80
+ const parent = r.get("parentProcessedBy");
81
+ return {
82
+ id: r.get("id"),
83
+ processedBy: flagList(r.get("processedBy")),
84
+ parentProcessedBy: parent === null || parent === undefined ? null : flagList(parent),
85
+ };
86
+ });
87
+ }
88
+ finally {
89
+ await session.close();
90
+ }
91
+ },
92
+ // The repair and the delete, in ONE write transaction.
93
+ //
94
+ // Sections are chained by :NEXT, and the next ingest finds the tail by
95
+ // looking for one with no outgoing :NEXT, LIMIT 1
96
+ // (memory-ingest.ts:1734-1742). Deleting from the middle of a chain leaves
97
+ // TWO such nodes, so a later ingest could chain new content onto a middle
98
+ // section. The repair connects each surviving predecessor to the first
99
+ // surviving section after the deleted span, shortest span first.
100
+ //
101
+ // It must run BEFORE the delete, because it walks the very edges the delete
102
+ // removes — and therefore inside the same transaction as the delete, because
103
+ // between the two the predecessor holds an edge to the doomed section AND
104
+ // its new edge to the target. A failure between them would leave that fork
105
+ // committed, and a routine added before the next cycle would make the
106
+ // sections outstanding again, so nothing would ever come back to repair it.
107
+ //
108
+ // Both statements carry `accountId` beside the element ids, the way
109
+ // `flagProcessedNodes` does (scheduling/mcp/src/lib/gate-flag.ts:100-104).
110
+ // The ids are already account-scoped by `listAgedSections`; the predicate is
111
+ // here so the scope is a property of the DELETE itself and not of the
112
+ // caller's discipline.
113
+ pruneSections: async (accountId, ids) => {
114
+ const session = getSession();
115
+ try {
116
+ return await session.executeWrite(async (tx) => {
117
+ const repair = await tx.run(`MATCH (p:Section)-[:NEXT]->(first:Section)
118
+ WHERE first.accountId = $accountId
119
+ AND elementId(first) IN $ids AND NOT elementId(p) IN $ids
120
+ MATCH path = (first)-[:NEXT*0..${RETENTION_CHAIN_HOPS}]->(q:Section)
121
+ WHERE NOT elementId(q) IN $ids
122
+ AND all(n IN nodes(path)[0..-1] WHERE elementId(n) IN $ids)
123
+ WITH p, q, length(path) AS len
124
+ ORDER BY len ASC
125
+ WITH p, head(collect(q)) AS target
126
+ MERGE (p)-[r:NEXT]->(target)
127
+ ON CREATE SET r.createdByAgent = 'email-retention', r.createdAt = $now
128
+ RETURN count(r) AS repaired`, { accountId, ids, now: now().toISOString() });
129
+ const del = await tx.run(`MATCH (s:Section)
130
+ WHERE s.accountId = $accountId AND s.source = 'email'
131
+ AND elementId(s) IN $ids
132
+ DETACH DELETE s
133
+ RETURN count(s) AS pruned`, { accountId, ids });
134
+ return {
135
+ repaired: repair.records[0]?.get("repaired")?.toNumber?.() ?? 0,
136
+ pruned: del.records[0]?.get("pruned")?.toNumber?.() ?? 0,
137
+ };
138
+ });
139
+ }
140
+ finally {
141
+ await session.close();
142
+ }
143
+ },
144
+ };
145
+ }
146
+ //# sourceMappingURL=retention-sources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retention-sources.js","sourceRoot":"","sources":["../../src/sampler/retention-sources.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * The configured retention period in whole days, or null.
3
+ *
4
+ * Null means PRUNE NOTHING, and every unreadable value resolves to it: unset,
5
+ * empty, zero, negative, fractional, non-numeric. Deletion is a thing the
6
+ * operator turns on per install, never a default, because the failure direction
7
+ * here is unrecoverable — the 2026-04-20 incident that ended autonomous graph
8
+ * hygiene rules is recorded at `memory/mcp/src/lib/graph-prune.ts:6`.
9
+ */
10
+ export declare function resolveRetentionDays(env: NodeJS.ProcessEnv): number | null;
11
+ /**
12
+ * The largest period this accepts, a hundred years.
13
+ *
14
+ * An upper bound and not only a lower one, because `cutoffIso` subtracts
15
+ * `days * 86_400_000` milliseconds and a value past the ±8.64e15 Date range
16
+ * yields an Invalid Date whose `toISOString()` throws. Without this, a fat-finger
17
+ * `EMAIL_RETENTION_DAYS` passes the integer guard and then throws once per
18
+ * account per cycle for ever, reported as a retention error rather than as the
19
+ * bad configuration it is. Anything beyond a century is a typo, and refusing it
20
+ * here prunes nothing, which is the same safe direction as every other
21
+ * unreadable value.
22
+ */
23
+ export declare const RETENTION_MAX_DAYS = 36500;
24
+ /**
25
+ * The instant a section must predate to be old enough to prune.
26
+ *
27
+ * Emitted through `toISOString()` because every `:Section.createdAt` is written
28
+ * the same way through a writer-owned key the ingest's reserved list forbids
29
+ * content from setting (`memory/mcp/src/tools/memory-ingest.ts:222-238`). Both
30
+ * sides therefore have the same fixed-width shape, which is what makes the
31
+ * lexical comparison in the Cypher equal to a chronological one.
32
+ */
33
+ export declare function cutoffIso(now: Date, days: number): string;
34
+ /** One routine's gate as the graph holds it. `gateArgs` is the JSON the gate
35
+ * tool receives, `{"eventId":…,"checks":[…]}`. */
36
+ export interface GateRow {
37
+ eventId: string;
38
+ gateArgs: string | null;
39
+ }
40
+ /** The routines watching each grain, by the `eventId` their flags carry. */
41
+ export interface InterestedSets {
42
+ section: string[];
43
+ archive: string[];
44
+ }
45
+ /**
46
+ * Which routines are interested in this account's email nodes, split by grain.
47
+ *
48
+ * A routine gated on `ConversationArchive` counts as interested in the sections
49
+ * underneath that archive (operator decision 2026-08-13): it reads them, so
50
+ * pruning one it has not flagged would strip content out from under a routine
51
+ * that never saw it. The two sets are consulted at different grains because that
52
+ * is where the dispatcher writes their flags — a `Section`-gated routine's id
53
+ * lands on the section, a `ConversationArchive`-gated routine's on the parent.
54
+ *
55
+ * A gate this cannot read contributes NOTHING and never throws. That is the safe
56
+ * direction in both halves: an unreadable gate shrinks the interested set, and a
57
+ * smaller set can only make MORE sections prunable — so it is paired with the
58
+ * empty-set rule in `sweepAccount`, which refuses to prune on a set of nobody.
59
+ */
60
+ export declare function interestedSets(rows: GateRow[]): InterestedSets;
61
+ /**
62
+ * One candidate: a section already filtered to `source:'email'` and to an age
63
+ * past the cutoff, carried with the two flag lists that decide it.
64
+ * `parentProcessedBy` is null when the section has no `:ConversationArchive`
65
+ * parent, which is a shape this sweep did not write and therefore keeps.
66
+ */
67
+ export interface AgedSection {
68
+ id: string;
69
+ processedBy: string[];
70
+ parentProcessedBy: string[] | null;
71
+ }
72
+ /**
73
+ * Split aged candidates into the ones every interested routine has finished with
74
+ * and a count of the ones still outstanding for somebody.
75
+ *
76
+ * Age is NOT re-checked here: the caller's Cypher filtered on the cutoff, so a
77
+ * row reaching this function is old enough by construction. The predicate is
78
+ * membership only.
79
+ *
80
+ * A flag from a routine that is no longer interested is ignored rather than
81
+ * required, because the set is derived from the gates that exist NOW. A deleted
82
+ * routine must not pin the archive for ever.
83
+ */
84
+ export declare function partitionAged(rows: AgedSection[], sets: InterestedSets): {
85
+ prunable: string[];
86
+ keptOutstanding: number;
87
+ };
88
+ /**
89
+ * The most candidates one account's sweep pulls per cycle.
90
+ *
91
+ * Bounded because this runs inside the sampler's 60-second loop and the aged set
92
+ * does not shrink on its own: only PRUNABLE rows leave it, so an account whose
93
+ * routines have stalled would otherwise grow an unbounded fetch for ever. The
94
+ * batch is ordered oldest-first, and a capped sweep drains across cycles.
95
+ * `capped=true` on the log line is what stops a truncated pass reading as a
96
+ * complete one.
97
+ */
98
+ export declare const RETENTION_CANDIDATE_CAP = 5000;
99
+ /**
100
+ * The graph operations the sweep needs, injected exactly as `SamplerDeps` is, so
101
+ * every branch above is testable without Neo4j and the Cypher lives in one place.
102
+ */
103
+ export interface RetentionDeps {
104
+ /** Distinct accountIds holding at least one `:Section {source:'email'}`. */
105
+ listEmailAccountIds: () => Promise<string[]>;
106
+ /** Every `:Event` on the account that carries a gate, enabled or suspended. */
107
+ listGateArgs: (accountId: string) => Promise<GateRow[]>;
108
+ /** Email sections older than `cutoff`, oldest first, with their flag lists. */
109
+ listAgedSections: (accountId: string, cutoff: string, limit: number) => Promise<AgedSection[]>;
110
+ /**
111
+ * Repair the `:NEXT` chain across the span and delete it, as ONE unit.
112
+ *
113
+ * A single call rather than a repair and a delete the sweep sequences itself,
114
+ * because the repair MERGEs `(p)-[:NEXT]->(target)` while `p`'s edge to the
115
+ * first doomed section still exists and relies on the delete to remove it. Two
116
+ * calls means a delete failure leaves `p` with two outgoing `:NEXT` edges and
117
+ * the reading order forked. Retrying next cycle recovers it only while the
118
+ * sections stay prunable, and a routine added in between makes them
119
+ * outstanding again, so the fork would persist with nothing left to fix it.
120
+ */
121
+ pruneSections: (accountId: string, ids: string[]) => Promise<{
122
+ repaired: number;
123
+ pruned: number;
124
+ }>;
125
+ now: () => Date;
126
+ log: (line: string) => void;
127
+ }
128
+ /**
129
+ * Sweep one account.
130
+ *
131
+ * The empty interested set is the load-bearing branch: it deletes NOTHING and
132
+ * says so. Read literally, "flagged by every interested routine" is vacuously
133
+ * true over an empty set, which on every install today — no shipped routine
134
+ * carries a `graph-unflagged` check until Task 2686 — would delete the whole
135
+ * email archive older than N on the first sweep. An empty set means nobody has
136
+ * consented to this data being processed, not that everybody has.
137
+ */
138
+ export declare function sweepAccount(deps: RetentionDeps, accountId: string, days: number): Promise<void>;
139
+ /**
140
+ * One retention pass over every account holding email sections.
141
+ *
142
+ * Accounts come from the DATA, not from the routine set: an account holding
143
+ * email and no routine is exactly the one whose `interested=0` line the operator
144
+ * needs to see, and enumerating from routines would make it invisible.
145
+ *
146
+ * One account's failure is named and never stops the next, matching the arms in
147
+ * `main.ts`.
148
+ */
149
+ export declare function runRetention(deps: RetentionDeps, env: NodeJS.ProcessEnv): Promise<void>;
150
+ //# sourceMappingURL=retention.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retention.d.ts","sourceRoot":"","sources":["../../src/sampler/retention.ts"],"names":[],"mappings":"AAMA;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,UAAU,GAAG,MAAM,GAAG,IAAI,CAM1E;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,QAAS,CAAC;AAEzC;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;mDACmD;AACnD,MAAM,WAAW,OAAO;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,4EAA4E;AAC5E,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,cAAc,CAqB9D;AAED;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,iBAAiB,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CACpC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,WAAW,EAAE,EACnB,IAAI,EAAE,cAAc,GACnB;IAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,CAcjD;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,uBAAuB,OAAO,CAAC;AAE5C;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,4EAA4E;IAC5E,mBAAmB,EAAE,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7C,+EAA+E;IAC/E,YAAY,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;IACxD,+EAA+E;IAC/E,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC/F;;;;;;;;;;OAUG;IACH,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACnG,GAAG,EAAE,MAAM,IAAI,CAAC;IAChB,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7B;AAED;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,aAAa,EACnB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAqCf;AAED;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,aAAa,EACnB,GAAG,EAAE,MAAM,CAAC,UAAU,GACrB,OAAO,CAAC,IAAI,CAAC,CAsBf"}