@rubytech/create-maxy-code 0.1.573 → 0.1.574

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 (127) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/admins-write/__tests__/index.test.ts +97 -0
  3. package/payload/platform/lib/admins-write/dist/index.d.ts +12 -0
  4. package/payload/platform/lib/admins-write/dist/index.d.ts.map +1 -1
  5. package/payload/platform/lib/admins-write/dist/index.js +27 -5
  6. package/payload/platform/lib/admins-write/dist/index.js.map +1 -1
  7. package/payload/platform/lib/admins-write/src/index.ts +35 -5
  8. package/payload/platform/lib/admins-write/vitest.config.ts +14 -0
  9. package/payload/platform/lib/dispatch-read/dist/session.d.ts.map +1 -1
  10. package/payload/platform/lib/dispatch-read/dist/session.js +19 -4
  11. package/payload/platform/lib/dispatch-read/dist/session.js.map +1 -1
  12. package/payload/platform/lib/dispatch-read/src/__tests__/session.test.ts +91 -0
  13. package/payload/platform/lib/dispatch-read/src/session.ts +25 -6
  14. package/payload/platform/plugins/admin/.claude-plugin/plugin.json +1 -1
  15. package/payload/platform/plugins/admin/PLUGIN.md +8 -3
  16. package/payload/platform/plugins/admin/hooks/__tests__/html-structure-gate.test.sh +264 -0
  17. package/payload/platform/plugins/admin/hooks/__tests__/quote-render-pdf-conformance.test.sh +122 -24
  18. package/payload/platform/plugins/admin/hooks/__tests__/specialist-dispatch-gate.test.sh +158 -0
  19. package/payload/platform/plugins/admin/hooks/html-structure-gate.sh +165 -0
  20. package/payload/platform/plugins/admin/hooks/quote-render-pdf-conformance.sh +111 -19
  21. package/payload/platform/plugins/admin/hooks/specialist-dispatch-gate.sh +159 -0
  22. package/payload/platform/plugins/admin/mcp/dist/admin-bind-classify.d.ts +27 -0
  23. package/payload/platform/plugins/admin/mcp/dist/admin-bind-classify.d.ts.map +1 -0
  24. package/payload/platform/plugins/admin/mcp/dist/admin-bind-classify.js +29 -0
  25. package/payload/platform/plugins/admin/mcp/dist/admin-bind-classify.js.map +1 -0
  26. package/payload/platform/plugins/admin/mcp/dist/binding-validate.d.ts +11 -0
  27. package/payload/platform/plugins/admin/mcp/dist/binding-validate.d.ts.map +1 -0
  28. package/payload/platform/plugins/admin/mcp/dist/binding-validate.js +20 -0
  29. package/payload/platform/plugins/admin/mcp/dist/binding-validate.js.map +1 -0
  30. package/payload/platform/plugins/admin/mcp/dist/index.js +133 -1
  31. package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
  32. package/payload/platform/plugins/admin/skills/admin-user-management/SKILL.md +32 -2
  33. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +157 -46
  34. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +8 -0
  35. package/payload/platform/plugins/docs/references/admin-ui.md +71 -31
  36. package/payload/platform/plugins/docs/references/channel-wake-and-prompt.md +38 -13
  37. package/payload/platform/plugins/docs/references/internals.md +6 -0
  38. package/payload/platform/plugins/docs/references/outlook-guide.md +31 -1
  39. package/payload/platform/plugins/docs/references/troubleshooting.md +10 -0
  40. package/payload/platform/plugins/outlook/PLUGIN.md +3 -1
  41. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-ledger-write-sites.test.d.ts +2 -0
  42. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-ledger-write-sites.test.d.ts.map +1 -0
  43. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-ledger-write-sites.test.js +177 -0
  44. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-ledger-write-sites.test.js.map +1 -0
  45. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-ledger.test.d.ts +2 -0
  46. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-ledger.test.d.ts.map +1 -0
  47. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-ledger.test.js +130 -0
  48. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-ledger.test.js.map +1 -0
  49. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-reconcile.test.d.ts +2 -0
  50. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-reconcile.test.d.ts.map +1 -0
  51. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-reconcile.test.js +162 -0
  52. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-reconcile.test.js.map +1 -0
  53. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-state-fields.test.d.ts +2 -0
  54. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-state-fields.test.d.ts.map +1 -0
  55. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-state-fields.test.js +168 -0
  56. package/payload/platform/plugins/outlook/mcp/dist/__tests__/draft-state-fields.test.js.map +1 -0
  57. package/payload/platform/plugins/outlook/mcp/dist/lib/draft-ledger.d.ts +64 -0
  58. package/payload/platform/plugins/outlook/mcp/dist/lib/draft-ledger.d.ts.map +1 -0
  59. package/payload/platform/plugins/outlook/mcp/dist/lib/draft-ledger.js +110 -0
  60. package/payload/platform/plugins/outlook/mcp/dist/lib/draft-ledger.js.map +1 -0
  61. package/payload/platform/plugins/outlook/mcp/dist/lib/draft-reconcile.d.ts +119 -0
  62. package/payload/platform/plugins/outlook/mcp/dist/lib/draft-reconcile.d.ts.map +1 -0
  63. package/payload/platform/plugins/outlook/mcp/dist/lib/draft-reconcile.js +0 -0
  64. package/payload/platform/plugins/outlook/mcp/dist/lib/draft-reconcile.js.map +1 -0
  65. package/payload/platform/plugins/outlook/mcp/dist/scripts/draft-reconcile.d.ts +8 -0
  66. package/payload/platform/plugins/outlook/mcp/dist/scripts/draft-reconcile.d.ts.map +1 -0
  67. package/payload/platform/plugins/outlook/mcp/dist/scripts/draft-reconcile.js +247 -0
  68. package/payload/platform/plugins/outlook/mcp/dist/scripts/draft-reconcile.js.map +1 -0
  69. package/payload/platform/plugins/outlook/mcp/dist/tools/draft-send.d.ts.map +1 -1
  70. package/payload/platform/plugins/outlook/mcp/dist/tools/draft-send.js +10 -0
  71. package/payload/platform/plugins/outlook/mcp/dist/tools/draft-send.js.map +1 -1
  72. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.d.ts.map +1 -1
  73. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.js +21 -0
  74. package/payload/platform/plugins/outlook/mcp/dist/tools/draft.js.map +1 -1
  75. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-fetch-body.d.ts +16 -0
  76. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-fetch-body.d.ts.map +1 -1
  77. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-fetch-body.js +5 -1
  78. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-fetch-body.js.map +1 -1
  79. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.d.ts +4 -0
  80. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.d.ts.map +1 -1
  81. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.js +2 -1
  82. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.js.map +1 -1
  83. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-reply.d.ts.map +1 -1
  84. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-reply.js +24 -0
  85. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-reply.js.map +1 -1
  86. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.d.ts.map +1 -1
  87. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.js +2 -1
  88. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-search.js.map +1 -1
  89. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-send.d.ts.map +1 -1
  90. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-send.js +29 -0
  91. package/payload/platform/plugins/outlook/mcp/dist/tools/mail-send.js.map +1 -1
  92. package/payload/platform/plugins/scheduling/mcp/dist/__tests__/draft-reconcile-throttle.test.d.ts +2 -0
  93. package/payload/platform/plugins/scheduling/mcp/dist/__tests__/draft-reconcile-throttle.test.d.ts.map +1 -0
  94. package/payload/platform/plugins/scheduling/mcp/dist/__tests__/draft-reconcile-throttle.test.js +21 -0
  95. package/payload/platform/plugins/scheduling/mcp/dist/__tests__/draft-reconcile-throttle.test.js.map +1 -0
  96. package/payload/platform/plugins/scheduling/mcp/dist/lib/draft-reconcile-throttle.d.ts +13 -0
  97. package/payload/platform/plugins/scheduling/mcp/dist/lib/draft-reconcile-throttle.d.ts.map +1 -0
  98. package/payload/platform/plugins/scheduling/mcp/dist/lib/draft-reconcile-throttle.js +10 -0
  99. package/payload/platform/plugins/scheduling/mcp/dist/lib/draft-reconcile-throttle.js.map +1 -0
  100. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +54 -0
  101. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
  102. package/payload/platform/services/claude-session-manager/dist/authored-specialist-tools.d.ts +32 -0
  103. package/payload/platform/services/claude-session-manager/dist/authored-specialist-tools.d.ts.map +1 -0
  104. package/payload/platform/services/claude-session-manager/dist/authored-specialist-tools.js +144 -0
  105. package/payload/platform/services/claude-session-manager/dist/authored-specialist-tools.js.map +1 -0
  106. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  107. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +1 -0
  108. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  109. package/payload/platform/services/claude-session-manager/dist/html-structure-census.d.ts +44 -0
  110. package/payload/platform/services/claude-session-manager/dist/html-structure-census.d.ts.map +1 -0
  111. package/payload/platform/services/claude-session-manager/dist/html-structure-census.js +185 -0
  112. package/payload/platform/services/claude-session-manager/dist/html-structure-census.js.map +1 -0
  113. package/payload/platform/services/claude-session-manager/dist/index.js +125 -0
  114. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  115. package/payload/platform/services/claude-session-manager/dist/quote-delivery-reconcile.d.ts.map +1 -1
  116. package/payload/platform/services/claude-session-manager/dist/quote-delivery-reconcile.js +7 -0
  117. package/payload/platform/services/claude-session-manager/dist/quote-delivery-reconcile.js.map +1 -1
  118. package/payload/platform/services/claude-session-manager/dist/quote-print-census.d.ts +42 -0
  119. package/payload/platform/services/claude-session-manager/dist/quote-print-census.d.ts.map +1 -0
  120. package/payload/platform/services/claude-session-manager/dist/quote-print-census.js +254 -0
  121. package/payload/platform/services/claude-session-manager/dist/quote-print-census.js.map +1 -0
  122. package/payload/platform/services/claude-session-manager/dist/specialist-census.d.ts +92 -0
  123. package/payload/platform/services/claude-session-manager/dist/specialist-census.d.ts.map +1 -0
  124. package/payload/platform/services/claude-session-manager/dist/specialist-census.js +227 -0
  125. package/payload/platform/services/claude-session-manager/dist/specialist-census.js.map +1 -0
  126. package/payload/platform/templates/account-settings.json +8 -4
  127. package/payload/server/server.js +465 -361
