@rubytech/create-realagent-code 0.1.603 → 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 (133) hide show
  1. package/dist/__tests__/email-sampler-unit.test.js +22 -0
  2. package/dist/index.js +3 -0
  3. package/dist/port-resolution.js +9 -1
  4. package/package.json +1 -1
  5. package/payload/platform/lib/routine-templates/dist/roster.d.ts +46 -2
  6. package/payload/platform/lib/routine-templates/dist/roster.d.ts.map +1 -1
  7. package/payload/platform/lib/routine-templates/dist/roster.js +84 -32
  8. package/payload/platform/lib/routine-templates/dist/roster.js.map +1 -1
  9. package/payload/platform/lib/routine-templates/src/__tests__/roster.test.ts +0 -0
  10. package/payload/platform/lib/routine-templates/src/__tests__/seed.test.ts +48 -2
  11. package/payload/platform/lib/routine-templates/src/roster.ts +133 -35
  12. package/payload/platform/lib/telegram-store-read/dist/index.d.ts +59 -0
  13. package/payload/platform/lib/telegram-store-read/dist/index.d.ts.map +1 -0
  14. package/payload/platform/lib/telegram-store-read/dist/index.js +219 -0
  15. package/payload/platform/lib/telegram-store-read/dist/index.js.map +1 -0
  16. package/payload/platform/lib/telegram-store-read/src/__tests__/fold.test.ts +124 -0
  17. package/payload/platform/lib/telegram-store-read/src/index.ts +262 -0
  18. package/payload/platform/lib/telegram-store-read/tsconfig.json +9 -0
  19. package/payload/platform/lib/telegram-store-read/vitest.config.ts +9 -0
  20. package/payload/platform/package.json +2 -2
  21. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +39 -6
  22. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +11 -0
  23. package/payload/platform/plugins/connector/PLUGIN.md +9 -1
  24. package/payload/platform/plugins/connector/mcp/dist/__tests__/queue-descriptor.test.d.ts +2 -0
  25. package/payload/platform/plugins/connector/mcp/dist/__tests__/queue-descriptor.test.d.ts.map +1 -0
  26. package/payload/platform/plugins/connector/mcp/dist/__tests__/queue-descriptor.test.js +67 -0
  27. package/payload/platform/plugins/connector/mcp/dist/__tests__/queue-descriptor.test.js.map +1 -0
  28. package/payload/platform/plugins/connector/mcp/dist/index.js +36 -5
  29. package/payload/platform/plugins/connector/mcp/dist/index.js.map +1 -1
  30. package/payload/platform/plugins/connector/mcp/dist/lib/store.d.ts +35 -0
  31. package/payload/platform/plugins/connector/mcp/dist/lib/store.d.ts.map +1 -1
  32. package/payload/platform/plugins/connector/mcp/dist/lib/store.js +33 -0
  33. package/payload/platform/plugins/connector/mcp/dist/lib/store.js.map +1 -1
  34. package/payload/platform/plugins/connector/mcp/package.json +4 -2
  35. package/payload/platform/plugins/connector/mcp/vitest.config.ts +8 -0
  36. package/payload/platform/plugins/docs/references/admin-ui.md +2 -2
  37. package/payload/platform/plugins/docs/references/outlook-guide.md +21 -2
  38. package/payload/platform/plugins/docs/references/telegram-guide.md +15 -1
  39. package/payload/platform/plugins/email/PLUGIN.md +14 -1
  40. package/payload/platform/plugins/email/mcp/dist/sampler/__tests__/retention.test.d.ts +2 -0
  41. package/payload/platform/plugins/email/mcp/dist/sampler/__tests__/retention.test.d.ts.map +1 -0
  42. package/payload/platform/plugins/email/mcp/dist/sampler/__tests__/retention.test.js +233 -0
  43. package/payload/platform/plugins/email/mcp/dist/sampler/__tests__/retention.test.js.map +1 -0
  44. package/payload/platform/plugins/email/mcp/dist/sampler/__tests__/sample-mailbox.test.js +30 -0
  45. package/payload/platform/plugins/email/mcp/dist/sampler/__tests__/sample-mailbox.test.js.map +1 -1
  46. package/payload/platform/plugins/email/mcp/dist/sampler/main.js +68 -3
  47. package/payload/platform/plugins/email/mcp/dist/sampler/main.js.map +1 -1
  48. package/payload/platform/plugins/email/mcp/dist/sampler/retention-sources.d.ts +3 -0
  49. package/payload/platform/plugins/email/mcp/dist/sampler/retention-sources.d.ts.map +1 -0
  50. package/payload/platform/plugins/email/mcp/dist/sampler/retention-sources.js +146 -0
  51. package/payload/platform/plugins/email/mcp/dist/sampler/retention-sources.js.map +1 -0
  52. package/payload/platform/plugins/email/mcp/dist/sampler/retention.d.ts +150 -0
  53. package/payload/platform/plugins/email/mcp/dist/sampler/retention.d.ts.map +1 -0
  54. package/payload/platform/plugins/email/mcp/dist/sampler/retention.js +199 -0
  55. package/payload/platform/plugins/email/mcp/dist/sampler/retention.js.map +1 -0
  56. package/payload/platform/plugins/email/mcp/dist/sampler/sample-mailbox.d.ts +5 -0
  57. package/payload/platform/plugins/email/mcp/dist/sampler/sample-mailbox.d.ts.map +1 -1
  58. package/payload/platform/plugins/email/mcp/dist/sampler/sample-mailbox.js +10 -5
  59. package/payload/platform/plugins/email/mcp/dist/sampler/sample-mailbox.js.map +1 -1
  60. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-section-properties-strip.test.js +11 -0
  61. package/payload/platform/plugins/memory/mcp/dist/tools/__tests__/memory-ingest-section-properties-strip.test.js.map +1 -1
  62. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts.map +1 -1
  63. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +8 -0
  64. package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
  65. package/payload/platform/plugins/outlook/mcp/dist/__tests__/sample-outlook-mailbox.test.d.ts +2 -0
  66. package/payload/platform/plugins/outlook/mcp/dist/__tests__/sample-outlook-mailbox.test.d.ts.map +1 -0
  67. package/payload/platform/plugins/outlook/mcp/dist/__tests__/sample-outlook-mailbox.test.js +324 -0
  68. package/payload/platform/plugins/outlook/mcp/dist/__tests__/sample-outlook-mailbox.test.js.map +1 -0
  69. package/payload/platform/plugins/outlook/mcp/dist/__tests__/sampler-cursor.test.d.ts +2 -0
  70. package/payload/platform/plugins/outlook/mcp/dist/__tests__/sampler-cursor.test.d.ts.map +1 -0
  71. package/payload/platform/plugins/outlook/mcp/dist/__tests__/sampler-cursor.test.js +102 -0
  72. package/payload/platform/plugins/outlook/mcp/dist/__tests__/sampler-cursor.test.js.map +1 -0
  73. package/payload/platform/plugins/outlook/mcp/dist/__tests__/sampler-enumerate.test.d.ts +2 -0
  74. package/payload/platform/plugins/outlook/mcp/dist/__tests__/sampler-enumerate.test.d.ts.map +1 -0
  75. package/payload/platform/plugins/outlook/mcp/dist/__tests__/sampler-enumerate.test.js +125 -0
  76. package/payload/platform/plugins/outlook/mcp/dist/__tests__/sampler-enumerate.test.js.map +1 -0
  77. package/payload/platform/plugins/outlook/mcp/dist/sampler/arm.d.ts +2 -0
  78. package/payload/platform/plugins/outlook/mcp/dist/sampler/arm.d.ts.map +1 -0
  79. package/payload/platform/plugins/outlook/mcp/dist/sampler/arm.js +109 -0
  80. package/payload/platform/plugins/outlook/mcp/dist/sampler/arm.js.map +1 -0
  81. package/payload/platform/plugins/outlook/mcp/dist/sampler/cursor.d.ts +25 -0
  82. package/payload/platform/plugins/outlook/mcp/dist/sampler/cursor.d.ts.map +1 -0
  83. package/payload/platform/plugins/outlook/mcp/dist/sampler/cursor.js +94 -0
  84. package/payload/platform/plugins/outlook/mcp/dist/sampler/cursor.js.map +1 -0
  85. package/payload/platform/plugins/outlook/mcp/dist/sampler/enumerate.d.ts +41 -0
  86. package/payload/platform/plugins/outlook/mcp/dist/sampler/enumerate.d.ts.map +1 -0
  87. package/payload/platform/plugins/outlook/mcp/dist/sampler/enumerate.js +122 -0
  88. package/payload/platform/plugins/outlook/mcp/dist/sampler/enumerate.js.map +1 -0
  89. package/payload/platform/plugins/outlook/mcp/dist/sampler/sample-outlook-mailbox.d.ts +73 -0
  90. package/payload/platform/plugins/outlook/mcp/dist/sampler/sample-outlook-mailbox.d.ts.map +1 -0
  91. package/payload/platform/plugins/outlook/mcp/dist/sampler/sample-outlook-mailbox.js +162 -0
  92. package/payload/platform/plugins/outlook/mcp/dist/sampler/sample-outlook-mailbox.js.map +1 -0
  93. package/payload/platform/plugins/scheduling/PLUGIN.md +8 -1
  94. package/payload/platform/plugins/scheduling/mcp/dist/index.js +1 -1
  95. package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
  96. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate-flag.test.d.ts +2 -0
  97. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate-flag.test.d.ts.map +1 -0
  98. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate-flag.test.js +90 -0
  99. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate-flag.test.js.map +1 -0
  100. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate.test.js +26 -4
  101. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate.test.js.map +1 -1
  102. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate-flag.d.ts +78 -0
  103. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate-flag.d.ts.map +1 -0
  104. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate-flag.js +76 -0
  105. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate-flag.js.map +1 -0
  106. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.d.ts +8 -0
  107. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.d.ts.map +1 -1
  108. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.js +9 -1
  109. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.js.map +1 -1
  110. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/gate-dispatch-wiring.test.js +15 -0
  111. package/payload/platform/plugins/scheduling/mcp/dist/scripts/__tests__/gate-dispatch-wiring.test.js.map +1 -1
  112. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts +12 -1
  113. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.d.ts.map +1 -1
  114. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js +34 -1
  115. package/payload/platform/plugins/scheduling/mcp/dist/scripts/agent-turn-dispatch.js.map +1 -1
  116. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +9 -1
  117. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
  118. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-gate-write.test.js +40 -0
  119. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-gate-write.test.js.map +1 -1
  120. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/scheduling-gate.test.js +435 -5
  121. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/scheduling-gate.test.js.map +1 -1
  122. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts.map +1 -1
  123. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js +21 -1
  124. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js.map +1 -1
  125. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.d.ts +294 -6
  126. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.d.ts.map +1 -1
  127. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.js +553 -11
  128. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.js.map +1 -1
  129. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +1 -1
  130. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  131. package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
  132. package/payload/server/public/operator.html +1 -1
  133. package/payload/server/server.js +69 -5
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Task 2688 — the changed-since queue a registered connector can describe.
3
+ *
4
+ * The descriptor exists so a shipped routine template can gate on "this
5
+ * account's operational system" without naming a connector, a path or an
6
+ * address (Task 2389). These assert the two properties the gate depends on:
7
+ * the descriptor survives a round trip through the registry, and it reaches a
8
+ * reader through the listing while the credential never does.
9
+ */
10
+ import { mkdtempSync } from "node:fs";
11
+ import { tmpdir } from "node:os";
12
+ import { join } from "node:path";
13
+ import { describe, expect, it } from "vitest";
14
+ import { listConnectors, queueFromArgs, readRegistry, registerConnector } from "../lib/store.js";
15
+ const ACCOUNT = "20028fb5-0000-4000-8000-000000000000";
16
+ const REC = {
17
+ baseUrl: "https://api.example.com",
18
+ host: "api.example.com",
19
+ authScheme: "bearer",
20
+ };
21
+ const QUEUE = {
22
+ kind: "operational",
23
+ path: "/v2/jobs/changed",
24
+ changedSinceParam: "updatedSince",
25
+ itemsField: "items",
26
+ };
27
+ function root() {
28
+ return join(mkdtempSync(join(tmpdir(), "connector-store-")), "platform");
29
+ }
30
+ describe("queue descriptor", () => {
31
+ it("round-trips through the registry", () => {
32
+ const p = root();
33
+ registerConnector(p, ACCOUNT, "jobs", { ...REC, queue: QUEUE }, "secret");
34
+ expect(readRegistry(p, ACCOUNT).jobs.queue).toEqual(QUEUE);
35
+ });
36
+ it("is returned by the connector listing, and the credential is not", () => {
37
+ const p = root();
38
+ registerConnector(p, ACCOUNT, "jobs", { ...REC, queue: QUEUE }, "secret");
39
+ expect(listConnectors(p, ACCOUNT)).toEqual([
40
+ { name: "jobs", baseUrl: REC.baseUrl, queue: QUEUE },
41
+ ]);
42
+ });
43
+ it("omits the queue for a connector registered without one", () => {
44
+ const p = root();
45
+ registerConnector(p, ACCOUNT, "plain", REC, "secret");
46
+ expect(listConnectors(p, ACCOUNT)).toEqual([{ name: "plain", baseUrl: REC.baseUrl }]);
47
+ });
48
+ });
49
+ describe("queue validation", () => {
50
+ it("accepts a complete descriptor", () => {
51
+ expect(queueFromArgs(QUEUE)).toEqual({ queue: QUEUE });
52
+ });
53
+ it("refuses a kind outside the two", () => {
54
+ expect(queueFromArgs({ ...QUEUE, kind: "sales" })).toEqual({
55
+ refusal: "queue.kind must be operational or accounting.",
56
+ });
57
+ });
58
+ it("refuses a blank field", () => {
59
+ expect(queueFromArgs({ ...QUEUE, path: " " })).toEqual({
60
+ refusal: "queue.path must not be blank.",
61
+ });
62
+ });
63
+ it("passes undefined through as no queue", () => {
64
+ expect(queueFromArgs(undefined)).toEqual({});
65
+ });
66
+ });
67
+ //# sourceMappingURL=queue-descriptor.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"queue-descriptor.test.js","sourceRoot":"","sources":["../../src/__tests__/queue-descriptor.test.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
2
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
3
  import { z } from "zod";
