@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,119 @@
1
+ import type { DraftLedgerEntry } from "./draft-ledger.js";
2
+ /**
3
+ * Classification for one ledger draft, and the pruning rule for the file.
4
+ *
5
+ * Pure and injectable: the Graph read happens in scripts/draft-reconcile.ts and
6
+ * arrives here as a `DraftState`, so every verdict is testable without a live
7
+ * mailbox (Task 2530).
8
+ */
9
+ export type DraftVerdict = "still-draft" | "sent-by-maxy" | "sent-not-by-maxy" | "deleted" | "unreadable";
10
+ export type DraftState = {
11
+ kind: "read";
12
+ isDraft: boolean;
13
+ parentFolderId: string | null;
14
+ sentDateTime: string | null;
15
+ } | {
16
+ kind: "not-found";
17
+ } | {
18
+ kind: "error";
19
+ statusCode: number;
20
+ code: string;
21
+ };
22
+ /**
23
+ * Hard age cap. Terminal rows are pruned as soon as they are classified; this
24
+ * bounds the file even if a draft never reaches a terminal verdict.
25
+ *
26
+ * 90 days, not the email ledger's 48 hours (`sent-ledger.ts:28`). That file's
27
+ * window is a 24h sweep, so 48h leaves margin. A draft awaiting the operator's
28
+ * review legitimately sits for days, and a short prune would drop exactly the
29
+ * rows that carry the point.
30
+ */
31
+ export declare const RETENTION_CAP_MS: number;
32
+ /**
33
+ * The ids this plugin actually sent.
34
+ *
35
+ * An empty id is excluded. mail-send's `sendMail` route has no draft id when
36
+ * Graph returns none and writes "", and a created row's id is never "" — but
37
+ * letting "" into the set would make a naive membership test match nothing
38
+ * useful while looking like it worked.
39
+ */
40
+ export declare function sentDraftIds(rows: DraftLedgerEntry[]): Set<string>;
41
+ /**
42
+ * Created rows with no matching sent row.
43
+ *
44
+ * A sent row with no created row is normal, not an error: mail-send's
45
+ * attachment route and mail-reply both mint their own draft and send it in one
46
+ * call, so they never write a `created` row.
47
+ */
48
+ export declare function openDrafts(rows: DraftLedgerEntry[]): DraftLedgerEntry[];
49
+ /**
50
+ * `parentFolderId` is the discriminator, not `isDraft`.
51
+ *
52
+ * Two states both mean the draft is no longer an open draft in that folder, and
53
+ * only one of them sets `isDraft: false`. On 2026-08-07 the Ecolec Drafts
54
+ * listing returned zero items while the same draft id still fetched and still
55
+ * returned `from: null` eleven seconds later — a draft object outside the
56
+ * Drafts folder. Keying on `isDraft` alone calls that healthy.
57
+ *
58
+ * A null `draftsFolderId` is `unreadable`, never a verdict: with nothing to
59
+ * compare against, reporting a healthy draft as sent by someone else is the one
60
+ * mistake this check must not make.
61
+ */
62
+ export declare function classifyDraft(state: DraftState, draftsFolderId: string | null): DraftVerdict;
63
+ /**
64
+ * A verdict that will not change, so its rows can be dropped.
65
+ *
66
+ * `unreadable` is deliberately not terminal — a transient 503 must not retire a
67
+ * draft — and neither is `still-draft`, which is the state the reconcile exists
68
+ * to keep watching.
69
+ */
70
+ export declare function isTerminal(verdict: DraftVerdict): boolean;
71
+ /** One Sent Items row, as much of it as the match needs. */
72
+ export interface SentItemsRow {
73
+ id?: unknown;
74
+ subject?: unknown;
75
+ conversationId?: unknown;
76
+ toRecipients?: Array<{
77
+ emailAddress?: {
78
+ address?: string;
79
+ };
80
+ }>;
81
+ sentDateTime?: unknown;
82
+ }
83
+ export interface SentCopy {
84
+ id: string;
85
+ to: string;
86
+ at: string;
87
+ }
88
+ /**
89
+ * Find the copy that actually went out, among a page of Sent Items.
90
+ *
91
+ * Matched by conversationId when the draft carries one, else by subject. The
92
+ * 2026-08-07 Leaders draft was standalone with `conversationId: null`, so a
93
+ * conversationId-only match would have found nothing and the operator would
94
+ * still not know who received it.
95
+ *
96
+ * A row with neither key matches nothing rather than matching the newest item:
97
+ * naming the wrong recipient is worse than naming none.
98
+ */
99
+ export declare function matchSentCopy(rows: SentItemsRow[], draft: DraftLedgerEntry): SentCopy | null;
100
+ /** Graph's message projection, narrowed to the three fields a verdict needs. */
101
+ export declare function mapDraftState(json: {
102
+ isDraft?: boolean;
103
+ parentFolderId?: string | null;
104
+ sentDateTime?: string | null;
105
+ } | null): DraftState;
106
+ /**
107
+ * The prune key: account AND draft, never the draft id alone.
108
+ *
109
+ * Graph ids are long enough that a collision across two accounts is unlikely,
110
+ * but account scope is a property that has to hold exactly — a shared key would
111
+ * let one account's terminal verdict delete another account's open row.
112
+ */
113
+ export declare function ledgerKey(accountId: string, draftId: string): string;
114
+ /**
115
+ * Drop every row for a draft that reached a terminal verdict, and every row past
116
+ * the hard cap. Called once per run from the reconcile's single process.
117
+ */
118
+ export declare function pruneRows(rows: DraftLedgerEntry[], terminalKeys: Set<string>, nowMs: number): DraftLedgerEntry[];
119
+ //# sourceMappingURL=draft-reconcile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"draft-reconcile.d.ts","sourceRoot":"","sources":["../../src/lib/draft-reconcile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GACpB,aAAa,GACb,cAAc,GACd,kBAAkB,GAClB,SAAS,GACT,YAAY,CAAC;AAEjB,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAC9F;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,GACrB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAExD;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,QAA2B,CAAC;AAEzD;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,gBAAgB,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAIlE;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,gBAAgB,EAAE,GAAG,gBAAgB,EAAE,CAGvE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI,GAAG,YAAY,CAM5F;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAEzD;AAED,4DAA4D;AAC5D,MAAM,WAAW,YAAY;IAC3B,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,YAAY,CAAC,EAAE,KAAK,CAAC;QAAE,YAAY,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC,CAAC;IAC9D,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,KAAK,EAAE,gBAAgB,GAAG,QAAQ,GAAG,IAAI,CAgB5F;AAED,gFAAgF;AAChF,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,GAAG,IAAI,GAAG,UAAU,CAOpB;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEpE;AAED;;;GAGG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,gBAAgB,EAAE,EACxB,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,EACzB,KAAK,EAAE,MAAM,GACZ,gBAAgB,EAAE,CASpB"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"draft-reconcile.js","sourceRoot":"","sources":["../../src/lib/draft-reconcile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ interface VendorApp {
2
+ clientId: string;
3
+ tenantId: string;
4
+ }
5
+ /** The brand's Outlook app, read from disk because the heartbeat's env has none. */
6
+ export declare function readVendorApp(platformRoot: string): VendorApp | null;
7
+ export {};
8
+ //# sourceMappingURL=draft-reconcile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"draft-reconcile.d.ts","sourceRoot":"","sources":["../../src/scripts/draft-reconcile.ts"],"names":[],"mappings":"AA+CA,UAAU,SAAS;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,oFAAoF;AACpF,wBAAgB,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAcpE"}
@@ -0,0 +1,247 @@
1
+ /**
2
+ * Standing reconciliation of every draft this plugin created, one pass over
3
+ * every local account.
4
+ *
5
+ * Spawned by the scheduling heartbeat on an hourly throttle rather than
6
+ * imported by it: the scheduling plugin builds with its own rootDir and does
7
+ * not carry the Graph client, so the Graph work stays on this side of the
8
+ * boundary — the rule Task 1822 set for the email sent-copy sweep.
9
+ *
10
+ * It has to be a standing check rather than another action log. The failure it
11
+ * exists for emits no event: nobody calls a tool when an external mail client
12
+ * sends a draft, so there is nothing to hang a log line on, and re-running the
13
+ * code will not surface it. On 2026-08-07 a Maxy-created draft left the Drafts
14
+ * folder at 12:26:47Z and the platform recorded nothing at all.
15
+ *
16
+ * The heartbeat's cron line carries only PLATFORM_ROOT, NEO4J_URI and
17
+ * MAXY_UI_INTERNAL_PORT — no OUTLOOK_CLIENT_ID — so the vendor app is read from
18
+ * platform/config/brand.json here, the same source and the same reason as
19
+ * scheduling's connector-freebusy.ts:83.
20
+ *
21
+ * Exit code is 0 whenever the reconcile ran, including when it found a
22
+ * sent-not-by-maxy: a finding is a result, not a failure of the check. Non-zero
23
+ * means it could not run, which is what keeps "failing" and "did not run"
24
+ * distinguishable.
25
+ */
26
+ import { existsSync, readFileSync } from "node:fs";
27
+ import { resolve } from "node:path";
28
+ import { enumerateValidAccountIds } from "../../../../../lib/account-enumeration/dist/index.js";
29
+ import { MailboxRegistry } from "../auth/mailbox-registry.js";
30
+ import { callGraph, classifyGraphError } from "../lib/graph-client.js";
31
+ import { log } from "../lib/log.js";
32
+ import { readDraftLedger, writeDraftLedger } from "../lib/draft-ledger.js";
33
+ import { openDrafts, sentDraftIds, classifyDraft, pruneRows, isTerminal, ledgerKey, mapDraftState, matchSentCopy, } from "../lib/draft-reconcile.js";
34
+ /** The brand's Outlook app, read from disk because the heartbeat's env has none. */
35
+ export function readVendorApp(platformRoot) {
36
+ const path = resolve(platformRoot, "config", "brand.json");
37
+ try {
38
+ const brand = JSON.parse(readFileSync(path, "utf-8"));
39
+ const clientId = typeof brand.outlookClientId === "string" ? brand.outlookClientId : "";
40
+ if (!clientId)
41
+ return null;
42
+ const tenantId = typeof brand.outlookTenantId === "string" && brand.outlookTenantId
43
+ ? brand.outlookTenantId
44
+ : "common";
45
+ return { clientId, tenantId };
46
+ }
47
+ catch {
48
+ return null;
49
+ }
50
+ }
51
+ /**
52
+ * The Drafts folder's opaque id.
53
+ *
54
+ * `parentFolderId` on a message is an opaque id, never the well-known `drafts`
55
+ * token, so it can only be compared against a resolved one. Null on failure,
56
+ * which classifyDraft turns into `unreadable` rather than a verdict.
57
+ */
58
+ async function resolveDraftsFolderId(config) {
59
+ try {
60
+ const json = await callGraph(config, (client) => client.api("/me/mailFolders/drafts").select("id").get(), { tool: "draft-reconcile" });
61
+ return json?.id ?? null;
62
+ }
63
+ catch {
64
+ return null;
65
+ }
66
+ }
67
+ async function readDraftState(config, draftId) {
68
+ try {
69
+ const json = await callGraph(config, (client) => client
70
+ .api(`/me/messages/${encodeURIComponent(draftId)}`)
71
+ .select("isDraft,parentFolderId,sentDateTime,lastModifiedDateTime")
72
+ .get(), { tool: "draft-reconcile", target: draftId });
73
+ return mapDraftState(json);
74
+ }
75
+ catch (err) {
76
+ const cls = classifyGraphError(err);
77
+ if (cls.statusCode === 404)
78
+ return { kind: "not-found" };
79
+ // Two of the rate-limit classes carry no `code` field at all, so it is read
80
+ // off the union rather than assumed present.
81
+ const code = "code" in cls ? cls.code : cls.kind;
82
+ return { kind: "error", statusCode: cls.statusCode, code };
83
+ }
84
+ }
85
+ /**
86
+ * The newest Sent Items, fetched at most once per mailbox per run.
87
+ *
88
+ * Several drafts in one mailbox can come back sent-not-by-maxy together, and
89
+ * they all match against the same page — refetching it per draft would be N
90
+ * identical Graph calls. A failed fetch caches the empty page too, so one
91
+ * outage does not become N retries inside a single run.
92
+ */
93
+ async function sentItemsPage(config) {
94
+ try {
95
+ const page = await callGraph(config, (client) => client
96
+ .api("/me/mailFolders/sentitems/messages")
97
+ .select("id,subject,conversationId,toRecipients,sentDateTime")
98
+ .top(25)
99
+ .orderby("sentDateTime desc")
100
+ .get(), { tool: "draft-reconcile" });
101
+ return page?.value ?? [];
102
+ }
103
+ catch {
104
+ return [];
105
+ }
106
+ }
107
+ function emptyCounts() {
108
+ return {
109
+ "still-draft": 0,
110
+ "sent-by-maxy": 0,
111
+ "sent-not-by-maxy": 0,
112
+ deleted: 0,
113
+ unreadable: 0,
114
+ };
115
+ }
116
+ async function reconcileAccount(accountId, accountsDir, vendor, rows, nowMs, terminal) {
117
+ const mine = rows.filter((r) => r.accountId === accountId);
118
+ if (mine.length === 0)
119
+ return;
120
+ const counts = emptyCounts();
121
+ const alreadySent = sentDraftIds(mine);
122
+ // A created row whose id also carries a sent row is answered by the ledger
123
+ // alone. No Graph call: the ledger already knows, and asking would spend a
124
+ // request to learn nothing.
125
+ for (const r of mine) {
126
+ if (r.event === "created" && alreadySent.has(r.draftId)) {
127
+ counts["sent-by-maxy"]++;
128
+ terminal.add(ledgerKey(accountId, r.draftId));
129
+ }
130
+ }
131
+ const registry = new MailboxRegistry(accountId, accountsDir);
132
+ const byMailbox = new Map();
133
+ for (const r of openDrafts(mine)) {
134
+ const list = byMailbox.get(r.mailbox) ?? [];
135
+ list.push(r);
136
+ byMailbox.set(r.mailbox, list);
137
+ }
138
+ for (const [mailbox, drafts] of byMailbox) {
139
+ const resolved = registry.resolve(mailbox || undefined);
140
+ if (resolved.kind !== "ok") {
141
+ // Not terminal: a mailbox that is momentarily unresolvable must not
142
+ // retire a draft that is still sitting there.
143
+ for (const r of drafts) {
144
+ counts.unreadable++;
145
+ log({
146
+ event: "draft-reconcile",
147
+ account: accountId,
148
+ mailbox,
149
+ draftId: r.draftId,
150
+ outcome: "unreadable",
151
+ createdTs: r.at,
152
+ reason: `mailbox-${resolved.kind}`,
153
+ });
154
+ }
155
+ continue;
156
+ }
157
+ const config = {
158
+ clientId: vendor.clientId,
159
+ tenantId: vendor.tenantId,
160
+ accountId,
161
+ tokenStore: resolved.store,
162
+ mailboxPathPrefix: resolved.pathPrefix,
163
+ mailboxAddress: resolved.delegatedAddress ?? resolved.descriptor.mail ?? null,
164
+ };
165
+ const draftsFolderId = await resolveDraftsFolderId(config);
166
+ let sentPage = null;
167
+ for (const r of drafts) {
168
+ const state = await readDraftState(config, r.draftId);
169
+ const outcome = classifyDraft(state, draftsFolderId);
170
+ counts[outcome]++;
171
+ if (isTerminal(outcome))
172
+ terminal.add(ledgerKey(accountId, r.draftId));
173
+ const createdMs = Date.parse(r.at);
174
+ const fields = {
175
+ event: "draft-reconcile",
176
+ account: accountId,
177
+ mailbox,
178
+ draftId: r.draftId,
179
+ outcome,
180
+ createdTs: r.at,
181
+ sentDateTime: state.kind === "read" ? state.sentDateTime : null,
182
+ parentFolderId: state.kind === "read" ? state.parentFolderId : null,
183
+ ageMs: Number.isFinite(createdMs) ? nowMs - createdMs : null,
184
+ };
185
+ if (state.kind === "error") {
186
+ fields.status = state.statusCode;
187
+ fields.code = state.code;
188
+ }
189
+ if (outcome === "sent-not-by-maxy") {
190
+ if (sentPage === null)
191
+ sentPage = await sentItemsPage(config);
192
+ const copy = matchSentCopy(sentPage, r);
193
+ fields.sentCopyId = copy?.id ?? "none";
194
+ fields.sentCopyTo = copy?.to ?? "none";
195
+ fields.sentCopyAt = copy?.at ?? "none";
196
+ }
197
+ log(fields);
198
+ }
199
+ }
200
+ // Emitted on every pass including an all-zero one: an absent census line is
201
+ // the signal that the reconcile stopped running, and that only works if a
202
+ // quiet pass still speaks.
203
+ log({
204
+ event: "draft-reconcile-census",
205
+ account: accountId,
206
+ total: mine.filter((r) => r.event === "created").length,
207
+ stillDraft: counts["still-draft"],
208
+ sentByMaxy: counts["sent-by-maxy"],
209
+ sentNotByMaxy: counts["sent-not-by-maxy"],
210
+ deleted: counts.deleted,
211
+ unreadable: counts.unreadable,
212
+ });
213
+ }
214
+ async function main() {
215
+ const platformRoot = process.env.PLATFORM_ROOT;
216
+ if (!platformRoot)
217
+ throw new Error("PLATFORM_ROOT is not set");
218
+ const vendor = readVendorApp(platformRoot);
219
+ if (!vendor) {
220
+ // A brand that ships no Outlook vendor app has nothing to reconcile. A
221
+ // distinct outcome from "did not run", and it has to say so.
222
+ log({ event: "draft-reconcile-skipped", reason: "no-vendor-app" });
223
+ return;
224
+ }
225
+ const accountsDir = resolve(platformRoot, "..", "data/accounts");
226
+ const accountIds = existsSync(accountsDir) ? enumerateValidAccountIds(accountsDir) : [];
227
+ const rows = readDraftLedger();
228
+ const terminal = new Set();
229
+ const nowMs = Date.now();
230
+ for (const accountId of accountIds) {
231
+ await reconcileAccount(accountId, accountsDir, vendor, rows, nowMs, terminal);
232
+ }
233
+ const kept = pruneRows(rows, terminal, nowMs);
234
+ if (kept.length !== rows.length)
235
+ writeDraftLedger(kept);
236
+ log({
237
+ event: "draft-reconcile-pruned",
238
+ accounts: accountIds.length,
239
+ removed: rows.length - kept.length,
240
+ kept: kept.length,
241
+ });
242
+ }
243
+ main().catch((err) => {
244
+ log({ event: "draft-reconcile-failed", error: err.message });
245
+ process.exit(1);
246
+ });
247
+ //# sourceMappingURL=draft-reconcile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"draft-reconcile.js","sourceRoot":"","sources":["../../src/scripts/draft-reconcile.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"draft-send.d.ts","sourceRoot":"","sources":["../../src/tools/draft-send.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAIhE,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,eAAe,CAAC,CAoB1B"}
1
+ {"version":3,"file":"draft-send.d.ts","sourceRoot":"","sources":["../../src/tools/draft-send.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAKhE,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE,aAAa,GAClB,OAAO,CAAC,eAAe,CAAC,CA6B1B"}
@@ -1,5 +1,6 @@
1
1
  import { postGraph } from "../lib/graph-client.js";
2
2
  import { log } from "../lib/log.js";
3
+ import { appendDraftLedger } from "../lib/draft-ledger.js";
3
4
  /**
4
5
  * Send an existing draft via Graph `POST /me/messages/{id}/send` (empty body).
5
6
  * Graph consumes the draft and returns 202. Success is asserted on the 202
@@ -16,6 +17,15 @@ export async function runDraftSend(config, args) {
16
17
  try {
17
18
  const { status } = await postGraph(config, `/me/messages/${encodeURIComponent(draftId)}/send`, undefined, { tool: "draft-send" });
18
19
  log({ event: "draft-sent", account, id: draftId, status });
20
+ appendDraftLedger({
21
+ at: new Date().toISOString(),
22
+ accountId: account,
23
+ mailbox: config.mailboxAddress ?? "",
24
+ draftId,
25
+ event: "sent",
26
+ tool: "draft-send",
27
+ route: "draft-send",
28
+ });
19
29
  return { status: "sent", draftId, httpStatus: status };
20
30
  }
21
31
  catch (err) {
@@ -1 +1 @@
1
- {"version":3,"file":"draft-send.js","sourceRoot":"","sources":["../../src/tools/draft-send.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AAYpC;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAAyB,EACzB,IAAmB;IAEnB,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC;IACjC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;IACpC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC9D,CAAC;IACD,GAAG,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IAC3D,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,SAAS,CAChC,MAAM,EACN,gBAAgB,kBAAkB,CAAC,OAAO,CAAC,OAAO,EAClD,SAAS,EACT,EAAE,IAAI,EAAE,YAAY,EAAE,CACvB,CAAC;QACF,GAAG,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;IACzD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QACzF,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"draft-send.js","sourceRoot":"","sources":["../../src/tools/draft-send.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAY3D;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAAyB,EACzB,IAAmB;IAEnB,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC;IACjC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;IACpC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC9D,CAAC;IACD,GAAG,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IAC3D,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,SAAS,CAChC,MAAM,EACN,gBAAgB,kBAAkB,CAAC,OAAO,CAAC,OAAO,EAClD,SAAS,EACT,EAAE,IAAI,EAAE,YAAY,EAAE,CACvB,CAAC;QACF,GAAG,CAAC,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC;QAC3D,iBAAiB,CAAC;YAChB,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YAC5B,SAAS,EAAE,OAAO;YAClB,OAAO,EAAE,MAAM,CAAC,cAAc,IAAI,EAAE;YACpC,OAAO;YACP,KAAK,EAAE,MAAM;YACb,IAAI,EAAE,YAAY;YAClB,KAAK,EAAE,YAAY;SACpB,CAAC,CAAC;QACH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC;IACzD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAG,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QACzF,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"draft.d.ts","sourceRoot":"","sources":["../../src/tools/draft.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAKpE;;;;;;GAMG;AACH,MAAM,WAAW,UAAW,SAAQ,IAAI,CAAC,YAAY,EAAE,IAAI,GAAG,SAAS,CAAC;IACtE,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;qFACiF;IACjF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,uEAAuE;IACvE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB;;;yDAGqD;IACrD,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B;;;;;4DAKwD;IACxD,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAsB,QAAQ,CAAC,MAAM,EAAE,iBAAiB,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CA8BjG"}
1
+ {"version":3,"file":"draft.d.ts","sourceRoot":"","sources":["../../src/tools/draft.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAMpE;;;;;;GAMG;AACH,MAAM,WAAW,UAAW,SAAQ,IAAI,CAAC,YAAY,EAAE,IAAI,GAAG,SAAS,CAAC;IACtE,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;qFACiF;IACjF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,uEAAuE;IACvE,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB;;;yDAGqD;IACrD,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B;;;;;4DAKwD;IACxD,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAsB,QAAQ,CAAC,MAAM,EAAE,iBAAiB,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,CAmDjG"}
@@ -2,6 +2,7 @@ import { postGraph } from "../lib/graph-client.js";
2
2
  import { buildMessage } from "../lib/message.js";
3
3
  import { attachFilesToDraft, resolveAttachmentsOrRefuse } from "../lib/attach.js";
4
4
  import { log } from "../lib/log.js";
5
+ import { appendDraftLedger } from "../lib/draft-ledger.js";
5
6
  import { addrs, applyCcBccToDraft } from "../lib/recipients.js";
6
7
  /**
7
8
  * Create a draft via Graph, attach any files to it, and return an id naming a
@@ -54,6 +55,26 @@ export async function runDraft(config, input) {
54
55
  const { draftId, conversationId, to } = threaded
55
56
  ? await createThreadedDraft(config, input)
56
57
  : await createStandaloneDraft(config, input);
58
+ // Written the moment the draft exists, BEFORE the attach. An attach that
59
+ // fails Graph-side rethrows but leaves the draft in Drafts (see the orphan
60
+ // note above), and a row written after the attach would miss exactly that
61
+ // draft — the reconcile would never watch the one case that already goes
62
+ // wrong (Task 2530).
63
+ //
64
+ // `subject` is undefined on the threaded path — Graph sets it there — and
65
+ // that is correct: a threaded draft carries a conversationId, which is the
66
+ // stronger Sent Items match key. A standalone draft has no conversationId
67
+ // and always has a subject.
68
+ appendDraftLedger({
69
+ at: new Date().toISOString(),
70
+ accountId: account,
71
+ mailbox: config.mailboxAddress ?? "",
72
+ draftId,
73
+ event: "created",
74
+ tool: "draft",
75
+ subject: input.subject,
76
+ conversationId,
77
+ });
57
78
  await attachFilesToDraft(config, draftId, files, "draft");
58
79
  log({
59
80
  event: "draft-created",
@@ -1 +1 @@
1
- {"version":3,"file":"draft.js","sourceRoot":"","sources":["../../src/tools/draft.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"draft.js","sourceRoot":"","sources":["../../src/tools/draft.ts"],"names":[],"mappings":"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"}
@@ -10,6 +10,22 @@ export interface FetchBodyResult {
10
10
  conversationId: string | null;
11
11
  isRead: boolean;
12
12
  hasAttachments: boolean;
13
+ /**
14
+ * Graph's own view of whether this is still an unsent draft, and which folder
15
+ * it is actually in. Both are needed to answer "is my draft still a draft":
16
+ * an item can be `isDraft: true` and no longer sit in the Drafts folder, and a
17
+ * fetch by id still succeeds in that state — on 2026-08-07 the Ecolec Drafts
18
+ * listing returned zero items while this same call returned `from: null`
19
+ * eleven seconds later (Task 2530). `parentFolderId` is the discriminator;
20
+ * `isDraft` alone calls that healthy.
21
+ *
22
+ * `sentDateTime` is `0001-01-01T00:00:00Z` on anything never sent, and null
23
+ * only when Graph omitted the field.
24
+ */
25
+ isDraft: boolean;
26
+ parentFolderId: string | null;
27
+ sentDateTime: string | null;
28
+ lastModifiedDateTime: string | null;
13
29
  contentType: "html" | "text";
14
30
  body: string;
15
31
  }