@@ -0,0 +1,162 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { openDrafts, sentDraftIds, classifyDraft, pruneRows, isTerminal, ledgerKey, matchSentCopy, mapDraftState, RETENTION_CAP_MS, } from "../lib/draft-reconcile.js";
4
+ const created = (id, at = "2026-08-07T11:52:25.000Z") => ({
5
+ at,
6
+ accountId: "acc-1",
7
+ mailbox: "rob@example.com",
8
+ draftId: id,
9
+ event: "created",
10
+ tool: "draft",
11
+ subject: "Extending our work",
12
+ conversationId: null,
13
+ });
14
+ const sent = (id) => ({
15
+ at: "2026-08-07T11:55:00.000Z",
16
+ accountId: "acc-1",
17
+ mailbox: "rob@example.com",
18
+ draftId: id,
19
+ event: "sent",
20
+ tool: "draft-send",
21
+ route: "draft-send",
22
+ });
23
+ test("a created row with a matching sent row is not an open draft", () => {
24
+ const rows = [created("AAA"), sent("AAA"), created("BBB")];
25
+ assert.deepEqual(openDrafts(rows).map((r) => r.draftId), ["BBB"]);
26
+ assert.ok(sentDraftIds(rows).has("AAA"));
27
+ });
28
+ test("a sent row with no created row is not an open draft and is not an error", () => {
29
+ // mail-send's attachment route and mail-reply both mint their own draft and
30
+ // send it in one call, so this shape is normal.
31
+ const rows = [sent("ONESHOT")];
32
+ assert.deepEqual(openDrafts(rows), []);
33
+ assert.ok(sentDraftIds(rows).has("ONESHOT"));
34
+ });
35
+ test("an empty draftId on a sent row does not swallow every open draft", () => {
36
+ // mail-send's sendMail route has no draft id when Graph returns none, and it
37
+ // writes "". A created row's id is never "", so nothing should match it — but
38
+ // a naive Set membership test would if "" leaked in.
39
+ const rows = [{ ...sent(""), tool: "mail-send", route: "sendMail" }, created("AAA")];
40
+ assert.deepEqual(openDrafts(rows).map((r) => r.draftId), ["AAA"]);
41
+ });
42
+ test("still in the Drafts folder is still-draft", () => {
43
+ const v = classifyDraft({ kind: "read", isDraft: true, parentFolderId: "DRAFTS", sentDateTime: "0001-01-01T00:00:00Z" }, "DRAFTS");
44
+ assert.equal(v, "still-draft");
45
+ });
46
+ test("isDraft false is sent-not-by-maxy", () => {
47
+ const v = classifyDraft({ kind: "read", isDraft: false, parentFolderId: "SENTITEMS", sentDateTime: "2026-08-07T12:26:47Z" }, "DRAFTS");
48
+ assert.equal(v, "sent-not-by-maxy");
49
+ });
50
+ test("a draft object outside the Drafts folder is sent-not-by-maxy", () => {
51
+ // The 2026-08-07 Ecolec state: the Drafts listing returned zero while a fetch
52
+ // by id still returned the draft. Keying on isDraft alone calls this healthy.
53
+ const v = classifyDraft({ kind: "read", isDraft: true, parentFolderId: "SOMEWHERE-ELSE", sentDateTime: "0001-01-01T00:00:00Z" }, "DRAFTS");
54
+ assert.equal(v, "sent-not-by-maxy");
55
+ });
56
+ test("a 404 is deleted", () => {
57
+ assert.equal(classifyDraft({ kind: "not-found" }, "DRAFTS"), "deleted");
58
+ });
59
+ test("any other Graph error is unreadable, never deleted", () => {
60
+ assert.equal(classifyDraft({ kind: "error", statusCode: 503, code: "ServiceUnavailable" }, "DRAFTS"), "unreadable");
61
+ });
62
+ test("an unresolvable Drafts folder is unreadable, never sent-not-by-maxy", () => {
63
+ // Without the folder id there is nothing to compare parentFolderId against.
64
+ // Guessing would report a healthy draft as sent by someone else.
65
+ const v = classifyDraft({ kind: "read", isDraft: true, parentFolderId: "DRAFTS", sentDateTime: null }, null);
66
+ assert.equal(v, "unreadable");
67
+ });
68
+ test("only unreadable and still-draft are non-terminal", () => {
69
+ assert.equal(isTerminal("still-draft"), false);
70
+ assert.equal(isTerminal("unreadable"), false);
71
+ assert.equal(isTerminal("sent-by-maxy"), true);
72
+ assert.equal(isTerminal("sent-not-by-maxy"), true);
73
+ assert.equal(isTerminal("deleted"), true);
74
+ });
75
+ test("pruning drops every row for a terminal draft and keeps an old still-draft row", () => {
76
+ const old = created("OLD", "2026-08-01T00:00:00.000Z");
77
+ const rows = [old, created("DONE"), sent("DONE")];
78
+ const kept = pruneRows(rows, new Set([ledgerKey("acc-1", "DONE")]), Date.parse("2026-08-07T12:00:00.000Z"));
79
+ assert.deepEqual(kept.map((r) => r.draftId), ["OLD"]);
80
+ });
81
+ test("a terminal draft in one account never prunes the same id in another", () => {
82
+ // Account scope is a property that has to hold exactly. Keying the prune on
83
+ // draftId alone would let one account's verdict delete another's open row.
84
+ const mine = created("SHARED");
85
+ const theirs = { ...created("SHARED"), accountId: "acc-2" };
86
+ const kept = pruneRows([mine, theirs], new Set([ledgerKey("acc-1", "SHARED")]), Date.parse("2026-08-07T12:00:00.000Z"));
87
+ assert.deepEqual(kept.map((r) => r.accountId), ["acc-2"]);
88
+ });
89
+ test("pruning drops any row past the hard cap even when still open", () => {
90
+ const nowMs = Date.parse("2026-08-07T12:00:00.000Z");
91
+ const ancient = created("ANCIENT", new Date(nowMs - RETENTION_CAP_MS - 1000).toISOString());
92
+ assert.deepEqual(pruneRows([ancient], new Set(), nowMs), []);
93
+ });
94
+ // --- The Sent Items proof, on a sent-not-by-maxy -------------------------
95
+ const SENT_PAGE = [
96
+ {
97
+ id: "OTHER",
98
+ subject: "Invoice reminder",
99
+ conversationId: "CONV-OTHER",
100
+ toRecipients: [{ emailAddress: { address: "someone@example.com" } }],
101
+ sentDateTime: "2026-08-07T13:00:00Z",
102
+ },
103
+ {
104
+ id: "SENTCOPY",
105
+ subject: "Extending our work",
106
+ conversationId: null,
107
+ toRecipients: [
108
+ { emailAddress: { address: "g@leaders.example" } },
109
+ { emailAddress: { address: "second@leaders.example" } },
110
+ ],
111
+ sentDateTime: "2026-08-07T12:26:47Z",
112
+ },
113
+ ];
114
+ test("a standalone draft matches its Sent Items copy by subject", () => {
115
+ // The 2026-08-07 Leaders shape: conversationId null, so a conversationId-only
116
+ // match would have found nothing and named no recipient.
117
+ const copy = matchSentCopy(SENT_PAGE, created("AAA"));
118
+ assert.equal(copy?.id, "SENTCOPY");
119
+ assert.equal(copy?.to, "g@leaders.example,second@leaders.example");
120
+ assert.equal(copy?.at, "2026-08-07T12:26:47Z");
121
+ });
122
+ test("a threaded draft matches by conversationId, not subject", () => {
123
+ const threaded = { ...created("BBB"), subject: undefined, conversationId: "CONV-OTHER" };
124
+ assert.equal(matchSentCopy(SENT_PAGE, threaded)?.id, "OTHER");
125
+ });
126
+ test("conversationId wins when both keys are present and disagree", () => {
127
+ const both = { ...created("CCC"), subject: "Extending our work", conversationId: "CONV-OTHER" };
128
+ assert.equal(matchSentCopy(SENT_PAGE, both)?.id, "OTHER");
129
+ });
130
+ test("a draft with neither key matches nothing rather than the newest item", () => {
131
+ // Naming the wrong recipient is worse than naming none.
132
+ const neither = { ...created("DDD"), subject: undefined, conversationId: null };
133
+ assert.equal(matchSentCopy(SENT_PAGE, neither), null);
134
+ });
135
+ test("no match in the page returns null", () => {
136
+ const absent = { ...created("EEE"), subject: "Nothing like this" };
137
+ assert.equal(matchSentCopy(SENT_PAGE, absent), null);
138
+ });
139
+ test("a matched copy with no recipients reports an empty string, not a crash", () => {
140
+ const page = [{ id: "X", subject: "Extending our work", conversationId: null, sentDateTime: "2026-08-07T12:00:00Z" }];
141
+ assert.equal(matchSentCopy(page, created("FFF"))?.to, "");
142
+ });
143
+ test("mapDraftState defaults every field Graph omitted", () => {
144
+ const s = mapDraftState({});
145
+ assert.deepEqual(s, { kind: "read", isDraft: false, parentFolderId: null, sentDateTime: null });
146
+ assert.deepEqual(mapDraftState(null), {
147
+ kind: "read",
148
+ isDraft: false,
149
+ parentFolderId: null,
150
+ sentDateTime: null,
151
+ });
152
+ });
153
+ test("mapDraftState carries the Ecolec shape through unchanged", () => {
154
+ const s = mapDraftState({ isDraft: true, parentFolderId: "ELSEWHERE", sentDateTime: "0001-01-01T00:00:00Z" });
155
+ assert.equal(classifyDraft(s, "DRAFTS"), "sent-not-by-maxy");
156
+ });
157
+ test("pruning keeps a row whose timestamp cannot be parsed", () => {
158
+ // Losing a row is the one outcome this file cannot afford.
159
+ const broken = created("BROKEN", "not-a-date");
160
+ assert.deepEqual(pruneRows([broken], new Set(), Date.now()).map((r) => r.draftId), ["BROKEN"]);
161
+ });
162
+ //# sourceMappingURL=draft-reconcile.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"draft-reconcile.test.js","sourceRoot":"","sources":["../../src/__tests__/draft-reconcile.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=draft-state-fields.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"draft-state-fields.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/draft-state-fields.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,168 @@
1
+ import test from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { mkdtempSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { TokenStore } from "../auth/token-store.js";
7
+ import { runMailFetchBody } from "../tools/mail-fetch-body.js";
8
+ import { runMailList } from "../tools/mail-list.js";
9
+ /**
10
+ * Task 2530. A message that has left the Drafts folder still fetches by id and,
11
+ * while it remains a draft object, still returns `from: null`. Before these
12
+ * fields there was no way for a caller to tell that state from a healthy draft,
13
+ * which is what produced the operator-reported "vanished from the folder list
14
+ * but still exists by reference".
15
+ */
16
+ function configWithToken(accountId) {
17
+ const dir = mkdtempSync(join(tmpdir(), "outlook-draftstate-"));
18
+ const tokenStore = new TokenStore(accountId, dir);
19
+ tokenStore.store("access-token", "refresh-token", 3600, { scopes: ["Mail.ReadWrite"] });
20
+ return { clientId: "test-client", tenantId: "common", accountId, tokenStore, mailboxAddress: null };
21
+ }
22
+ function mockGraph(status, bodyObj) {
23
+ const urls = [];
24
+ const original = globalThis.fetch;
25
+ globalThis.fetch = (async (input, init) => {
26
+ const req = input instanceof Request ? input : null;
27
+ const url = req ? req.url : typeof input === "string" ? input : input.toString();
28
+ if (!url.includes("graph.microsoft.com"))
29
+ return original(input, init);
30
+ urls.push(url);
31
+ return new Response(JSON.stringify(bodyObj), {
32
+ status,
33
+ headers: { "content-type": "application/json" },
34
+ });
35
+ });
36
+ return {
37
+ restore: () => {
38
+ globalThis.fetch = original;
39
+ },
40
+ urls,
41
+ };
42
+ }
43
+ test("fetch-body reports a sent message as not a draft, with its folder and send time", async () => {
44
+ const mock = mockGraph(200, {
45
+ id: "SENTCOPY",
46
+ subject: "Extending our work",
47
+ from: { emailAddress: { address: "rob@example.com", name: "Rob" } },
48
+ toRecipients: [{ emailAddress: { address: "g@example.com" } }],
49
+ ccRecipients: [],
50
+ receivedDateTime: "2026-08-07T12:26:47Z",
51
+ conversationId: null,
52
+ isRead: true,
53
+ hasAttachments: false,
54
+ isDraft: false,
55
+ parentFolderId: "SENTITEMS-ID",
56
+ sentDateTime: "2026-08-07T12:26:47Z",
57
+ lastModifiedDateTime: "2026-08-07T12:26:47Z",
58
+ body: { contentType: "text", content: "hi" },
59
+ });
60
+ try {
61
+ const r = await runMailFetchBody(configWithToken("acct-ds-1"), { messageId: "SENTCOPY" });
62
+ assert.equal(r.isDraft, false);
63
+ assert.equal(r.parentFolderId, "SENTITEMS-ID");
64
+ assert.equal(r.sentDateTime, "2026-08-07T12:26:47Z");
65
+ assert.equal(r.lastModifiedDateTime, "2026-08-07T12:26:47Z");
66
+ }
67
+ finally {
68
+ mock.restore();
69
+ }
70
+ });
71
+ test("fetch-body reports an unsent draft as a draft, with the never-sent sentinel", async () => {
72
+ const mock = mockGraph(200, {
73
+ id: "DRAFT1",
74
+ subject: "Extending our work",
75
+ from: null,
76
+ toRecipients: [{ emailAddress: { address: "g@example.com" } }],
77
+ ccRecipients: [],
78
+ receivedDateTime: "2026-08-07T11:52:25Z",
79
+ conversationId: null,
80
+ isRead: true,
81
+ hasAttachments: false,
82
+ isDraft: true,
83
+ parentFolderId: "DRAFTS-ID",
84
+ // Graph's never-sent value. Reported as-is so a reader can tell it apart
85
+ // from an absent field.
86
+ sentDateTime: "0001-01-01T00:00:00Z",
87
+ lastModifiedDateTime: "2026-08-07T11:52:25Z",
88
+ body: { contentType: "text", content: "hi" },
89
+ });
90
+ try {
91
+ const r = await runMailFetchBody(configWithToken("acct-ds-2"), { messageId: "DRAFT1" });
92
+ assert.equal(r.isDraft, true);
93
+ assert.equal(r.parentFolderId, "DRAFTS-ID");
94
+ assert.equal(r.sentDateTime, "0001-01-01T00:00:00Z");
95
+ }
96
+ finally {
97
+ mock.restore();
98
+ }
99
+ });
100
+ test("fetch-body asks Graph for all four fields", async () => {
101
+ const mock = mockGraph(200, {
102
+ id: "X",
103
+ receivedDateTime: "2026-08-07T11:52:25Z",
104
+ body: { contentType: "text", content: "" },
105
+ });
106
+ try {
107
+ await runMailFetchBody(configWithToken("acct-ds-3"), { messageId: "X" });
108
+ const url = decodeURIComponent(mock.urls[0] ?? "");
109
+ for (const field of ["isDraft", "parentFolderId", "sentDateTime", "lastModifiedDateTime"]) {
110
+ assert.ok(url.includes(field), `${field} missing from ${url}`);
111
+ }
112
+ }
113
+ finally {
114
+ mock.restore();
115
+ }
116
+ });
117
+ test("fetch-body defaults the four fields when Graph omits them", async () => {
118
+ const mock = mockGraph(200, {
119
+ id: "Y",
120
+ receivedDateTime: "2026-08-07T11:52:25Z",
121
+ body: { contentType: "text", content: "" },
122
+ });
123
+ try {
124
+ const r = await runMailFetchBody(configWithToken("acct-ds-4"), { messageId: "Y" });
125
+ assert.equal(r.isDraft, false);
126
+ assert.equal(r.parentFolderId, null);
127
+ assert.equal(r.sentDateTime, null);
128
+ assert.equal(r.lastModifiedDateTime, null);
129
+ }
130
+ finally {
131
+ mock.restore();
132
+ }
133
+ });
134
+ test("mail-list returns isDraft per item and asks Graph for it", async () => {
135
+ const mock = mockGraph(200, {
136
+ value: [
137
+ {
138
+ id: "DRAFT1",
139
+ subject: "Extending our work",
140
+ from: null,
141
+ receivedDateTime: "2026-08-07T11:52:25Z",
142
+ bodyPreview: "Hi George",
143
+ isRead: true,
144
+ hasAttachments: false,
145
+ isDraft: true,
146
+ },
147
+ {
148
+ id: "INBOX1",
149
+ subject: "RE: Extending our work",
150
+ from: { emailAddress: { address: "g@example.com" } },
151
+ receivedDateTime: "2026-08-07T12:55:31Z",
152
+ bodyPreview: "Thanks Rob",
153
+ isRead: false,
154
+ hasAttachments: false,
155
+ },
156
+ ],
157
+ });
158
+ try {
159
+ const r = await runMailList(configWithToken("acct-ds-5"), { folder: "Drafts" });
160
+ assert.equal(r.items[0].isDraft, true);
161
+ assert.equal(r.items[1].isDraft, false, "an omitted isDraft defaults to false");
162
+ assert.ok(decodeURIComponent(mock.urls[0] ?? "").includes("isDraft"));
163
+ }
164
+ finally {
165
+ mock.restore();
166
+ }
167
+ });
168
+ //# sourceMappingURL=draft-state-fields.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"draft-state-fields.test.js","sourceRoot":"","sources":["../../src/__tests__/draft-state-fields.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Append-only record of every draft this plugin created and every draft it sent.
3
+ *
4
+ * It exists because nothing else records that a draft was ours. A Drafts folder
5
+ * that empties while a copy turns up in Sent Items is equally consistent with an
6
+ * external mail client, so the reconcile had no left-hand side to compare live
7
+ * mailbox state against, and an agent asked "did you send this?" could only
8
+ * infer — which on 2026-08-07 produced a confident, wrong self-accusation
9
+ * (Task 2530).
10
+ *
11
+ * Every row states an action that completed: the `created` row is written after
12
+ * Graph returned the draft id, the `sent` rows after the send returned. None of
13
+ * them is an intent line.
14
+ */
15
+ export interface DraftLedgerEntry {
16
+ at: string;
17
+ accountId: string;
18
+ mailbox: string;
19
+ draftId: string;
20
+ event: "created" | "sent";
21
+ tool: "draft" | "draft-send" | "mail-send" | "mail-reply";
22
+ /** `sent` rows only — which transport actually carried it. */
23
+ route?: "sendMail" | "draft-send" | "reply";
24
+ /** `created` rows only. The Sent Items match key when conversationId is null. */
25
+ subject?: string;
26
+ /** `created` rows only. Null on the standalone path — Graph assigns none there,
27
+ * which is exactly the case where `subject` has to carry the match. */
28
+ conversationId?: string | null;
29
+ }
30
+ /**
31
+ * Null when PLATFORM_ROOT is unset.
32
+ *
33
+ * Deliberately no directory-relative fallback. The email ledger has one
34
+ * (`sent-ledger.ts:31`) and it resolves four levels up from `dist/lib` to
35
+ * `plugins/`, which is not the platform root — harmless only because the MCP is
36
+ * always spawned with PLATFORM_ROOT set, and actively harmful under test, where
37
+ * it writes a stray `plugins/config/` into the source tree. No platform root
38
+ * means there is no platform to write a ledger under, and saying so is more
39
+ * precise than guessing a path.
40
+ */
41
+ export declare function ledgerPath(): string | null;
42
+ /**
43
+ * Append one row. Pure append, never read-modify-write.
44
+ *
45
+ * Two sends completing together would otherwise interleave read/read/write/write
46
+ * and silently drop a row, which would surface later as a phantom
47
+ * `sent-not-by-maxy`. O_APPEND keeps concurrent single-line writes from tearing.
48
+ * Trimming is the reconcile's job, from its own single process.
49
+ *
50
+ * A failure here logs and returns. It must never fail the tool call: the mail
51
+ * matters more than the row about it.
52
+ */
53
+ export declare function appendDraftLedger(entry: DraftLedgerEntry): void;
54
+ export declare function readDraftLedger(): DraftLedgerEntry[];
55
+ /**
56
+ * Replace the file with `rows`.
57
+ *
58
+ * Called only from the reconcile's single process, never from a tool: rewriting
59
+ * is the one operation that can lose a concurrent append, so it happens as
60
+ * rarely as possible and never on the send path. A send landing inside the
61
+ * rewrite window loses at most its own observability row, never the mail.
62
+ */
63
+ export declare function writeDraftLedger(rows: DraftLedgerEntry[]): void;
64
+ //# sourceMappingURL=draft-ledger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"draft-ledger.d.ts","sourceRoot":"","sources":["../../src/lib/draft-ledger.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,SAAS,GAAG,MAAM,CAAC;IAC1B,IAAI,EAAE,OAAO,GAAG,YAAY,GAAG,WAAW,GAAG,YAAY,CAAC;IAC1D,8DAA8D;IAC9D,KAAK,CAAC,EAAE,UAAU,GAAG,YAAY,GAAG,OAAO,CAAC;IAC5C,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;4EACwE;IACxE,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,IAAI,MAAM,GAAG,IAAI,CAI1C;AAgBD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAiC/D;AAED,wBAAgB,eAAe,IAAI,gBAAgB,EAAE,CAQpD;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,gBAAgB,EAAE,GAAG,IAAI,CAO/D"}
@@ -0,0 +1,110 @@
1
+ import { existsSync, readFileSync, writeFileSync, appendFileSync, mkdirSync } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
3
+ import { log } from "./log.js";
4
+ /**
5
+ * Null when PLATFORM_ROOT is unset.
6
+ *
7
+ * Deliberately no directory-relative fallback. The email ledger has one
8
+ * (`sent-ledger.ts:31`) and it resolves four levels up from `dist/lib` to
9
+ * `plugins/`, which is not the platform root — harmless only because the MCP is
10
+ * always spawned with PLATFORM_ROOT set, and actively harmful under test, where
11
+ * it writes a stray `plugins/config/` into the source tree. No platform root
12
+ * means there is no platform to write a ledger under, and saying so is more
13
+ * precise than guessing a path.
14
+ */
15
+ export function ledgerPath() {
16
+ const root = process.env.PLATFORM_ROOT;
17
+ if (!root || !root.trim())
18
+ return null;
19
+ return resolve(root, "config/outlook-draft-ledger.jsonl");
20
+ }
21
+ function parseLines(raw) {
22
+ const out = [];
23
+ for (const line of raw.split("\n")) {
24
+ if (!line.trim())
25
+ continue;
26
+ try {
27
+ out.push(JSON.parse(line));
28
+ }
29
+ catch {
30
+ // A malformed line is skipped, never thrown on: this file is
31
+ // observability, and a parse error must not take out a send.
32
+ }
33
+ }
34
+ return out;
35
+ }
36
+ /**
37
+ * Append one row. Pure append, never read-modify-write.
38
+ *
39
+ * Two sends completing together would otherwise interleave read/read/write/write
40
+ * and silently drop a row, which would surface later as a phantom
41
+ * `sent-not-by-maxy`. O_APPEND keeps concurrent single-line writes from tearing.
42
+ * Trimming is the reconcile's job, from its own single process.
43
+ *
44
+ * A failure here logs and returns. It must never fail the tool call: the mail
45
+ * matters more than the row about it.
46
+ */
47
+ export function appendDraftLedger(entry) {
48
+ const path = ledgerPath();
49
+ if (path === null) {
50
+ log({
51
+ event: "draft-ledger-failed",
52
+ op: entry.event,
53
+ account: entry.accountId,
54
+ draftId: entry.draftId,
55
+ error: "PLATFORM_ROOT is not set",
56
+ });
57
+ return;
58
+ }
59
+ try {
60
+ mkdirSync(dirname(path), { recursive: true });
61
+ appendFileSync(path, JSON.stringify(entry) + "\n", { mode: 0o600 });
62
+ log({
63
+ event: "draft-ledger",
64
+ op: entry.event,
65
+ account: entry.accountId,
66
+ mailbox: entry.mailbox,
67
+ draftId: entry.draftId,
68
+ tool: entry.tool,
69
+ route: entry.route ?? null,
70
+ });
71
+ }
72
+ catch (err) {
73
+ log({
74
+ event: "draft-ledger-failed",
75
+ op: entry.event,
76
+ account: entry.accountId,
77
+ draftId: entry.draftId,
78
+ error: err.message,
79
+ });
80
+ }
81
+ }
82
+ export function readDraftLedger() {
83
+ const path = ledgerPath();
84
+ if (path === null || !existsSync(path))
85
+ return [];
86
+ try {
87
+ return parseLines(readFileSync(path, "utf8"));
88
+ }
89
+ catch {
90
+ return [];
91
+ }
92
+ }
93
+ /**
94
+ * Replace the file with `rows`.
95
+ *
96
+ * Called only from the reconcile's single process, never from a tool: rewriting
97
+ * is the one operation that can lose a concurrent append, so it happens as
98
+ * rarely as possible and never on the send path. A send landing inside the
99
+ * rewrite window loses at most its own observability row, never the mail.
100
+ */
101
+ export function writeDraftLedger(rows) {
102
+ const path = ledgerPath();
103
+ if (path === null)
104
+ throw new Error("outlook draft ledger: PLATFORM_ROOT is not set");
105
+ mkdirSync(dirname(path), { recursive: true });
106
+ writeFileSync(path, rows.map((r) => JSON.stringify(r)).join("\n") + (rows.length ? "\n" : ""), {
107
+ mode: 0o600,
108
+ });
109
+ }
110
+ //# sourceMappingURL=draft-ledger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"draft-ledger.js","sourceRoot":"","sources":["../../src/lib/draft-ledger.ts"],"names":[],"mappings":"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"}