4
4
  import { lifelineTool } from "../../../../lib/mcp-lifeline/dist/index.js";
5
- import { readRegistry, listConnectors, registerConnector, deregisterConnector, resolveConnector, } from "./lib/store.js";
5
+ import { readRegistry, listConnectors, registerConnector, deregisterConnector, resolveConnector, queueFromArgs, } from "./lib/store.js";
6
6
  import { performCall } from "./lib/call.js";
7
7
  import { hostResolvesPrivate } from "./lib/ssrf.js";
8
8
  // Plugin-system boot tolerance: module init must never throw. tools/list is enumerated
@@ -41,7 +41,7 @@ function ctx(tool) {
41
41
  return { platformRoot: PLATFORM_ROOT, accountId: ACCOUNT_ID };
42
42
  }
43
43
  const server = new McpServer({ name: "connector", version: "0.1.0" });
44
- lifelineTool(server, "connector-register", "Register a named third-party REST API: store its base URL and a static credential so the agent can call it. auth.scheme is bearer | api-key-header | basic; headerName is required for api-key-header. The credential is stored in the account secrets file and is never returned or logged. Returns the non-secret record only.", {
44
+ lifelineTool(server, "connector-register", "Register a named third-party REST API: store its base URL and a static credential so the agent can call it. auth.scheme is bearer | api-key-header | basic; headerName is required for api-key-header. The credential is stored in the account secrets file and is never returned or logged. Optionally describe a changed-since queue, which is what lets a scheduled reconcile wake only when this system has moved: queue.kind (operational or accounting) chooses which routine watches it, queue.path is the endpoint under the base URL, queue.changedSinceParam is the query parameter carrying the watermark, and queue.itemsField is the response field holding the array of changed records. Registering an existing name again replaces its record, so omitting queue removes it. Returns the non-secret record only.", {
45
45
  name: z.string().min(1),
46
46
  baseUrl: z.string().min(1),
47
47
  auth: z.object({
@@ -49,7 +49,15 @@ lifelineTool(server, "connector-register", "Register a named third-party REST AP
49
49
  value: z.string().min(1),
50
50
  headerName: z.string().min(1).optional(),
51
51
  }),
52
- }, async ({ name, baseUrl, auth }) => {
52
+ queue: z
53
+ .object({
54
+ kind: z.enum(["operational", "accounting"]),
55
+ path: z.string().min(1),
56
+ changedSinceParam: z.string().min(1),
57
+ itemsField: z.string().min(1),
58
+ })
59
+ .optional(),
60
+ }, async ({ name, baseUrl, auth, queue }) => {
53
61
  const c = ctx("connector-register");
54
62
  if ("refusal" in c)
55
63
  return c.refusal;
@@ -68,15 +76,38 @@ lifelineTool(server, "connector-register", "Register a named third-party REST AP
68
76
  process.stderr.write(`[connector] op=register-refused name=${name} reason=private-ip target=${url.hostname}\n`);
69
77
  return fail("connector-register: refused — baseUrl host resolves to a private or loopback address.");
70
78
  }
79
+ // Task 2688 — the zod schema already refuses a malformed queue from a tool
80
+ // call; this refuses one that reached the handler any other way, and it is
81
+ // the single place the shape is judged, so the store and the tool cannot
82
+ // disagree about what a valid descriptor is.
83
+ const parsedQueue = queueFromArgs(queue);
84
+ if (parsedQueue.refusal)
85
+ return fail(`connector-register: ${parsedQueue.refusal}`);
86
+ // Registering an existing name REPLACES its record, so omitting the queue
87
+ // removes it. Merging instead would be invented behaviour; the drop is made
88
+ // visible, because a queue silently lost mutes whichever routine gated on
89
+ // it and no other surface would say so.
90
+ const previous = readRegistry(c.platformRoot, c.accountId)[name];
91
+ if (previous?.queue && !parsedQueue.queue) {
92
+ process.stderr.write(`[connector] op=register name=${name} queue=dropped\n`);
93
+ }
71
94
  const rec = {
72
95
  baseUrl,
73
96
  host: url.hostname,
74
97
  authScheme: auth.scheme,
75
98
  ...(auth.scheme === "api-key-header" ? { headerName: auth.headerName } : {}),
99
+ ...(parsedQueue.queue ? { queue: parsedQueue.queue } : {}),
76
100
  };
77
101
  registerConnector(c.platformRoot, c.accountId, name, rec, auth.value);
78
- process.stderr.write(`[connector] op=register name=${name} host=${url.hostname} scheme=${auth.scheme}\n`);
79
- return ok({ name, baseUrl, host: url.hostname, authScheme: auth.scheme });
102
+ process.stderr.write(`[connector] op=register name=${name} host=${url.hostname} scheme=${auth.scheme} ` +
103
+ `queue=${parsedQueue.queue?.kind ?? "none"}\n`);
104
+ return ok({
105
+ name,
106
+ baseUrl,
107
+ host: url.hostname,
108
+ authScheme: auth.scheme,
109
+ ...(parsedQueue.queue ? { queue: parsedQueue.queue } : {}),
110
+ });
80
111
  });
81
112
  lifelineTool(server, "connector-call", "Make an authenticated request to a registered connector. The credential is injected from the secret store; the request is confined to the registered host (a different host, a private address, or a cross-host redirect is refused). Returns { status, headers (redacted), body (capped) }.", {
82
113
  name: z.string().min(1),
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,45 @@
1
1
  export type AuthScheme = "bearer" | "api-key-header" | "basic";
2
+ /** Task 2688 — which routine watches this queue. A scheduled reconcile names
3
+ * the KIND, never the connector, because a shipped routine template can carry
4
+ * no per-account value (Task 2389). */
5
+ export type ConnectorQueueKind = "operational" | "accounting";
6
+ /**
7
+ * Task 2688 — how this connector answers "what changed since <instant>".
8
+ *
9
+ * Held on the registry record rather than on the gate that reads it: the
10
+ * account knows its own API and describes it once, and the scheduling gate then
11
+ * asks for whichever queues carry the kind it was armed with. Without this the
12
+ * gate would have to name a path and a parameter itself, which no shipped
13
+ * template can do.
14
+ */
15
+ export interface ConnectorQueue {
16
+ kind: ConnectorQueueKind;
17
+ /** Path under the registered base URL, e.g. "/v2/jobs/changed". */
18
+ path: string;
19
+ /** The query parameter that carries the watermark, e.g. "updatedSince". */
20
+ changedSinceParam: string;
21
+ /** Top-level response field holding the array of changed records. */
22
+ itemsField: string;
23
+ }
2
24
  export interface ConnectorRecord {
3
25
  baseUrl: string;
4
26
  host: string;
5
27
  authScheme: AuthScheme;
6
28
  /** Required when authScheme is "api-key-header"; the header the credential is sent in. */
7
29
  headerName?: string;
30
+ queue?: ConnectorQueue;
8
31
  }
32
+ /**
33
+ * Validate a queue descriptor arriving off tool arguments.
34
+ *
35
+ * Returns `{}` for an absent queue, `{ queue }` for a valid one, and
36
+ * `{ refusal }` for anything else. A refusal string rather than a throw,
37
+ * because the tool answers a refusal as text and never as an exception.
38
+ */
39
+ export declare function queueFromArgs(raw: unknown): {
40
+ queue?: ConnectorQueue;
41
+ refusal?: string;
42
+ };
9
43
  type Registry = Record<string, ConnectorRecord>;
10
44
  type Secrets = Record<string, string>;
11
45
  export declare function readRegistry(platformRoot: string, accountId: string): Registry;
@@ -20,6 +54,7 @@ export declare function resolveConnector(platformRoot: string, accountId: string
20
54
  export declare function listConnectors(platformRoot: string, accountId: string): {
21
55
  name: string;
22
56
  baseUrl: string;
57
+ queue?: ConnectorQueue;
23
58
  }[];
24
59
  export {};
25
60
  //# sourceMappingURL=store.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/lib/store.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,gBAAgB,GAAG,OAAO,CAAC;AAE/D,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,UAAU,CAAC;IACvB,0FAA0F;IAC1F,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,KAAK,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAChD,KAAK,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAyBtC,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,QAAQ,CAE9E;AAED,wBAAgB,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAE5E;AAED,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,eAAe,EACpB,MAAM,EAAE,MAAM,GACb,IAAI,CAON;AAED,gFAAgF;AAChF,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CASlG;AAED,wBAAgB,gBAAgB,CAC9B,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,GACX;IAAE,GAAG,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAKjD;AAED,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAAE,CAK3G"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/lib/store.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,gBAAgB,GAAG,OAAO,CAAC;AAE/D;;wCAEwC;AACxC,MAAM,MAAM,kBAAkB,GAAG,aAAa,GAAG,YAAY,CAAC;AAE9D;;;;;;;;GAQG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,kBAAkB,CAAC;IACzB,mEAAmE;IACnE,IAAI,EAAE,MAAM,CAAC;IACb,2EAA2E;IAC3E,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qEAAqE;IACrE,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,UAAU,CAAC;IACvB,0FAA0F;IAC1F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,cAAc,CAAC;CACxB;AAID;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG;IAAE,KAAK,CAAC,EAAE,cAAc,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAoBxF;AAED,KAAK,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAChD,KAAK,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAyBtC,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,QAAQ,CAE9E;AAED,wBAAgB,WAAW,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAE5E;AAED,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,eAAe,EACpB,MAAM,EAAE,MAAM,GACb,IAAI,CAON;AAED,gFAAgF;AAChF,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CASlG;AAED,wBAAgB,gBAAgB,CAC9B,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,GACX;IAAE,GAAG,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAKjD;AAED,wBAAgB,cAAc,CAC5B,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,GAChB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,cAAc,CAAA;CAAE,EAAE,CAQ7D"}
@@ -1,5 +1,35 @@
1
1
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { dirname, join, resolve } from "node:path";
3
+ const QUEUE_KINDS = ["operational", "accounting"];
4
+ /**
5
+ * Validate a queue descriptor arriving off tool arguments.
6
+ *
7
+ * Returns `{}` for an absent queue, `{ queue }` for a valid one, and
8
+ * `{ refusal }` for anything else. A refusal string rather than a throw,
9
+ * because the tool answers a refusal as text and never as an exception.
10
+ */
11
+ export function queueFromArgs(raw) {
12
+ if (raw === undefined || raw === null)
13
+ return {};
14
+ const q = raw;
15
+ if (!QUEUE_KINDS.includes(q.kind)) {
16
+ return { refusal: "queue.kind must be operational or accounting." };
17
+ }
18
+ for (const field of ["path", "changedSinceParam", "itemsField"]) {
19
+ const v = q[field];
20
+ if (typeof v !== "string" || v.trim() === "") {
21
+ return { refusal: `queue.${field} must not be blank.` };
22
+ }
23
+ }
24
+ return {
25
+ queue: {
26
+ kind: q.kind,
27
+ path: q.path,
28
+ changedSinceParam: q.changedSinceParam,
29
+ itemsField: q.itemsField,
30
+ },
31
+ };
32
+ }
3
33
  /** Per-account secrets dir — the same location joblogic/quickbooks/cloudflare use. */
4
34
  function secretsDir(platformRoot, accountId) {
5
35
  return join(resolve(platformRoot, "..", "data/accounts"), accountId, "secrets");
@@ -58,6 +88,9 @@ export function listConnectors(platformRoot, accountId) {
58
88
  return Object.entries(readRegistry(platformRoot, accountId)).map(([name, r]) => ({
59
89
  name,
60
90
  baseUrl: r.baseUrl,
91
+ // Task 2688 — the descriptor carries no secret, and the scheduling gate
92
+ // reads it here rather than opening the registry file itself.
93
+ ...(r.queue ? { queue: r.queue } : {}),
61
94
  }));
62
95
  }
63
96
  //# sourceMappingURL=store.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/lib/store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/lib/store.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,8 @@
6
6
  "main": "dist/index.js",
7
7
  "scripts": {
8
8
  "build": "tsc",
9
- "start": "node dist/index.js"
9
+ "start": "node dist/index.js",
10
+ "test": "vitest run"
10
11
  },
11
12
  "dependencies": {
12
13
  "@modelcontextprotocol/sdk": "^1.12.1",
@@ -15,6 +16,7 @@
15
16
  },
16
17
  "devDependencies": {
17
18
  "@types/node": "^22.0.0",
18
- "typescript": "^5.7.0"
19
+ "typescript": "^5.7.0",
20
+ "vitest": "^4.1.2"
19
21
  }
20
22
  }
@@ -0,0 +1,8 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ environment: "node",
6
+ include: ["src/**/__tests__/**/*.test.ts"],
7
+ },
8
+ });
@@ -294,9 +294,9 @@ one, over the same automation set the routes list. The roster seed's own
294
294
  `descNull` counter is its narrow twin, scoped to seeded rows and so blind to
295
295
  exactly the operator-created ones most likely to carry no caption.
296
296
 
297
- **The shipped roster.** Every account starts with fourteen routines —
297
+ **The shipped roster.** Every account starts with thirteen routines —
298
298
  `start-of-day`, `inbound-check`, `telegram-inbound-check`, `end-of-day`,
299
- `appointment-reminder`, `commitment-chase`, `calendar-reconcile`,
299
+ `commitment-chase`, `calendar-reconcile`,
300
300
  `contact-reconcile`, `crm-reconcile`, `finance-reconcile`, `weekly-digest`,
301
301
  `channel-link-check`, `graph-health`, `filesystem-health`, in `/routines`
302
302
  display order. `ROUTINE_ROSTER_KEYS.length`
@@ -22,7 +22,7 @@ The `outlook` plugin gives the admin agent access to Microsoft 365 / Outlook.com
22
22
  | `outlook-mail-attachment` | List or download any message's attachments — received or draft. With `messageId` alone, LISTS them (`id`, `name`, `contentType`, `size`, `isInline`, and a `kind` of file/item/reference) via `GET /me/messages/{id}/attachments` and `$select` (no bytes). A draft is a message in the same collection, so passing a draftId lists what that draft carries; the returned `name`/`id` is what `outlook-draft-edit`'s `removeAttachments` takes. With `messageId`+`attachmentId`, DOWNLOADS one file attachment: its bytes are written to `{accountDir}/uploads/outlook/<messageHash>/<bytesHash>-<name>` at `0o600` and the saved path is returned, ready for the Read tool, `SendUserFile`, or an `outlook-mail-reply` attachment. Only file attachments download — item/reference kinds, empty payloads, and anything over 25 MB are refused with nothing written. `outlook-mail-list` / `outlook-mail-search` / `outlook-mail-fetch-body` report `hasAttachments` so the agent knows when to call this. Needs `Mail.Read`. |
23
23
  | `outlook-mail-reply` | Reply in-thread (`createReply` / `createReplyAll` + send). Threads natively via conversationId. `replyAll`, added cc/bcc, and account-scoped file attachments (up to 25 MB per file; files over Graph's ~3 MB inline limit use a chunked upload session). Body is plain text, prepended above the quote. A reply whose recipients resolve **only** to the sending mailbox is **refused** before the send and its draft deleted: Graph addresses a reply to the original message's sender, so replying to your own sent message addresses it to yourself. An explicit `cc` does not exempt it — use `outlook-mail-send` with an explicit `to`. The resolved recipients are logged as `to=`. Needs `Mail.ReadWrite` + `Mail.Send`. |
24
24
  | `outlook-mail-delete` | Move messages to Deleted Items (`POST /me/messages/{id}/move`). Recoverable — never hard-deletes. An id already gone is counted not-moved. Needs `Mail.ReadWrite`. |
25
- | `outlook-mail-ingest` | Write operator-reviewed messages into the business graph as `:ConversationArchive {source:'email'}` threads (the analogue of the IMAP `email-ingest`). Input `decisions`: one `{messageId, disposition:'ingest'\|'discard'}` per listed message. Each `ingest` message's full body + envelope is fetched, threads group by Graph `conversationId`, and participants resolve closed-set against Neo4j (`:Person`/`:AdminUser` by `accountId`); an unresolved From/To/Cc address skips its whole thread and is returned to the operator (no `:Person` auto-create). Resolvable threads dispatch through the shared `conversation-archive-ingest.sh --source email`. Human-in-the-loop: nothing lands without an explicit per-message decision. The plugin's only Neo4j coupling. Needs `Mail.Read`. |
25
+ | `outlook-mail-ingest` | Write operator-reviewed messages into the business graph as `:ConversationArchive {source:'email'}` threads (the analogue of the IMAP `email-ingest`). Input `decisions`: one `{messageId, disposition:'ingest'\|'discard'}` per listed message. Each `ingest` message's full body + envelope is fetched, threads group by Graph `conversationId`, and participants resolve closed-set against Neo4j (`:Person`/`:AdminUser` by `accountId`); an unresolved From/To/Cc address skips its whole thread and is returned to the operator (no `:Person` auto-create). Resolvable threads dispatch through the shared `conversation-archive-ingest.sh --source email`. Human-in-the-loop: nothing lands without an explicit per-message decision. One of the plugin's two Neo4j couplings; the other is the email-sampler's Outlook arm below, which writes through the same dispatcher with no operator decision at all. Needs `Mail.Read`. |
26
26
  | `outlook-mail-otp-extract` | Poll the inbox for a one-time code from a sender (domain). Reads full bodies to extract it; optional subject regex and timeout (default 60 s). |
27
27
  | `outlook-calendar-list` | Calendar events in next rangeDays days (default 7, max 365). |
28
28
  | `outlook-calendar-event` | Full detail of a single event by id. |
@@ -59,6 +59,20 @@ The attach call checks the access before it stores anything, so what it says bac
59
59
 
60
60
  Mail only: calendar and contacts tools refuse on a shared mailbox and say so. If a shared mailbox stops answering, the delegation was probably removed in Exchange — `outlook-mailbox-list verify:true` reports which ones are still reachable.
61
61
 
62
+ An attached shared mailbox is also sampled into the graph on a timer, the same as a signed-in one. See the sampler section below.
63
+
64
+ ## Deterministic ingest into the graph
65
+
66
+ Separately from `outlook-mail-ingest`, which needs an operator decision per message, the `<brand>-email-sampler.service` unit writes inbound Outlook mail into the graph on a timer with no agent turn. It samples every signed-in mailbox whose status is `ok` and every attached shared mailbox, across every account on the install, and writes `:ConversationArchive {source:'email'}` through the same dispatcher `outlook-mail-ingest` uses. Bodies are stored whole.
67
+
68
+ Its cursor is the newest `receivedDateTime` written plus the ids seen at that instant, held per account in `secrets/outlook/sampler-cursors.json` (mode 0600, keyed by SMTP address). The id set is what makes a timestamp cursor safe: filtering `gt` would drop a message arriving at the same instant as the boundary, and `ge` alone would re-ingest the boundary on every cycle.
69
+
70
+ A mailbox with no cursor backfills 7 days on its first pass. A brand with no `OUTLOOK_CLIENT_ID` runs no Outlook arm at all and logs one skip per cycle rather than an auth error per mailbox.
71
+
72
+ Two things behave the way `outlook-mail-ingest` behaves, because it is the same dispatcher. Participants resolve closed-set, so a thread with an unknown From/To/Cc address is skipped and its mail does not land; and attachments are not archived (the sampler writes text threads only).
73
+
74
+ Read it with `logs-read.sh --tail email-sampler`, filtering `provider=outlook`. A delegated mailbox's line carries `via=<delegator>`, which is what separates "the shared mailbox is quiet" from "the delegator stopped answering". Not yet proven on a live install.
75
+
62
76
  ## Drafting to a Contact
63
77
 
64
78
  When the admin agent drafts or sends a message to a known contact, it reads that contact's record first and applies any routing the record carries, for example a preferred send-to address, showing the result for your approval before anything is sent.
@@ -99,6 +113,11 @@ All log lines start with `[outlook-mcp]` and write to `server.log`, except the d
99
113
  | Mail ingest (per message) | `mail-ingest op=ingest id=<id> conversationId=<id> disposition=ingest` |
100
114
  | Mail ingest (per thread) | `mail-ingest op=thread-dispatched root=<key> messages=<N> participants=<N>` (or `op=thread-failed root=<key> status=<N> err=<msg>`; `status=null` with a non-`none` `err` is a spawn launch failure, not a script rejection) |
101
115
  | Mail ingest (summary) | `mail-ingest op=summary ingested=<N> discarded=<N> threadsDispatched=<N> threadsSkipped=<N> unresolved=<N>` — one per call; `threadsSkipped` with `unresolved>0` is the closed-set resolution refusing an unknown participant, not a failure |
116
+ | Sampler census, per cycle | `op=sample-census provider=outlook accounts=<n> mailboxes=<n> delegated=<n> skipped=<n>` — emitted every cycle even when every count is zero, so an install with no Outlook mailbox still says so; without it, silence would be indistinguishable from the arm never running. `accounts=0` means the accounts directory resolved somewhere that does not exist |
117
+ | Sampler, per mailbox per cycle | `op=sample mailbox=<addr> provider=outlook fetched=<n> threads=<n> skipped=<n> cursor=<iso>` — NOT in `server.log`; this arm writes to the email-sampler log, read with `logs-read.sh --tail email-sampler`. `via=<delegator>` is added on a delegated mailbox. `fetched` counts messages read, `threads` counts what actually landed in the graph, and `skipped` counts threads the closed-set participant rule refused; `fetched>0 threads=0` means nothing was written even though the cursor advanced. An idle mailbox logs `fetched=0` with no thread counts |
118
+ | Sampler, unresolved participants | `op=sample-unresolved mailbox=<addr> provider=outlook threads=<n> addresses=<csv>` — only when a thread was refused. The addresses are what to add as `:Person` or `:AdminUser` for the account; until then that mail stays out of the graph and the cursor has already passed it |
119
+ | Sampler failure | `op=sample-error mailbox=<addr> provider=outlook detail=<msg>` — the cursor is not advanced, so the same window is retried next cycle |
120
+ | Sampler inert | `op=sample-skip provider=outlook detail=no-client-id` — one line per cycle on a brand with no Azure app, instead of one auth error per mailbox |
102
121
  | Calendar list | `calendar-list account=<id> rangeDays=<N> count=<N> elapsedMs=<N>` |
103
122
  | Calendar event | `calendar-event account=<id> eventId=<trunc-12> elapsedMs=<N>` |
104
123
  | Event created | `cal-create-request account=<id> subj=<trunc-64>` then `event-created account=<id> eventId=<trunc-16> status=201`. A non-201 logs `event-create-failed ... status=<N>` and throws. A 201 whose body carried no readable id logs `event-created-id-unknown account=<id> status=201` and returns a created-but-id-unknown result (the event exists; do not retry) rather than a failure. |
@@ -182,4 +201,4 @@ Latency triage: `mail-list count=0 elapsedMs<200` consistent → permissions iss
182
201
 
183
202
  ## Out of scope
184
203
 
185
- Mail move to arbitrary folders / flag, hard (permanent) delete, removing an attachment from a **sent** message (Graph forbids it), attachment-byte archival into the graph (`outlook-mail-ingest` lands text threads only; the IMAP `:DigitalDocument` archival is a separate concern), contacts write, recurring-series occurrence editing, the `scheduling` Neo4j calendar and any auto-sync with it, OneDrive / Files, push notifications, on-premises Exchange, M365 admin scopes (`User.Read.All`, `AuditLog.Read.All`), public-agent exposure, multi-tenant federation. Mail read, full-body read, inbound attachment list/download, reply, delete (to Deleted Items), and compose — all four outbound paths carrying attachments, with a draft's attachments editable in place (add, remove, replace) — are supported (`outlook-mail-fetch-body`, `outlook-mail-attachment`, `outlook-mail-reply`, `outlook-mail-delete`, `outlook-mail-ingest`, `outlook-mail-otp-extract`, `outlook-mail-send`, `outlook-draft`, `outlook-draft-edit`, `outlook-draft-send`), and the calendar is read plus control. See `platform/plugins/outlook/PLUGIN.md` for the full out-of-scope list.
204
+ Mail move to arbitrary folders / flag, hard (permanent) delete, removing an attachment from a **sent** message (Graph forbids it), attachment-byte archival into the graph (`outlook-mail-ingest` and the email-sampler's Outlook arm both land text threads only; the IMAP `:DigitalDocument` archival is a separate concern), contacts write, recurring-series occurrence editing, the `scheduling` Neo4j calendar and any auto-sync with it, OneDrive / Files, push notifications, on-premises Exchange, M365 admin scopes (`User.Read.All`, `AuditLog.Read.All`), public-agent exposure, multi-tenant federation. Mail read, full-body read, inbound attachment list/download, reply, delete (to Deleted Items), and compose — all four outbound paths carrying attachments, with a draft's attachments editable in place (add, remove, replace) — are supported (`outlook-mail-fetch-body`, `outlook-mail-attachment`, `outlook-mail-reply`, `outlook-mail-delete`, `outlook-mail-ingest`, `outlook-mail-otp-extract`, `outlook-mail-send`, `outlook-draft`, `outlook-draft-edit`, `outlook-draft-send`), and the calendar is read plus control. See `platform/plugins/outlook/PLUGIN.md` for the full out-of-scope list.
@@ -150,7 +150,21 @@ A group's ID is a long negative number, like `-1001234567890`. To find it, @ment
150
150
 
151
151
  ## Message History
152
152
 
153
- Ask {{productName}}: "What messages has the bot received?" or "Show recent Telegram activity."
153
+ Ask {{productName}} to read a conversation back: "What did I say to Sam on Telegram?" or "Show me the whole
154
+ chat with that driver." It returns the conversation both ways round, oldest first: every message the bot
155
+ received and every one it sent. A voice note reads as its transcript, an edited message as its new wording,
156
+ and a message the other person deleted is shown struck out rather than quietly dropped.
157
+
158
+ Give it the chat id, which you can get from "Who can you reach on Telegram?". In a group, the id is the
159
+ group's own.
160
+
161
+ Two things to know:
162
+
163
+ - **History starts when this machine started storing it.** A conversation that predates the install, or that
164
+ ran while the machine was off, is not there to read. Telegram's Bot API offers no way to fetch it back.
165
+ - **If the same chat id belongs to two conversations, it will ask which you meant** rather than guess. That
166
+ happens when two of your bots both talk to the same person, and when a connected business account's
167
+ customer has the same id as your own chat with that bot.
154
168
 
155
169
  ## Troubleshooting
156
170
 
@@ -145,7 +145,20 @@ Manages the agent's own dedicated email account — IMAP for reading, SMTP for s
145
145
 
146
146
  There are two independent paths into the graph, both writing `:ConversationArchive {source:'email'}`:
147
147
 
148
- - **Deterministic ingest — the `email-sampler` service.** A per-brand, always-on systemd user unit (`<brand>-email-sampler.service`) runs the plugin's compiled `dist/sampler/main.js`. Each cycle it enumerates every connected IMAP mailbox on the install and writes every inbound message above its own cursor, with no agent turn and no approval step. Its cursor is `:EmailAccount.samplerCursorUid` + `samplerUidValidity`, written only by the sampler and kept strictly independent of `lastFetchedUid` (which `email-fetch` owns). This is the foundation a gate that reads email-in-graph can trust: every inbound message lands within one cycle. Observability: one `op=sample mailbox=<addr> fetched=<n> cursor=<uid>` line per mailbox per cycle (`op=sample-error` on failure, cursor unadvanced), read with `logs-read.sh --tail email-sampler`. Attachment archival by the sampler is deferred (see the task follow-up).
148
+ - **Deterministic ingest — the `email-sampler` service.** A per-brand, always-on systemd user unit (`<brand>-email-sampler.service`) runs the plugin's compiled `dist/sampler/main.js`. Each cycle it enumerates every connected mailbox on the install and writes every inbound message above that mailbox's own cursor, with no agent turn and no approval step. This is the foundation a gate that reads email-in-graph can trust: every inbound message lands within one cycle. Attachment archival by the sampler is deferred (see the task follow-up).
149
+
150
+ The service has **two arms in one process**, distinguished in the log by `provider=`:
151
+
152
+ - **IMAP.** Enumerates every `:EmailAccount` node. Its cursor is `:EmailAccount.samplerCursorUid` + `samplerUidValidity`, written only by the sampler and kept strictly independent of `lastFetchedUid` (which `email-fetch` owns). Stores the first 500 characters of a body, the same `previewBody` cap the read tools use.
153
+ - **Outlook.** Microsoft 365 and Outlook.com mailboxes have no `:EmailAccount` node, so this arm enumerates the on-disk credential store instead: every signed-in mailbox under `secrets/outlook/mailboxes/` whose status is `ok`, plus every shared mailbox in that account's `delegated.json`, read through its delegator's tokens. Its cursor is the newest `receivedDateTime` written plus the ids seen at that instant, held in `secrets/outlook/sampler-cursors.json` (mode 0600, keyed by SMTP address). The id set is what makes a timestamp cursor safe: `gt` would drop a message arriving at the same instant as the boundary, `ge` alone would re-ingest the boundary every cycle. Stores the **whole** body, untruncated. The arm's code lives in the outlook plugin (`plugins/outlook/mcp/src/sampler/`) beside the tools it reuses, and the email sampler imports its compiled `dist/sampler/arm.js` by path at runtime; an unbuilt outlook plugin is a logged skip that never stops IMAP ingest.
154
+
155
+ Observability for both: `logs-read.sh --tail email-sampler`. One line per mailbox per cycle, `op=sample mailbox=<addr> provider=imap|outlook fetched=<n> cursor=<pos>`, with `via=<delegator>` added on a delegated Outlook mailbox, and `op=sample-error … provider=<arm>` on failure (cursor unadvanced). `provider=` is a total filter: every arm's line carries one, so filtering never silently returns a subset. The Outlook arm also emits one `op=sample-census provider=outlook accounts=<n> mailboxes=<n> delegated=<n> skipped=<n>` per cycle, even when every count is zero, so an install with no Outlook mailbox is distinguishable from an arm that never ran. `fetched=0` on an idle mailbox is healthy; a cursor that never advances while mail arrives is the failure.
156
+
157
+ A mailbox with no cursor backfills 7 days on its first cycle, on both arms. Mail older than that window is never sampled. The Outlook arm is not yet proven on a live install.
158
+
159
+ **Retention.** The same service sweeps once per cycle, after both arms. It deletes a `:Section {source:'email'}` only when every routine on that account holding a `graph-unflagged` check has flagged it processed and it is older than `EMAIL_RETENTION_DAYS` whole days, measured from ingest. Two rules keep the sweep safe rather than destructive: an unset, zero, negative or unparseable `EMAIL_RETENTION_DAYS` prunes nothing, and an account where NO routine holds such a check prunes nothing, because an empty interested set means nobody has consented to the data being processed rather than everybody. A routine counts whether it is enabled or suspended, so enabling a parked routine never finds its backlog deleted. `:ConversationArchive` nodes are never pruned: they are merged per correspondent set and a reply bumps them, so they never age past a cutoff. A period above 36500 days is refused like any other unreadable value, because a larger one overflows the cutoff date. One line per account per cycle, `op=email-retention account=<id> interested=<k> pruned=<n> kept-outstanding=<m> repaired=<r> ageDays=<N|unset>`, with `capped=true` when the candidate batch hit its 5000-row ceiling. `pruned=0` is healthy; `interested>0` with `kept-outstanding` rising across cycles is a routine that is not processing its backlog, and `pruned>0 repaired=0` on a span taken from the middle of a thread is a `:NEXT` chain left broken. No shipped routine carries a `graph-unflagged` check yet, so every install currently logs `interested=0` and deletes nothing.
160
+
161
+ The chain repair and the delete run in ONE write transaction. Between them the surviving predecessor holds both its old edge to the doomed section and its new edge to the target, so a failure landing between the two would commit that fork, and a routine added before the next cycle would make those sections outstanding again with nothing left to repair them.
149
162
 
150
163
  - **Operator-initiated ingest — human-in-the-loop.** When the operator says "check my emails", `email-fetch` lists new messages and the operator approves each one; only the approved subset is written via `email-ingest`, which also fetches and archives attachment bytes. See `skills/email-ingest/SKILL.md` for the conversation flow and `skills/email-composition/SKILL.md` for drafting replies once a thread is stored.
151
164
 
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=retention.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retention.test.d.ts","sourceRoot":"","sources":["../../../src/sampler/__tests__/retention.test.ts"],"names":[],"mappings":""}