@@ -1 +1 @@
1
- {"version":3,"file":"mail-fetch-body.d.ts","sourceRoot":"","sources":["../../src/tools/mail-fetch-body.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAMhE,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,cAAc,EAAE,OAAO,CAAC;IACxB,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;CACd;AAkBD;;;GAGG;AACH,wBAAgB,UAAU,CACxB,IAAI,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAAG,IAAI,GACnE;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAKlD;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,GAC1B,OAAO,CAAC,eAAe,CAAC,CAqC1B"}
1
+ {"version":3,"file":"mail-fetch-body.d.ts","sourceRoot":"","sources":["../../src/tools/mail-fetch-body.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAMhE,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,cAAc,EAAE,OAAO,CAAC;IACxB;;;;;;;;;;;OAWG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;CACd;AAsBD;;;GAGG;AACH,wBAAgB,UAAU,CACxB,IAAI,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GAAG,IAAI,GACnE;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAKlD;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CACpC,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,GAC1B,OAAO,CAAC,eAAe,CAAC,CAyC1B"}
@@ -2,7 +2,7 @@ import { callGraph } from "../lib/graph-client.js";
2
2
  import { htmlToText } from "../lib/html-to-text.js";
3
3
  import { log } from "../lib/log.js";
4
4
  import { addrs } from "../lib/recipients.js";
5
- const SELECT = "id,subject,from,toRecipients,ccRecipients,receivedDateTime,conversationId,isRead,hasAttachments,body";
5
+ const SELECT = "id,subject,from,toRecipients,ccRecipients,receivedDateTime,conversationId,isRead,hasAttachments,isDraft,parentFolderId,sentDateTime,lastModifiedDateTime,body";
6
6
  /**
7
7
  * Map Graph's `from.emailAddress` to the archive's { from, fromName } pair.
8
8
  * Exported so the mapping is unit-testable without a live Graph call.
@@ -46,6 +46,10 @@ export async function runMailFetchBody(config, args) {
46
46
  conversationId: msg.conversationId ?? null,
47
47
  isRead: msg.isRead ?? false,
48
48
  hasAttachments: msg.hasAttachments ?? false,
49
+ isDraft: msg.isDraft ?? false,
50
+ parentFolderId: msg.parentFolderId ?? null,
51
+ sentDateTime: msg.sentDateTime ?? null,
52
+ lastModifiedDateTime: msg.lastModifiedDateTime ?? null,
49
53
  contentType,
50
54
  body,
51
55
  };
@@ -1 +1 @@
1
- {"version":3,"file":"mail-fetch-body.js","sourceRoot":"","sources":["../../src/tools/mail-fetch-body.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,KAAK,EAAqB,MAAM,sBAAsB,CAAC;AA8BhE,MAAM,MAAM,GACV,sGAAsG,CAAC;AAEzG;;;GAGG;AACH,MAAM,UAAU,UAAU,CACxB,IAAoE;IAEpE,OAAO;QACL,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,IAAI,IAAI;QACzC,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,IAAI,IAAI;KAC3C,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAyB,EACzB,IAA2B;IAE3B,MAAM,IAAI,GAAG,gBAAgB,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;IAClE,MAAM,GAAG,GAAG,MAAM,SAAS,CACzB,MAAM,EACN,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAe,EAC7E,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,EAAE,IAAI,EAAE,CAC1C,CAAC;IAEF,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,WAAW,IAAI,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;IAChE,MAAM,WAAW,GAAoB,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IAC1E,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,EAAE,OAAO,IAAI,EAAE,CAAC;IACpC,MAAM,IAAI,GAAG,WAAW,KAAK,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAE5D,GAAG,CAAC;QACF,KAAK,EAAE,iBAAiB;QACxB,EAAE,EAAE,YAAY;QAChB,OAAO,EAAE,MAAM,CAAC,SAAS;QACzB,EAAE,EAAE,IAAI,CAAC,SAAS;QAClB,KAAK,EAAE,IAAI,CAAC,MAAM;QAClB,WAAW;KACZ,CAAC,CAAC;IAEH,MAAM,UAAU,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACxC,OAAO;QACL,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,IAAI;QAC5B,IAAI,EAAE,UAAU,CAAC,IAAI;QACrB,QAAQ,EAAE,UAAU,CAAC,QAAQ;QAC7B,EAAE,EAAE,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC;QAC3B,EAAE,EAAE,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC;QAC3B,gBAAgB,EAAE,GAAG,CAAC,gBAAgB;QACtC,cAAc,EAAE,GAAG,CAAC,cAAc,IAAI,IAAI;QAC1C,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,KAAK;QAC3B,cAAc,EAAE,GAAG,CAAC,cAAc,IAAI,KAAK;QAC3C,WAAW;QACX,IAAI;KACL,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"mail-fetch-body.js","sourceRoot":"","sources":["../../src/tools/mail-fetch-body.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,GAAG,EAAE,MAAM,eAAe,CAAC;AACpC,OAAO,EAAE,KAAK,EAAqB,MAAM,sBAAsB,CAAC;AAkDhE,MAAM,MAAM,GACV,+JAA+J,CAAC;AAElK;;;GAGG;AACH,MAAM,UAAU,UAAU,CACxB,IAAoE;IAEpE,OAAO;QACL,IAAI,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,IAAI,IAAI;QACzC,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,IAAI,IAAI;KAC3C,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAAyB,EACzB,IAA2B;IAE3B,MAAM,IAAI,GAAG,gBAAgB,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;IAClE,MAAM,GAAG,GAAG,MAAM,SAAS,CACzB,MAAM,EACN,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAe,EAC7E,EAAE,IAAI,EAAE,iBAAiB,EAAE,MAAM,EAAE,IAAI,EAAE,CAC1C,CAAC;IAEF,MAAM,OAAO,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,WAAW,IAAI,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;IAChE,MAAM,WAAW,GAAoB,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC;IAC1E,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,EAAE,OAAO,IAAI,EAAE,CAAC;IACpC,MAAM,IAAI,GAAG,WAAW,KAAK,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;IAE5D,GAAG,CAAC;QACF,KAAK,EAAE,iBAAiB;QACxB,EAAE,EAAE,YAAY;QAChB,OAAO,EAAE,MAAM,CAAC,SAAS;QACzB,EAAE,EAAE,IAAI,CAAC,SAAS;QAClB,KAAK,EAAE,IAAI,CAAC,MAAM;QAClB,WAAW;KACZ,CAAC,CAAC;IAEH,MAAM,UAAU,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACxC,OAAO;QACL,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,IAAI;QAC5B,IAAI,EAAE,UAAU,CAAC,IAAI;QACrB,QAAQ,EAAE,UAAU,CAAC,QAAQ;QAC7B,EAAE,EAAE,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC;QAC3B,EAAE,EAAE,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC;QAC3B,gBAAgB,EAAE,GAAG,CAAC,gBAAgB;QACtC,cAAc,EAAE,GAAG,CAAC,cAAc,IAAI,IAAI;QAC1C,MAAM,EAAE,GAAG,CAAC,MAAM,IAAI,KAAK;QAC3B,cAAc,EAAE,GAAG,CAAC,cAAc,IAAI,KAAK;QAC3C,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,KAAK;QAC7B,cAAc,EAAE,GAAG,CAAC,cAAc,IAAI,IAAI;QAC1C,YAAY,EAAE,GAAG,CAAC,YAAY,IAAI,IAAI;QACtC,oBAAoB,EAAE,GAAG,CAAC,oBAAoB,IAAI,IAAI;QACtD,WAAW;QACX,IAAI;KACL,CAAC;AACJ,CAAC"}
@@ -41,6 +41,10 @@ export interface MailListItem {
41
41
  * outlook-mail-attachment to list/download them. Inline-only images can read
42
42
  * false, matching Graph. */
43
43
  hasAttachments: boolean;
44
+ /** Graph `isDraft`. Carried on every folder's listing, not just Drafts, so a
45
+ * caller comparing a Drafts listing against a message it fetched by id is
46
+ * reading the same field on both sides (Task 2530). */
47
+ isDraft: boolean;
44
48
  }
45
49
  export interface MailListResult {
46
50
  items: MailListItem[];
@@ -1 +1 @@
1
- {"version":3,"file":"mail-list.d.ts","sourceRoot":"","sources":["../../src/tools/mail-list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAIhE,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IAMb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAUjF;AAsBD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC;IAChB;;iCAE6B;IAC7B,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,kFAAkF;IAClF,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AA6BD,wBAAsB,WAAW,CAC/B,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,cAAc,CAAC,CA+DzB"}
1
+ {"version":3,"file":"mail-list.d.ts","sourceRoot":"","sources":["../../src/tools/mail-list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAIhE,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC;IAMb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAUjF;AAsBD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC;IAChB;;iCAE6B;IAC7B,cAAc,EAAE,OAAO,CAAC;IACxB;;4DAEwD;IACxD,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,kFAAkF;IAClF,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AA8BD,wBAAsB,WAAW,CAC/B,MAAM,EAAE,iBAAiB,EACzB,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,cAAc,CAAC,CA+DzB"}
@@ -43,7 +43,7 @@ const WELL_KNOWN_ALIASES = {
43
43
  archive: "archive",
44
44
  outbox: "outbox",
45
45
  };
46
- const FIELDS = "id,subject,from,receivedDateTime,bodyPreview,isRead,hasAttachments";
46
+ const FIELDS = "id,subject,from,receivedDateTime,bodyPreview,isRead,hasAttachments,isDraft";
47
47
  function toGraphDate(input, field) {
48
48
  const d = new Date(input);
49
49
  if (!Number.isFinite(d.getTime())) {
@@ -128,6 +128,7 @@ function shapeMessage(msg) {
128
128
  bodyPreview: (msg.bodyPreview ?? "").slice(0, 1024),
129
129
  isRead: msg.isRead ?? false,
130
130
  hasAttachments: msg.hasAttachments ?? false,
131
+ isDraft: msg.isDraft ?? false,
131
132
  };
132
133
  }
133
134
  function clampTop(input, fallback) {