@open-everywhere/provider-notion 0.1.0

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 (186) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +21 -0
  3. package/README.md +48 -0
  4. package/agent/channels/notion-acting-identity.ts +88 -0
  5. package/agent/channels/notion-ancestry-trust.ts +458 -0
  6. package/agent/channels/notion-api.ts +1290 -0
  7. package/agent/channels/notion-bot-user.ts +250 -0
  8. package/agent/channels/notion-catch-up.ts +339 -0
  9. package/agent/channels/notion-credentials.ts +417 -0
  10. package/agent/channels/notion-engagement.ts +372 -0
  11. package/agent/channels/notion-event-normalizer.ts +587 -0
  12. package/agent/channels/notion-identity.ts +271 -0
  13. package/agent/channels/notion-ingress-drain.ts +289 -0
  14. package/agent/channels/notion-ingress.ts +182 -0
  15. package/agent/channels/notion-message-ops.ts +140 -0
  16. package/agent/channels/notion-origin-codec.ts +38 -0
  17. package/agent/channels/notion-post-arm.ts +189 -0
  18. package/agent/channels/notion-question-return.ts +223 -0
  19. package/agent/channels/notion-read.ts +283 -0
  20. package/agent/channels/notion-scope.ts +560 -0
  21. package/agent/channels/notion-stop.ts +169 -0
  22. package/agent/channels/notion-supervisor.ts +856 -0
  23. package/agent/channels/notion-webhook-timing.ts +131 -0
  24. package/agent/channels/notion-webhook-verifier.ts +55 -0
  25. package/agent/channels/notion-webhook.ts +963 -0
  26. package/agent/channels/notion-work-search.ts +74 -0
  27. package/agent/channels/notion-work.ts +144 -0
  28. package/agent/channels/notion.ts +1242 -0
  29. package/agent/contributions.ts +39 -0
  30. package/agent/docs-generated.ts +15 -0
  31. package/agent/memory/blocks.ts +295 -0
  32. package/agent/memory/memory.ts +622 -0
  33. package/agent/provider-events.ts +198 -0
  34. package/agent/register.ts +144 -0
  35. package/agent/schedules/notion-ingress.ts +20 -0
  36. package/agent/surface-parity.ts +105 -0
  37. package/agent/tools/notion_search.ts +110 -0
  38. package/agent/tools/read_notion_comments.ts +250 -0
  39. package/agent/tools/read_notion_page.ts +351 -0
  40. package/dist/agent/channels/notion-acting-identity.d.ts +23 -0
  41. package/dist/agent/channels/notion-acting-identity.d.ts.map +1 -0
  42. package/dist/agent/channels/notion-acting-identity.js +85 -0
  43. package/dist/agent/channels/notion-acting-identity.js.map +1 -0
  44. package/dist/agent/channels/notion-ancestry-trust.d.ts +208 -0
  45. package/dist/agent/channels/notion-ancestry-trust.d.ts.map +1 -0
  46. package/dist/agent/channels/notion-ancestry-trust.js +331 -0
  47. package/dist/agent/channels/notion-ancestry-trust.js.map +1 -0
  48. package/dist/agent/channels/notion-api.d.ts +655 -0
  49. package/dist/agent/channels/notion-api.d.ts.map +1 -0
  50. package/dist/agent/channels/notion-api.js +938 -0
  51. package/dist/agent/channels/notion-api.js.map +1 -0
  52. package/dist/agent/channels/notion-bot-user.d.ts +107 -0
  53. package/dist/agent/channels/notion-bot-user.d.ts.map +1 -0
  54. package/dist/agent/channels/notion-bot-user.js +192 -0
  55. package/dist/agent/channels/notion-bot-user.js.map +1 -0
  56. package/dist/agent/channels/notion-catch-up.d.ts +64 -0
  57. package/dist/agent/channels/notion-catch-up.d.ts.map +1 -0
  58. package/dist/agent/channels/notion-catch-up.js +289 -0
  59. package/dist/agent/channels/notion-catch-up.js.map +1 -0
  60. package/dist/agent/channels/notion-credentials.d.ts +210 -0
  61. package/dist/agent/channels/notion-credentials.d.ts.map +1 -0
  62. package/dist/agent/channels/notion-credentials.js +360 -0
  63. package/dist/agent/channels/notion-credentials.js.map +1 -0
  64. package/dist/agent/channels/notion-engagement.d.ts +219 -0
  65. package/dist/agent/channels/notion-engagement.d.ts.map +1 -0
  66. package/dist/agent/channels/notion-engagement.js +283 -0
  67. package/dist/agent/channels/notion-engagement.js.map +1 -0
  68. package/dist/agent/channels/notion-event-normalizer.d.ts +194 -0
  69. package/dist/agent/channels/notion-event-normalizer.d.ts.map +1 -0
  70. package/dist/agent/channels/notion-event-normalizer.js +542 -0
  71. package/dist/agent/channels/notion-event-normalizer.js.map +1 -0
  72. package/dist/agent/channels/notion-identity.d.ts +119 -0
  73. package/dist/agent/channels/notion-identity.d.ts.map +1 -0
  74. package/dist/agent/channels/notion-identity.js +205 -0
  75. package/dist/agent/channels/notion-identity.js.map +1 -0
  76. package/dist/agent/channels/notion-ingress-drain.d.ts +66 -0
  77. package/dist/agent/channels/notion-ingress-drain.d.ts.map +1 -0
  78. package/dist/agent/channels/notion-ingress-drain.js +185 -0
  79. package/dist/agent/channels/notion-ingress-drain.js.map +1 -0
  80. package/dist/agent/channels/notion-ingress.d.ts +64 -0
  81. package/dist/agent/channels/notion-ingress.d.ts.map +1 -0
  82. package/dist/agent/channels/notion-ingress.js +130 -0
  83. package/dist/agent/channels/notion-ingress.js.map +1 -0
  84. package/dist/agent/channels/notion-message-ops.d.ts +3 -0
  85. package/dist/agent/channels/notion-message-ops.d.ts.map +1 -0
  86. package/dist/agent/channels/notion-message-ops.js +126 -0
  87. package/dist/agent/channels/notion-message-ops.js.map +1 -0
  88. package/dist/agent/channels/notion-origin-codec.d.ts +7 -0
  89. package/dist/agent/channels/notion-origin-codec.d.ts.map +1 -0
  90. package/dist/agent/channels/notion-origin-codec.js +32 -0
  91. package/dist/agent/channels/notion-origin-codec.js.map +1 -0
  92. package/dist/agent/channels/notion-post-arm.d.ts +3 -0
  93. package/dist/agent/channels/notion-post-arm.d.ts.map +1 -0
  94. package/dist/agent/channels/notion-post-arm.js +185 -0
  95. package/dist/agent/channels/notion-post-arm.js.map +1 -0
  96. package/dist/agent/channels/notion-question-return.d.ts +77 -0
  97. package/dist/agent/channels/notion-question-return.d.ts.map +1 -0
  98. package/dist/agent/channels/notion-question-return.js +183 -0
  99. package/dist/agent/channels/notion-question-return.js.map +1 -0
  100. package/dist/agent/channels/notion-read.d.ts +62 -0
  101. package/dist/agent/channels/notion-read.d.ts.map +1 -0
  102. package/dist/agent/channels/notion-read.js +257 -0
  103. package/dist/agent/channels/notion-read.js.map +1 -0
  104. package/dist/agent/channels/notion-scope.d.ts +188 -0
  105. package/dist/agent/channels/notion-scope.d.ts.map +1 -0
  106. package/dist/agent/channels/notion-scope.js +433 -0
  107. package/dist/agent/channels/notion-scope.js.map +1 -0
  108. package/dist/agent/channels/notion-stop.d.ts +48 -0
  109. package/dist/agent/channels/notion-stop.d.ts.map +1 -0
  110. package/dist/agent/channels/notion-stop.js +141 -0
  111. package/dist/agent/channels/notion-stop.js.map +1 -0
  112. package/dist/agent/channels/notion-supervisor.d.ts +462 -0
  113. package/dist/agent/channels/notion-supervisor.d.ts.map +1 -0
  114. package/dist/agent/channels/notion-supervisor.js +482 -0
  115. package/dist/agent/channels/notion-supervisor.js.map +1 -0
  116. package/dist/agent/channels/notion-webhook-timing.d.ts +35 -0
  117. package/dist/agent/channels/notion-webhook-timing.d.ts.map +1 -0
  118. package/dist/agent/channels/notion-webhook-timing.js +99 -0
  119. package/dist/agent/channels/notion-webhook-timing.js.map +1 -0
  120. package/dist/agent/channels/notion-webhook-verifier.d.ts +5 -0
  121. package/dist/agent/channels/notion-webhook-verifier.d.ts.map +1 -0
  122. package/dist/agent/channels/notion-webhook-verifier.js +53 -0
  123. package/dist/agent/channels/notion-webhook-verifier.js.map +1 -0
  124. package/dist/agent/channels/notion-webhook.d.ts +242 -0
  125. package/dist/agent/channels/notion-webhook.d.ts.map +1 -0
  126. package/dist/agent/channels/notion-webhook.js +675 -0
  127. package/dist/agent/channels/notion-webhook.js.map +1 -0
  128. package/dist/agent/channels/notion-work-search.d.ts +9 -0
  129. package/dist/agent/channels/notion-work-search.d.ts.map +1 -0
  130. package/dist/agent/channels/notion-work-search.js +69 -0
  131. package/dist/agent/channels/notion-work-search.js.map +1 -0
  132. package/dist/agent/channels/notion-work.d.ts +68 -0
  133. package/dist/agent/channels/notion-work.d.ts.map +1 -0
  134. package/dist/agent/channels/notion-work.js +126 -0
  135. package/dist/agent/channels/notion-work.js.map +1 -0
  136. package/dist/agent/channels/notion.d.ts +95 -0
  137. package/dist/agent/channels/notion.d.ts.map +1 -0
  138. package/dist/agent/channels/notion.js +1081 -0
  139. package/dist/agent/channels/notion.js.map +1 -0
  140. package/dist/agent/contributions.d.ts +32 -0
  141. package/dist/agent/contributions.d.ts.map +1 -0
  142. package/dist/agent/contributions.js +37 -0
  143. package/dist/agent/contributions.js.map +1 -0
  144. package/dist/agent/docs-generated.d.ts +3 -0
  145. package/dist/agent/docs-generated.d.ts.map +1 -0
  146. package/dist/agent/docs-generated.js +13 -0
  147. package/dist/agent/docs-generated.js.map +1 -0
  148. package/dist/agent/memory/blocks.d.ts +58 -0
  149. package/dist/agent/memory/blocks.d.ts.map +1 -0
  150. package/dist/agent/memory/blocks.js +264 -0
  151. package/dist/agent/memory/blocks.js.map +1 -0
  152. package/dist/agent/memory/memory.d.ts +157 -0
  153. package/dist/agent/memory/memory.d.ts.map +1 -0
  154. package/dist/agent/memory/memory.js +471 -0
  155. package/dist/agent/memory/memory.js.map +1 -0
  156. package/dist/agent/provider-events.d.ts +3 -0
  157. package/dist/agent/provider-events.d.ts.map +1 -0
  158. package/dist/agent/provider-events.js +196 -0
  159. package/dist/agent/provider-events.js.map +1 -0
  160. package/dist/agent/register.d.ts +5 -0
  161. package/dist/agent/register.d.ts.map +1 -0
  162. package/dist/agent/register.js +128 -0
  163. package/dist/agent/register.js.map +1 -0
  164. package/dist/agent/schedules/notion-ingress.d.ts +6 -0
  165. package/dist/agent/schedules/notion-ingress.d.ts.map +1 -0
  166. package/dist/agent/schedules/notion-ingress.js +16 -0
  167. package/dist/agent/schedules/notion-ingress.js.map +1 -0
  168. package/dist/agent/surface-parity.d.ts +3 -0
  169. package/dist/agent/surface-parity.d.ts.map +1 -0
  170. package/dist/agent/surface-parity.js +99 -0
  171. package/dist/agent/surface-parity.js.map +1 -0
  172. package/dist/agent/tools/notion_search.d.ts +56 -0
  173. package/dist/agent/tools/notion_search.d.ts.map +1 -0
  174. package/dist/agent/tools/notion_search.js +100 -0
  175. package/dist/agent/tools/notion_search.js.map +1 -0
  176. package/dist/agent/tools/read_notion_comments.d.ts +57 -0
  177. package/dist/agent/tools/read_notion_comments.d.ts.map +1 -0
  178. package/dist/agent/tools/read_notion_comments.js +236 -0
  179. package/dist/agent/tools/read_notion_comments.js.map +1 -0
  180. package/dist/agent/tools/read_notion_page.d.ts +53 -0
  181. package/dist/agent/tools/read_notion_page.d.ts.map +1 -0
  182. package/dist/agent/tools/read_notion_page.js +327 -0
  183. package/dist/agent/tools/read_notion_page.js.map +1 -0
  184. package/package.json +231 -0
  185. package/tsconfig.build.json +22 -0
  186. package/tsconfig.json +15 -0
@@ -0,0 +1,482 @@
1
+ // The Notion ambient supervisor — the eve-FREE tested core that turns every comment on a
2
+ // connected doc into a JUDGED wake to a standing per-doc supervisor session (the "agent lives in
3
+ // the doc" UX, notion-ambient-surface-design.md). It is the first NON-Slack adopter of the
4
+ // surface-general ambient-supervision pattern (surface-adapter-port.md §"Ambient supervision
5
+ // everywhere"), so it is deliberately THIN and mirrors the Slack routeSupervisorEvent shape
6
+ // (slack-events.ts) — GitHub/Linear copy this file next.
7
+ //
8
+ // WHAT'S REUSED WHOLESALE (zero Notion-specific logic in any of it):
9
+ // • renderWake + the channel-watch preamble (the judge) — normalized-wake.ts
10
+ // • shouldRotate — the pure rotation policy — slack-events.ts
11
+ // • the (org, surfaceKey) channel_session registry + the :oe-sup:g spend counter — via deps
12
+ // WHAT'S NEW (this file): the supervisor token/sentinel carrying the :oe-sup:g infix, a pure
13
+ // classifier (self / bot / unresolved-fail-closed / @mention / ambient), the NormalizedWake
14
+ // builder, and the dispatch-core (session get-or-create → build → wake).
15
+ //
16
+ // The Notion doc-session is the container-session's ORCHESTRATION HALF ONLY (surface-adapter-port
17
+ // §"container session"): a page-scoped standing session aware of all the doc's discussions,
18
+ // holding page memory, that judges every comment and replies into the triggering discussion.
19
+ // There is NO container-conversation half — a Notion doc has no top-level conversation to post to.
20
+ import { spreadVendorTs, renderWake, mkMessageTrigger, shouldRotate, mintWakeId, singleWindowMentionPolicy, matchesAgentName, stampNonMemberChatAuth, resolveExternalPolicy, withholdsBody, withholdWakeBodies, ingestedAfterWithholding, tailAuthorGrain, seatCatchUpSinceMs, engagedFollowUpContract, } from "@open-everywhere/core/agent/adapter-api.js";
21
+ import { mentionsUser, richTextToPlainText } from "./notion-api.js";
22
+ // The §2.5 engagement writer (OPE-1501). A VALUE import, not a type — the dispatch core calls it as
23
+ // the default for `NotionSupervisorDeps.markEngaged`. `notion-engagement.ts` imports only
24
+ // `notion-identity.ts` and core, so this adds no cycle.
25
+ import { markNotionEngaged } from "./notion-engagement.js";
26
+ /** The catch-up's REACHBACK FLOOR (hours): how far back a seat that has been quiet for a long time
27
+ * reads. 7 days, the same floor every other surface uses. */
28
+ const NOTION_CATCH_UP_REACHBACK_HOURS = 168;
29
+ // --- supervisor token / sentinel (carry the :oe-sup:g infix — load-bearing) ----------
30
+ //
31
+ // hooks/spend.ts isSupervisorToken keys off the `:oe-sup:g` substring to (a) tag the usage-event
32
+ // `kind="supervisor"` and (b) drive the per-generation cumulative-token rotation counter. Carry
33
+ // the infix and the ENTIRE cost/rotation apparatus works for free (design §3.1). The sentinel
34
+ // mirrors Slack's; the token puts the pageId in the channel-id slot, `notion:`-namespaced.
35
+ /** The reserved pseudo-thread sentinel for a supervisor generation (mirrors Slack's). */
36
+ export function notionSupervisorSentinel(generation) {
37
+ return `oe-sup:g${generation}`;
38
+ }
39
+ /** The reserved continuation token for a page's supervisor session at a given generation:
40
+ * `notion:<pageId>:oe-sup:g<generation>`. Contains the `:oe-sup:g` infix hooks/spend.ts keys on,
41
+ * and is stable+explicit by construction (a custom-channel token eve never re-anchors — so,
42
+ * UNLIKE Slack, no non-empty state.threadTs re-anchor sentinel is needed; design §3.1). */
43
+ export function notionSupervisorToken(pageId, generation) {
44
+ return `notion:${pageId}:${notionSupervisorSentinel(generation)}`;
45
+ }
46
+ /**
47
+ * The Notion synthetic-mention (name-drop) convention. Notion CANNOT @mention an internal
48
+ * integration (notion-webhook.ts — the structured `mentionsUser` path is STRUCTURALLY DEAD for the
49
+ * agent), so a person addresses the doc agent by NAME instead: the deployment's configured names
50
+ * (OE_AGENT_NAME / OE_AGENT_SHORT_NAME, defaulting to the brand "open everywhere" / "oe" when unset)
51
+ * or the platform-resolved integration name (botName). This is the ONLY ambient-INDEPENDENT directed
52
+ * input Notion has, so — unlike Slack/GitHub/Linear, where a name-drop stays a judged input because a
53
+ * real @mention is available — here it is promoted to `direct`.
54
+ *
55
+ * The name set + word-boundary matcher live in core (identity/bot-name.ts matchesAgentName), which
56
+ * is ALSO what the addressed link-claim recogniser strips its leading token with (hooks/link-claim.ts)
57
+ * — one source of truth for "what does this agent answer to". Word-boundary matched (`\b`) so the
58
+ * short "oe" token never fires inside "Zoe"/"poet"/"does". A hit flows through the SAME seam a real
59
+ * @mention would: planNotionSingleWindow stamps `direct-mention`, which marks the wake as OWED a
60
+ * reply and tells the seat so. 🔴 THE STAMP DELIVERS NOTHING BY ITSELF (OPE-1982): the terminal-prose
61
+ * bridge it used to arm is deleted, so the reply lands via createCommentReply only when the seat
62
+ * calls `reply` — on its own turn, or on the ONE corrective re-drive a prose ending earns.
63
+ */
64
+ export function notionNameDrop(text, botName) {
65
+ return matchesAgentName(text, botName);
66
+ }
67
+ /**
68
+ * Decide how the doc supervisor should treat one comment. The self-reply loop guard is HARDENED
69
+ * here (design §4.2), in cost order:
70
+ * 1. UNRESOLVED PRIMARY bot user id ⇒ DROP (fail closed) — self-identity is a precondition for the
71
+ * loop guard; without it the agent would loop on its own replies (each createCommentReply fires a
72
+ * fresh comment.created). This is the critical hardening over the old `if (botUserId && …)`.
73
+ * 2. SELF-authored ⇒ DROP — our own posted reply. The comparison is `created_by.id` against
74
+ * {@link NotionOwnBotIds.all}: EVERY configured integration's bot user id, not only the one that
75
+ * verified this delivery (OPE-1921). THAT IS THE WHOLE GUARD.
76
+ * 🔴 THE SET IS WHAT MAKES IT A GUARD ON A DEPLOYMENT RATHER THAN ON ONE INTEGRATION. Two
77
+ * integrations can both see one page, so a reply integration A posted is delivered on B's
78
+ * subscription too. Compared against B's id alone it matched nothing, read as a person and woke
79
+ * a turn; A's subscription then saw B's answer. Each reply is a NEW comment id, so the OPE-1906
80
+ * once-guard cannot bound that loop — only recognising our own words can.
81
+ * 3. UNIDENTIFIED AUTHOR (no `created_by.id`) ⇒ DROP (fail closed) — step 2 is the only check that
82
+ * can recognise our own words, so a comment whose author id we cannot read defeats it.
83
+ * 🔴 WITHOUT THIS STEP THE GUARD FALLS BACK ON `created_by.type`, A FIELD NOTION MAY OMIT
84
+ * (OPE-1857, notion-api.ts NotionComment): a comment's `created_by` is a PARTIAL user, so an
85
+ * id-less author reached step 4 with nothing to test, read as a person, and woke a turn. That
86
+ * is the same fail-closed posture step 1 takes for the other half of the comparison — an
87
+ * identity we cannot establish never wakes the agent, in EITHER direction.
88
+ * 4. BOT/integration-authored (created_by.type present and not "person") ⇒ DROP — the Slack
89
+ * authorIsBot analog, prevents bot-to-bot loops. A BACKSTOP for a foreign bot, never the
90
+ * self-guard: an ABSENT `type` says nothing, and steps 2-3 have already answered "is it ours".
91
+ * 5. INCOMPLETE OWN-ID SET ⇒ DROP (fail closed, OPE-1921) — one configured integration's bot user
92
+ * id did not resolve, so step 2 could not have recognised that integration's own reply. An
93
+ * author Notion states is a `person` is still admitted; an author it says NOTHING about is
94
+ * exactly the ambiguity the loop rides, so it drops.
95
+ * 🔴 IT COSTS AVAILABILITY AND THE TRADE IS DELIBERATE — see {@link NotionOwnBotIds.complete}.
96
+ * A deployment that cannot resolve one integration's id is deaf to every comment Notion does not
97
+ * mark `person`-authored until it can. 🔴 THAT IS NOT ONLY AMBIENT CHATTER: this step sits ABOVE
98
+ * steps 6 and 7, so an addressed structured @mention, an addressed name-drop and an
99
+ * `oe link <code>` identity claim all drop with it. Failing OPEN instead buys an unbounded reply
100
+ * loop between two of our own integrations, which no once-guard can stop.
101
+ * 6. STRUCTURED bot @mention ⇒ direct (guaranteed engage). Structurally unreachable for an
102
+ * internal integration today, but kept as the canonical addressed signal. Tested against the
103
+ * PRIMARY id alone: a structured mention names ONE bot user, and answering a mention of a
104
+ * sibling integration would put this integration in a conversation it was not addressed in.
105
+ * 7. NAME-DROP ⇒ direct ({@link notionNameDrop}). Notion's ONLY addressed path (no integration
106
+ * @mention), so a brand/name hit is promoted to the owed-a-reply treatment, not left as a
107
+ * judged input — this is the deliberate Notion-specific divergence from Slack/GitHub/Linear.
108
+ * 8. otherwise ⇒ channel-watch (judged by the standing doc supervisor).
109
+ *
110
+ * 🔴 THIS FUNCTION GUARDS ONE COMMENT, NOT ONE WAKE. It cannot tell a second delivery of the SAME
111
+ * comment from the first, because both carry identical bytes. The delivery once-guard is the other
112
+ * half of the loop defence and lives at the ingress
113
+ * (notion-engagement.ts `firstNotionDispatchOfComment`, called by notion-webhook.ts).
114
+ */
115
+ export function classifyNotionComment(comment, own, botName) {
116
+ if (!own.primary)
117
+ return { addressing: "drop", reason: "unresolved bot user id — fail-closed loop guard" };
118
+ const author = comment.created_by;
119
+ if (author?.id !== undefined && own.all.has(author.id)) {
120
+ // The two are named apart because they send an operator to different places: our own reply
121
+ // coming back is ordinary, and a SIBLING integration's reply arriving here is the OPE-1921
122
+ // delivery shape — the one that used to wake a turn.
123
+ const sibling = author.id !== own.primary;
124
+ return { addressing: "drop", reason: sibling ? "self-authored comment (a sibling integration of this deployment)" : "self-authored comment (our own reply)" };
125
+ }
126
+ if (!author?.id)
127
+ return { addressing: "drop", reason: "unidentified author (no created_by.id) — fail-closed loop guard" };
128
+ if (author.type && author.type !== "person")
129
+ return { addressing: "drop", reason: `bot/integration-authored (${author.type})` };
130
+ if (!own.complete && author.type !== "person") {
131
+ return { addressing: "drop", reason: "a configured integration's bot user id did not resolve, and this author is unattributable — fail-closed loop guard" };
132
+ }
133
+ if (mentionsUser(comment.rich_text, own.primary))
134
+ return { addressing: "direct", reason: "structured bot @mention" };
135
+ if (notionNameDrop(richTextToPlainText(comment.rich_text), botName)) {
136
+ return { addressing: "direct", reason: "bot name-drop (synthetic mention — Notion has no @mention for integrations)" };
137
+ }
138
+ return { addressing: "channel-watch", reason: "ambient comment — judged by the doc supervisor" };
139
+ }
140
+ /**
141
+ * The ONE single-window decision for a Notion delivery (channels/unified-window.ts
142
+ * singleWindowMentionPolicy is the SHARED seam — the exact function the GitHub reference's
143
+ * native-suppression AND classifier consult, github.ts:261 / github-supervisor.ts:139): given
144
+ * the grain's `singleWindow`, whether the classified comment is a structured @mention
145
+ * (classifyNotionComment returned `direct`), and the PRIVACY FLOOR `privacyOk`, return the
146
+ * {@link UnifiedWindowWake} the dispatch should carry, or undefined for the byte-identical
147
+ * native path.
148
+ *
149
+ * `privacyOk` is the OPERATOR-VOUCH fact the caller resolves (notion.ts): identity/trust.ts
150
+ * isVouchedInternal on the page's workspaceKey/surfaceKey (or the OE_TRUST=internal blanket
151
+ * switch), fail-closed when unregistered. This is the SAME per-surface allowlist that gates
152
+ * Notion's shared MEMORY (notion-identity.ts / bindNotion), so a page is admitted to the shared
153
+ * cross-surface WINDOW exactly when the operator has vouched it internal — never inferred from a
154
+ * misleading API field (a Notion page's `public_url` is set ONLY by publish-to-web and is null
155
+ * for the common guest-shared vector, so it would admit an external guest's mention; the vouch
156
+ * register is the doctrine-correct floor — operator vouches, code enforces, no inference).
157
+ *
158
+ * Notion's mention signal lives in ONE webhook delivery (unlike Slack's dual-event), and its
159
+ * "native" handler IS this same supervisor dispatch — so suppress-native and divert are the SAME
160
+ * act here: a window-carrying wake is delivered through the unified channel instead of the notion
161
+ * channel's own send (notion.ts's wake dep), and both legs read THIS one verdict off the one
162
+ * parsed event — structurally never a double-handle, never a gap:
163
+ * • @mention on a singleWindow, VOUCHED-internal page → the seam suppresses the native session
164
+ * and stamps `addressing:"direct-mention"` (the terminal-prose delivery bridge's arm);
165
+ * • ambient (channel-watch) comment on the same page → Leg A: the window WITHOUT the addressed
166
+ * stamp (judged treatment in the shared window, prose stays hard-dropped);
167
+ * • un-vouched page, or singleWindow off, or no discussion id → undefined (the native
168
+ * per-doc/domain path, byte-identical — a mention there keeps today's `direct` wake).
169
+ */
170
+ export function planNotionSingleWindow(singleWindow, isMention, privacyOk, target) {
171
+ const policy = singleWindowMentionPolicy(singleWindow, isMention, privacyOk);
172
+ // A mention diverts iff the seam suppresses the native session; an ambient comment diverts on
173
+ // the same `singleWindow && privacyOk` floor (Leg A — never a public/unknown page into the
174
+ // shared window). Both derive from the seam's one predicate.
175
+ const divert = isMention ? policy.suppressNative : singleWindow && privacyOk;
176
+ if (!divert)
177
+ return undefined;
178
+ if (!target.discussionId)
179
+ return undefined; // no frozen reply target ⇒ no window (fail closed)
180
+ return {
181
+ wakeId: mintWakeId(),
182
+ origin: {
183
+ platform: "notion",
184
+ discussionId: target.discussionId,
185
+ ...(target.pageId ? { pageId: target.pageId } : {}),
186
+ ...(target.workspaceId ? { workspaceId: target.workspaceId } : {}),
187
+ ...(target.credentialId ? { credentialId: target.credentialId } : {}),
188
+ },
189
+ // ADDRESSED @mention ⇒ the seam's stamp marks the wake as OWED a reply, so the envelope tells
190
+ // the seat it owes an answer through the `reply` tool. Ambient wakes omit it. It is not a delivery
191
+ // path: a turn that ends in prose is dropped and re-driven once, on every addressing (OPE-1982).
192
+ ...(policy.addressing !== undefined ? { addressing: policy.addressing } : {}),
193
+ ...spreadVendorTs(target.vendorTs),
194
+ };
195
+ }
196
+ function toWakeSibling(m) {
197
+ return {
198
+ from: m.from,
199
+ author: m.authorId ?? (m.from === "self" ? "the doc agent" : "unknown"),
200
+ authorId: m.authorId,
201
+ id: m.id,
202
+ sentAt: m.sentAt,
203
+ trust: m.trust,
204
+ body: m.text,
205
+ };
206
+ }
207
+ /**
208
+ * Build the NormalizedWake for one doc-supervisor turn. platform "notion", containerNoun "doc",
209
+ * locus.channel = the page, locus.thread = the discussion, backscroll = the discussion's other
210
+ * comments as oldest-first envelope siblings, ending at the triggering comment. renderWake then
211
+ * emits the exact `{message, context}` shape every OE dispatch sends — the SAME renderer Slack
212
+ * uses, with NO platform branch. A `direct` wake carries no preamble/backscroll (the body is the
213
+ * driver, verbatim); a `channel-watch` wake gets the judged envelope + doc-noun preamble.
214
+ */
215
+ export function buildNotionSupervisorWake(input) {
216
+ // SINGLE-WINDOW addressed @mention: an ENVELOPED render (wake-id + backscroll survive — the
217
+ // reply tool needs the id) with the addressed preamble. Guarded on `direct` so the flag can
218
+ // never re-shape a channel-watch wake; unset ⇒ today's renders, byte-identical.
219
+ const directMention = input.directMention === true && input.addressing === "direct";
220
+ const judged = input.addressing === "channel-watch";
221
+ const enveloped = judged || directMention;
222
+ // The carried TAIL leads, then this wake's own thread comments — one oldest-first list that
223
+ // reads straight across the rotation boundary (OPE-794).
224
+ const backscroll = enveloped ? [...(input.tail ?? []), ...(input.threadComments ?? []).map(toWakeSibling)] : undefined;
225
+ return {
226
+ platform: "notion",
227
+ reason: input.addressing === "direct" ? "mention" : "channel-activity",
228
+ addressing: directMention ? "direct-mention" : input.addressing,
229
+ containerNoun: "doc",
230
+ // The ambient calibration dial — an addressed mention is owed an answer, so watch-only.
231
+ engagementTier: judged ? input.engagementTier : undefined,
232
+ // The ENROLMENT fact ({@link NotionSupervisorWakeInput.threadEngaged}), through the ONE shared
233
+ // helper every enveloped surface now calls (channels/engaged-wake.ts, OPE-1740). It keeps both
234
+ // exclusions this line used to spell out: an addressed comment is owed an answer whatever the
235
+ // page's enrolment says, and a `direct` wake carries no envelope to hang an attribute on.
236
+ followUp: engagedFollowUpContract({
237
+ addressing: directMention ? "direct-mention" : input.addressing,
238
+ threadEngaged: input.threadEngaged,
239
+ }),
240
+ // Single-window wake id — only the singleWindow dispatch passes it (byte-identical off).
241
+ wakeId: input.wakeId,
242
+ locus: {
243
+ channel: { id: input.pageId },
244
+ thread: { ts: input.discussionId },
245
+ },
246
+ backscroll,
247
+ ...(input.catchUpUnavailable !== undefined ? { catchUpUnavailable: input.catchUpUnavailable } : {}),
248
+ trigger: mkMessageTrigger({
249
+ from: input.trigger.from,
250
+ author: input.trigger.authorId ?? "unknown",
251
+ authorId: input.trigger.authorId,
252
+ id: input.trigger.id,
253
+ sentAt: input.trigger.sentAt,
254
+ // The caller's stated per-person tier — no default (OPE-596). Directness is the `mention`
255
+ // flag below, never a trust tier.
256
+ trust: input.trigger.trust,
257
+ mention: input.addressing === "direct",
258
+ body: input.trigger.text,
259
+ }),
260
+ };
261
+ }
262
+ /**
263
+ * THE INGESTED SET for one Notion wake, graded for the non-member taint join (OPE-466 / OPE-516).
264
+ *
265
+ * 🔴 NOTION HAD NO AUTHOR LEG AT ALL BEFORE OPE-516, and it could not have had one. The join's
266
+ * `refuted` field is the SURFACE's positive refutation — Slack's `is_restricted`, Teams' Entra
267
+ * `userType: "guest"` — and Notion publishes NO membership fact about a comment author. So the
268
+ * question is answered from the operator's own AUTHOR-TRUST REGISTER instead
269
+ * (identity/author-trust.ts): on a workspace the operator has armed, an author they did not declare
270
+ * grades `external`. On an unarmed workspace the leg contributes nothing and this is a no-op.
271
+ *
272
+ * `refuted` is deliberately never set here. Leaving it absent is the honest statement — Notion has
273
+ * no verdict to give, and a hardcoded `false` would read as "we asked and it was fine".
274
+ *
275
+ * NOTION HAS NO GROUP CONCEPT, so no memberships are contributed and a Notion declaration is
276
+ * principal-keys-only. That is a platform fact, not a gap in this function.
277
+ *
278
+ * A `self` author (the bot's own prior comment) carries no `authorId` from the webhook core, and its
279
+ * resolved tier is the machine floor, so it can never reach the register leg — the join requires a
280
+ * positively-human tier.
281
+ *
282
+ * Pure and exported so the grading is testable without a store or a live wake.
283
+ */
284
+ export function notionIngestedMessages(d) {
285
+ // No workspace id ⇒ no tenant key can be composed ⇒ no author question can be asked. Fail OPEN,
286
+ // matching every other carriage leg: an unnameable tenant is not evidence of anything.
287
+ const tenantKey = d.workspaceId ? `notion/${d.workspaceId}` : undefined;
288
+ const grade = (m) => ({
289
+ ...(m.trust === undefined ? {} : { trust: m.trust }),
290
+ ...(tenantKey === undefined ? {} : { authorTrust: { tenantKey, ...(m.authorId === undefined ? {} : { authorId: m.authorId }) } }),
291
+ });
292
+ return [grade(d.trigger), ...d.threadComments.map(grade)];
293
+ }
294
+ /**
295
+ * THE EXTERNAL-AUTHOR DECISION for one Notion dispatch (OPE-507). Pure, exported so the three
296
+ * behaviours are testable without a store or a live wake.
297
+ *
298
+ * Notion's three answers, and why they are what they are:
299
+ * • the TRIGGER's author is excluded ⇒ NO WAKE AT ALL (`suppressed`). That covers behaviour 2
300
+ * (ambient never triggers on their comment) and behaviour 1 (their @mention is not acted on).
301
+ * • a SIBLING's author is excluded ⇒ the wake proceeds and that sibling's body is WITHHELD
302
+ * (behaviour 3): a permitted member's later comment still wakes the agent, and the excluded
303
+ * author's earlier comment reaches the model as the arrived-marker.
304
+ * • nothing is excluded, or the policy is `taint` ⇒ `withheldIds` is empty and every caller path
305
+ * is byte-identical to before this existed.
306
+ *
307
+ * 🔴 NOTION POSTS NO PRIVATE NOTICE, AND THAT IS DELIBERATE. Behaviour 1 owes the excluded sender a
308
+ * PRIVATE notice, and Notion has no private channel back to a commenter — a comment reply lands in
309
+ * the shared discussion, where everyone reading the doc sees it. Announcing "I will not act on your
310
+ * message" into a shared doc is a disclosure, not a notice, so the Notion answer is to do nothing.
311
+ * The surfaces that HAVE a private channel (a Discord DM, a Slack DM) post the notice there.
312
+ */
313
+ export function notionExternalAuthorDecision(d) {
314
+ // `ingested` is built by notionIngestedMessages in EXACTLY this order — trigger first, then the
315
+ // siblings — so the index join below is the same pairing the taint carriage already relies on.
316
+ const ordered = [d.trigger, ...d.threadComments];
317
+ const withheldIds = new Set();
318
+ let suppressed = false;
319
+ for (const [i, m] of ordered.entries()) {
320
+ const facts = d.ingested[i];
321
+ if (facts === undefined || !withholdsBody(facts, d.policy))
322
+ continue;
323
+ if (i === 0)
324
+ suppressed = true;
325
+ else
326
+ withheldIds.add(m.id);
327
+ }
328
+ return { suppressed, withheldIds };
329
+ }
330
+ /**
331
+ * Route one classified comment to the doc supervisor: get-or-create the session, build + render
332
+ * the wake for that generation, deliver it, and stash the triggering discussion on the session
333
+ * state so `message.completed` replies into the right thread.
334
+ *
335
+ * A DELIVERY failure (the send itself) propagates to the caller's catch. A session that dies at
336
+ * TURN time does not: nothing here observes it, so the trigger is lost SILENTLY, and no reactive
337
+ * arm can exist (OPE-174 removed the inert one; channels/session-resume.ts holds the measurement).
338
+ * The working remedy is PRE-DISPATCH — the live `getOrCreateSession` claims the OPE-599
339
+ * runtime-change rotation before this call and hands back a session the running eve build can read.
340
+ */
341
+ /** The default {@link NotionSupervisorDeps.markEngaged}: the dispatch's own facts, in the seam's
342
+ * vocabulary. One derivation, so the row this writes is the row `notion-engagement.ts` reads and
343
+ * `stand_down` marks. */
344
+ async function liveNotionEngagementMark(d) {
345
+ const facts = {
346
+ workspaceId: d.workspaceId ?? undefined,
347
+ pageId: d.pageId,
348
+ ...(d.trigger.authorId ? { principalId: d.trigger.authorId } : {}),
349
+ ...(d.credentialId ? { credentialId: d.credentialId } : {}),
350
+ };
351
+ // The TRIGGER COMMENT's own id is the guard key: Notion redelivers a comment this route answered
352
+ // with a 500 (an uncaught throw below this mark), and a redelivered name-drop must not lift a
353
+ // stand-down made in between. See `markNotionEngaged`.
354
+ await markNotionEngaged(facts, d.trigger.id);
355
+ }
356
+ export async function handleNotionSupervisorDispatch(d, deps) {
357
+ const atIso = deps.now ? deps.now() : new Date().toISOString();
358
+ // THE EXTERNAL-AUTHOR POLICY (OPE-507). Resolved once, ABOVE the session get-or-create: a
359
+ // suppressed wake must not mint a generation, touch a session or spend a rotation budget on a
360
+ // message the agent is not going to read. Absent policy ⇒ the org default ⇒ `taint` ⇒ this whole
361
+ // block is inert and every line below runs exactly as it did.
362
+ const policy = d.externalPolicy ?? resolveExternalPolicy();
363
+ const ingested = notionIngestedMessages(d);
364
+ const external = notionExternalAuthorDecision({ trigger: d.trigger, threadComments: d.threadComments, ingested, policy });
365
+ if (external.suppressed)
366
+ return; // the trigger's author may not task the agent — no wake, no notice (see above)
367
+ // 🔴 THE §2.5 MARK (OPE-1501) — an ADDRESSED wake ENROLS the page, and this is the FIRST line at
368
+ // which that is safe: the external-author decision above is Notion's author gate, and it is made
369
+ // here rather than at the ingress. `participating: true` inside the writer is the re-invitation,
370
+ // so naming the agent again clears a previous stand-down — which is exactly why a suppressed
371
+ // author must never reach this line. Best-effort: the writer swallows a store failure, because
372
+ // engagement bookkeeping must never fail a turn.
373
+ if (d.addressing === "direct")
374
+ await (deps.markEngaged ?? liveNotionEngagementMark)(d);
375
+ const session = await deps.getOrCreateSession(d.surfaceKey, d.pageId, atIso);
376
+ // `oeUnified` marks a session CREATED as the domain session (eve applies send-state at creation
377
+ // only, and a session's grain is fixed for its lifetime — the token shape can't change mid-gen),
378
+ // so notion.ts's message-tool-only gate keys on it. Absent on the per-doc path (byte-identical).
379
+ const state = {
380
+ discussionId: d.discussionId || null,
381
+ pageId: d.pageId || null,
382
+ workspaceId: d.workspaceId,
383
+ ...(d.unified ? { oeUnified: true } : {}),
384
+ };
385
+ // The "changed since your last wake" PUSH — computed ONCE from THIS session's cursor (its previous
386
+ // wake time), naming which shared-memory silos changed. Only when armed (deps.memoryDelta present)
387
+ // AND a prior wake exists (session.lastWakeAt); fail-soft (a delta failure never breaks the wake).
388
+ let memoryDelta;
389
+ if (deps.memoryDelta && session.lastWakeAt) {
390
+ try {
391
+ memoryDelta = (await deps.memoryDelta(session.lastWakeAt, atIso, session.token)) ?? undefined;
392
+ }
393
+ catch {
394
+ memoryDelta = undefined;
395
+ }
396
+ }
397
+ const doWake = async (token, generation, tail, catchUpUnavailable) => {
398
+ const built = {
399
+ ...buildNotionSupervisorWake({
400
+ addressing: d.addressing,
401
+ pageId: d.pageId,
402
+ discussionId: d.discussionId,
403
+ trigger: d.trigger,
404
+ threadComments: d.threadComments,
405
+ engagementTier: d.engagementTier,
406
+ wakeId: d.window?.wakeId,
407
+ directMention: d.directMention,
408
+ threadEngaged: d.engagedFollowUp,
409
+ tail,
410
+ catchUpUnavailable,
411
+ }),
412
+ ...(memoryDelta ? { memoryDelta } : {}),
413
+ };
414
+ // BEHAVIOUR 3 (OPE-507) — an excluded author's SIBLING comment reaches the model as the
415
+ // arrived-marker, not as text. It runs HERE, before renderWake, so the bytes never enter the
416
+ // envelope, the untrusted wrap, or the rendered context. Returns `built` by reference when
417
+ // nothing is withheld, so every existing render golden is untouched.
418
+ const nw = withholdWakeBodies(built, (m) => external.withheldIds.has(m.id));
419
+ const { message, context } = renderWake(nw, deps.now);
420
+ // OPE-466 CARRIAGE — over the trigger AND every hydrated thread sibling. Notion re-sends the
421
+ // whole discussion on every wake, so a guest's earlier comment ENTERS the context on a wake it
422
+ // did not trigger; the leg keys on INGESTION, not on who spoke last, so those siblings count.
423
+ //
424
+ // 🔴 `ingestedAfterWithholding` IS WHY A WITHHELD BODY NEVER KEYS TAINT. A withheld sibling did
425
+ // not enter the context, so the join must not be told it did — marking on it would set the bit
426
+ // on every withheld wake and stop it discriminating. On `taint` the same array passes through by
427
+ // reference and the stamp is byte-identical.
428
+ const auth = stampNonMemberChatAuth(d.auth, ingestedAfterWithholding(ingested, policy), { surfaceKey: d.surfaceKey });
429
+ await deps.wake({ token, auth, message, context: context ?? [], state, window: d.window });
430
+ };
431
+ // A wake failure PROPAGATES to the caller's catch. (OPE-174 removed a terminal-session-death
432
+ // force-rotate branch here: it could never fire — a conversation-mode session PARKS at eve's
433
+ // token bound rather than failing, and a turn-time error cannot reject the awaited enqueue
434
+ // anyway. The reachable handler is core's agent/channels/session-park.ts.)
435
+ // 🔴 EVERY WAKE READS THE PAGE SINCE THE SEAT LAST LOOKED (OPE-1695 (b), brought to Notion by
436
+ // OPE-1726). A FRESH generation renders its carried tail, as it always did; an ONGOING generation
437
+ // reads the comments this discussion collected since its own `lastWakeAt`, through the ONE shared
438
+ // catch-up path — same bounds, same admission policy, same cap as Slack's. FAIL-SOFT: the shared
439
+ // path never throws, and an unregistered fetch comes back as the UNAVAILABLE note rather than as a
440
+ // silent empty context.
441
+ const catchUp = session.freshGeneration || !deps.catchUp
442
+ ? undefined
443
+ : await deps.catchUp({
444
+ container: d.pageId,
445
+ ...(d.discussionId ? { threadRoot: d.discussionId } : {}),
446
+ ...(d.trigger.anchorId ? { threadAnchor: d.trigger.anchorId } : {}),
447
+ sinceMs: seatCatchUpSinceMs(session.lastWakeAt, Date.parse(atIso) || Date.now(), NOTION_CATCH_UP_REACHBACK_HOURS),
448
+ untilMs: Date.parse(d.trigger.sentAt) || Date.parse(atIso) || Date.now(),
449
+ excludeId: d.trigger.id,
450
+ channelKey: d.surfaceKey,
451
+ });
452
+ await doWake(session.token, session.generation, session.freshGeneration ? session.tail : (catchUp?.messages ?? []), catchUp?.unavailable);
453
+ if (deps.touchSession)
454
+ await deps.touchSession(d.surfaceKey, atIso);
455
+ if (deps.recordWake) {
456
+ // AFTER the wake landed, and only the TRIGGER — never the backscroll rendered beside it. That is
457
+ // what stops a tail inheriting a tail (channels/session-tail.ts).
458
+ await deps.recordWake({
459
+ kind: "wake",
460
+ at: atIso,
461
+ from: d.trigger.from,
462
+ trust: d.trigger.trust,
463
+ // THE AUTHOR GRAIN (OPE-1747) — `notionIngestedMessages` builds `ingested` trigger-FIRST, the
464
+ // same index pairing `notionExternalAuthorDecision` already relies on, so `[0]` is this row's
465
+ // author. Notion has no membership verdict of its own, so the OPERATOR's register query is the
466
+ // whole refutation and it must ride the row. Resolved through the one core road, off the SAME
467
+ // local the taint join above reads.
468
+ ...tailAuthorGrain(ingested[0] ?? { trust: d.trigger.trust }),
469
+ author: d.trigger.authorId ?? "unknown",
470
+ ...(d.trigger.authorId ? { authorId: d.trigger.authorId } : {}),
471
+ id: d.trigger.id,
472
+ platform: "notion",
473
+ container: d.surfaceKey,
474
+ threadRoot: d.discussionId,
475
+ text: d.trigger.text,
476
+ }, session.token, d.surfaceKey);
477
+ }
478
+ }
479
+ // Re-export shouldRotate so the live wiring (notion.ts) drives get-or-create/rotate with the SAME
480
+ // pure policy Slack uses, without importing slack-events itself.
481
+ export { shouldRotate };
482
+ //# sourceMappingURL=notion-supervisor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notion-supervisor.js","sourceRoot":"","sources":["../../../agent/channels/notion-supervisor.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,iGAAiG;AACjG,2FAA2F;AAC3F,6FAA6F;AAC7F,4FAA4F;AAC5F,yDAAyD;AACzD,EAAE;AACF,qEAAqE;AACrE,+EAA+E;AAC/E,gEAAgE;AAChE,8FAA8F;AAC9F,6FAA6F;AAC7F,4FAA4F;AAC5F,yEAAyE;AACzE,EAAE;AACF,kGAAkG;AAClG,4FAA4F;AAC5F,6FAA6F;AAC7F,mGAAmG;AAEnG,OAAO,EACL,cAAc,EAEd,UAAU,EACV,gBAAgB,EAChB,YAAY,EACZ,UAAU,EACV,yBAAyB,EACzB,gBAAgB,EAChB,sBAAsB,EACtB,qBAAqB,EACrB,aAAa,EACb,kBAAkB,EAClB,wBAAwB,EAQxB,eAAe,EAEf,kBAAkB,EAGlB,uBAAuB,GACxB,MAAM,4CAA4C,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAsB,MAAM,iBAAiB,CAAC;AAMxF,oGAAoG;AACpG,0FAA0F;AAC1F,wDAAwD;AACxD,OAAO,EAAE,iBAAiB,EAAwB,MAAM,wBAAwB,CAAC;AAEjF;8DAC8D;AAC9D,MAAM,+BAA+B,GAAG,GAAG,CAAC;AAE5C,wFAAwF;AACxF,EAAE;AACF,iGAAiG;AACjG,gGAAgG;AAChG,8FAA8F;AAC9F,2FAA2F;AAE3F,yFAAyF;AACzF,MAAM,UAAU,wBAAwB,CAAC,UAAkB;IACzD,OAAO,WAAW,UAAU,EAAE,CAAC;AACjC,CAAC;AAED;;;4FAG4F;AAC5F,MAAM,UAAU,qBAAqB,CAAC,MAAc,EAAE,UAAkB;IACtE,OAAO,UAAU,MAAM,IAAI,wBAAwB,CAAC,UAAU,CAAC,EAAE,CAAC;AACpE,CAAC;AA4BD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,OAAgB;IAC3D,OAAO,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,MAAM,UAAU,qBAAqB,CAAC,OAAsB,EAAE,GAAoB,EAAE,OAAgB;IAClG,IAAI,CAAC,GAAG,CAAC,OAAO;QAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,iDAAiD,EAAE,CAAC;IAC3G,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAClC,IAAI,MAAM,EAAE,EAAE,KAAK,SAAS,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;QACvD,2FAA2F;QAC3F,2FAA2F;QAC3F,qDAAqD;QACrD,MAAM,OAAO,GAAG,MAAM,CAAC,EAAE,KAAK,GAAG,CAAC,OAAO,CAAC;QAC1C,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,kEAAkE,CAAC,CAAC,CAAC,uCAAuC,EAAE,CAAC;IAChK,CAAC;IACD,IAAI,CAAC,MAAM,EAAE,EAAE;QAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,iEAAiE,EAAE,CAAC;IAC1H,IAAI,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,6BAA6B,MAAM,CAAC,IAAI,GAAG,EAAE,CAAC;IAChI,IAAI,CAAC,GAAG,CAAC,QAAQ,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,oHAAoH,EAAE,CAAC;IAC9J,CAAC;IACD,IAAI,YAAY,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,yBAAyB,EAAE,CAAC;IACrH,IAAI,cAAc,CAAC,mBAAmB,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC;QACpE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,6EAA6E,EAAE,CAAC;IACzH,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,EAAE,gDAAgD,EAAE,CAAC;AACnG,CAAC;AAqBD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,UAAU,sBAAsB,CACpC,YAAqB,EACrB,SAAkB,EAClB,SAAkB,EAClB,MAAgC;IAEhC,MAAM,MAAM,GAAG,yBAAyB,CAAC,YAAY,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;IAC7E,8FAA8F;IAC9F,2FAA2F;IAC3F,6DAA6D;IAC7D,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC,YAAY,IAAI,SAAS,CAAC;IAC7E,IAAI,CAAC,MAAM;QAAE,OAAO,SAAS,CAAC;IAC9B,IAAI,CAAC,MAAM,CAAC,YAAY;QAAE,OAAO,SAAS,CAAC,CAAC,mDAAmD;IAC/F,OAAO;QACL,MAAM,EAAE,UAAU,EAAE;QACpB,MAAM,EAAE;YACN,QAAQ,EAAE,QAAQ;YAClB,YAAY,EAAE,MAAM,CAAC,YAAY;YACjC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACnD,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAClE,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACtE;QACD,8FAA8F;QAC9F,mGAAmG;QACnG,iGAAiG;QACjG,GAAG,CAAC,MAAM,CAAC,UAAU,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7E,GAAG,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC;KACnC,CAAC;AACJ,CAAC;AAgGD,SAAS,aAAa,CAAC,CAAoB;IACzC,OAAO;QACL,IAAI,EAAE,CAAC,CAAC,IAAI;QACZ,MAAM,EAAE,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,SAAS,CAAC;QACvE,QAAQ,EAAE,CAAC,CAAC,QAAQ;QACpB,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,KAAK,EAAE,CAAC,CAAC,KAAK;QACd,IAAI,EAAE,CAAC,CAAC,IAAI;KACb,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,yBAAyB,CAAC,KAAgC;IACxE,4FAA4F;IAC5F,4FAA4F;IAC5F,gFAAgF;IAChF,MAAM,aAAa,GAAG,KAAK,CAAC,aAAa,KAAK,IAAI,IAAI,KAAK,CAAC,UAAU,KAAK,QAAQ,CAAC;IACpF,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,KAAK,eAAe,CAAC;IACpD,MAAM,SAAS,GAAG,MAAM,IAAI,aAAa,CAAC;IAC1C,4FAA4F;IAC5F,yDAAyD;IACzD,MAAM,UAAU,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,cAAc,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACvH,OAAO;QACL,QAAQ,EAAE,QAAQ;QAClB,MAAM,EAAE,KAAK,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,kBAAkB;QACtE,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU;QAC/D,aAAa,EAAE,KAAK;QACpB,wFAAwF;QACxF,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS;QACzD,+FAA+F;QAC/F,+FAA+F;QAC/F,8FAA8F;QAC9F,0FAA0F;QAC1F,QAAQ,EAAE,uBAAuB,CAAC;YAChC,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU;YAC/D,aAAa,EAAE,KAAK,CAAC,aAAa;SACnC,CAAC;QACF,yFAAyF;QACzF,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,KAAK,EAAE;YACL,OAAO,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,MAAM,EAAE;YAC7B,MAAM,EAAE,EAAE,EAAE,EAAE,KAAK,CAAC,YAAY,EAAE;SACnC;QACD,UAAU;QACV,GAAG,CAAC,KAAK,CAAC,kBAAkB,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,KAAK,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACnG,OAAO,EAAE,gBAAgB,CAAC;YACxB,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI;YACxB,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,QAAQ,IAAI,SAAS;YAC3C,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,QAAQ;YAChC,EAAE,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE;YACpB,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM;YAC5B,0FAA0F;YAC1F,kCAAkC;YAClC,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK;YAC1B,OAAO,EAAE,KAAK,CAAC,UAAU,KAAK,QAAQ;YACtC,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI;SACzB,CAAC;KACH,CAAC;AACJ,CAAC;AAsLD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,sBAAsB,CAAC,CAItC;IACC,gGAAgG;IAChG,uFAAuF;IACvF,MAAM,SAAS,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IACxE,MAAM,KAAK,GAAG,CAAC,CAAoB,EAAuB,EAAE,CAAC,CAAC;QAC5D,GAAG,CAAC,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;QACpD,GAAG,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,CAAC;KAClI,CAAC,CAAC;IACH,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,4BAA4B,CAAC,CAK5C;IACC,gGAAgG;IAChG,+FAA+F;IAC/F,MAAM,OAAO,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,cAAc,CAAC,CAAC;IACjD,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;IACtC,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;QAC5B,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC;YAAE,SAAS;QACrE,IAAI,CAAC,KAAK,CAAC;YAAE,UAAU,GAAG,IAAI,CAAC;;YAC1B,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC;AACrC,CAAC;AAED;;;;;;;;;;GAUG;AACH;;0BAE0B;AAC1B,KAAK,UAAU,wBAAwB,CAAC,CAA2B;IACjE,MAAM,KAAK,GAAoB;QAC7B,WAAW,EAAE,CAAC,CAAC,WAAW,IAAI,SAAS;QACvC,MAAM,EAAE,CAAC,CAAC,MAAM;QAChB,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAClE,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC5D,CAAC;IACF,iGAAiG;IACjG,8FAA8F;IAC9F,uDAAuD;IACvD,MAAM,iBAAiB,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,8BAA8B,CAAC,CAA2B,EAAE,IAA0B;IAC1G,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC/D,0FAA0F;IAC1F,8FAA8F;IAC9F,iGAAiG;IACjG,8DAA8D;IAC9D,MAAM,MAAM,GAAG,CAAC,CAAC,cAAc,IAAI,qBAAqB,EAAE,CAAC;IAC3D,MAAM,QAAQ,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAAC;IAC3C,MAAM,QAAQ,GAAG,4BAA4B,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,cAAc,EAAE,CAAC,CAAC,cAAc,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;IAC1H,IAAI,QAAQ,CAAC,UAAU;QAAE,OAAO,CAAC,+EAA+E;IAChH,iGAAiG;IACjG,iGAAiG;IACjG,iGAAiG;IACjG,6FAA6F;IAC7F,+FAA+F;IAC/F,iDAAiD;IACjD,IAAI,CAAC,CAAC,UAAU,KAAK,QAAQ;QAAE,MAAM,CAAC,IAAI,CAAC,WAAW,IAAI,wBAAwB,CAAC,CAAC,CAAC,CAAC,CAAC;IACvF,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC7E,gGAAgG;IAChG,iGAAiG;IACjG,iGAAiG;IACjG,MAAM,KAAK,GAAuB;QAChC,YAAY,EAAE,CAAC,CAAC,YAAY,IAAI,IAAI;QACpC,MAAM,EAAE,CAAC,CAAC,MAAM,IAAI,IAAI;QACxB,WAAW,EAAE,CAAC,CAAC,WAAW;QAC1B,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC1C,CAAC;IAEF,mGAAmG;IACnG,mGAAmG;IACnG,mGAAmG;IACnG,IAAI,WAA+B,CAAC;IACpC,IAAI,IAAI,CAAC,WAAW,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;QAC3C,IAAI,CAAC;YACH,WAAW,GAAG,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,UAAU,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,SAAS,CAAC;QAChG,CAAC;QAAC,MAAM,CAAC;YACP,WAAW,GAAG,SAAS,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,MAAM,MAAM,GAAG,KAAK,EAAE,KAAa,EAAE,UAAkB,EAAE,IAAiC,EAAE,kBAA2B,EAAE,EAAE;QACzH,MAAM,KAAK,GAAmB;YAC5B,GAAG,yBAAyB,CAAC;gBAC3B,UAAU,EAAE,CAAC,CAAC,UAAU;gBACxB,MAAM,EAAE,CAAC,CAAC,MAAM;gBAChB,YAAY,EAAE,CAAC,CAAC,YAAY;gBAC5B,OAAO,EAAE,CAAC,CAAC,OAAO;gBAClB,cAAc,EAAE,CAAC,CAAC,cAAc;gBAChC,cAAc,EAAE,CAAC,CAAC,cAAc;gBAChC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM;gBACxB,aAAa,EAAE,CAAC,CAAC,aAAa;gBAC9B,aAAa,EAAE,CAAC,CAAC,eAAe;gBAChC,IAAI;gBACJ,kBAAkB;aACnB,CAAC;YACF,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACxC,CAAC;QACF,wFAAwF;QACxF,6FAA6F;QAC7F,2FAA2F;QAC3F,qEAAqE;QACrE,MAAM,EAAE,GAAG,kBAAkB,CAAC,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5E,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,UAAU,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QACtD,6FAA6F;QAC7F,+FAA+F;QAC/F,8FAA8F;QAC9F,EAAE;QACF,gGAAgG;QAChG,+FAA+F;QAC/F,iGAAiG;QACjG,6CAA6C;QAC7C,MAAM,IAAI,GAAG,sBAAsB,CAAC,CAAC,CAAC,IAAI,EAAE,wBAAwB,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;QACtH,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7F,CAAC,CAAC;IAEF,6FAA6F;IAC7F,6FAA6F;IAC7F,2FAA2F;IAC3F,2EAA2E;IAC3E,8FAA8F;IAC9F,kGAAkG;IAClG,kGAAkG;IAClG,iGAAiG;IACjG,mGAAmG;IACnG,wBAAwB;IACxB,MAAM,OAAO,GACX,OAAO,CAAC,eAAe,IAAI,CAAC,IAAI,CAAC,OAAO;QACtC,CAAC,CAAC,SAAS;QACX,CAAC,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC;YACjB,SAAS,EAAE,CAAC,CAAC,MAAM;YACnB,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzD,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACnE,OAAO,EAAE,kBAAkB,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,+BAA+B,CAAC;YACjH,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE;YACxE,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE;YACvB,UAAU,EAAE,CAAC,CAAC,UAAU;SACzB,CAAC,CAAC;IACT,MAAM,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC;IAC1I,IAAI,IAAI,CAAC,YAAY;QAAE,MAAM,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IACpE,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;QACpB,iGAAiG;QACjG,kEAAkE;QAClE,MAAM,IAAI,CAAC,UAAU,CACnB;YACE,IAAI,EAAE,MAAM;YACZ,EAAE,EAAE,KAAK;YACT,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI;YACpB,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK;YACtB,8FAA8F;YAC9F,8FAA8F;YAC9F,+FAA+F;YAC/F,8FAA8F;YAC9F,oCAAoC;YACpC,GAAG,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YAC7D,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,IAAI,SAAS;YACvC,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/D,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,EAAE;YAChB,QAAQ,EAAE,QAAQ;YAClB,SAAS,EAAE,CAAC,CAAC,UAAU;YACvB,UAAU,EAAE,CAAC,CAAC,YAAY;YAC1B,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI;SACrB,EACD,OAAO,CAAC,KAAK,EACb,CAAC,CAAC,UAAU,CACb,CAAC;IACJ,CAAC;AACH,CAAC;AAED,kGAAkG;AAClG,iEAAiE;AACjE,OAAO,EAAE,YAAY,EAAE,CAAC"}
@@ -0,0 +1,35 @@
1
+ export declare const NOTION_WEBHOOK_TIMING_ACTION = "notion.webhook_timing";
2
+ export type NotionWebhookTimingPhase = "credential" | "botTriggerPage" | "ancestorSiblingTrust" | "engagementDedupe"
3
+ /** Inclusive dispatch span. The public detail subtracts the nested Eve and final-audit spans. */
4
+ | "dispatch" | "eveSend" | "finalAudit";
5
+ export interface NotionWebhookTimingDetail {
6
+ readonly [key: string]: unknown;
7
+ readonly outcome: string;
8
+ readonly failedPhase?: NotionWebhookTimingPhase;
9
+ readonly hmacCredentialMs?: number;
10
+ readonly botTriggerPageMs?: number;
11
+ readonly ancestorSiblingTrustMs?: number;
12
+ readonly engagementStoreDedupeMs?: number;
13
+ readonly dispatchPreflightCatchUpMs?: number;
14
+ readonly eveSendMs?: number;
15
+ readonly finalAuditMs?: number;
16
+ readonly totalMs: number;
17
+ }
18
+ export interface NotionWebhookTimingRecorder {
19
+ readonly measure: <T>(phase: NotionWebhookTimingPhase, operation: () => Promise<T>) => Promise<T>;
20
+ readonly measureSync: <T>(phase: NotionWebhookTimingPhase, operation: () => T) => T;
21
+ readonly detail: (outcome: string) => NotionWebhookTimingDetail;
22
+ }
23
+ /**
24
+ * Run one whole route request and emit exactly one final sample. The caller decides when a normal
25
+ * outcome is final; any throw before that point becomes `threw` and is rethrown unchanged. Emission
26
+ * itself is telemetry and therefore fail-soft — it can never replace the HTTP result or error.
27
+ */
28
+ export declare function runNotionWebhookTimed<T>(timing: NotionWebhookTimingRecorder, emit: (detail: NotionWebhookTimingDetail) => void, operation: (finish: (outcome: string) => void) => Promise<T>): Promise<T>;
29
+ /**
30
+ * Create one request-scoped recorder. Phases accumulate because some operations in a phase are
31
+ * separated by gates. `dispatch` deliberately contains `eveSend` and `finalAudit`; detail() removes
32
+ * those nested spans so the published buckets are mutually exclusive rather than double-counted.
33
+ */
34
+ export declare function createNotionWebhookTiming(now?: () => number): NotionWebhookTimingRecorder;
35
+ //# sourceMappingURL=notion-webhook-timing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notion-webhook-timing.d.ts","sourceRoot":"","sources":["../../../agent/channels/notion-webhook-timing.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,4BAA4B,0BAA0B,CAAC;AAEpE,MAAM,MAAM,wBAAwB,GAChC,YAAY,GACZ,gBAAgB,GAChB,sBAAsB,GACtB,kBAAkB;AACpB,iGAAiG;GAC/F,UAAU,GACV,SAAS,GACT,YAAY,CAAC;AAEjB,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,wBAAwB,CAAC;IAChD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IACzC,QAAQ,CAAC,uBAAuB,CAAC,EAAE,MAAM,CAAC;IAC1C,QAAQ,CAAC,0BAA0B,CAAC,EAAE,MAAM,CAAC;IAC7C,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,wBAAwB,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAClG,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,wBAAwB,EAAE,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;IACpF,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,yBAAyB,CAAC;CACjE;AAED;;;;GAIG;AACH,wBAAsB,qBAAqB,CAAC,CAAC,EAC3C,MAAM,EAAE,2BAA2B,EACnC,IAAI,EAAE,CAAC,MAAM,EAAE,yBAAyB,KAAK,IAAI,EACjD,SAAS,EAAE,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,GAC3D,OAAO,CAAC,CAAC,CAAC,CAiBZ;AASD;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,GAAE,MAAM,MAAgC,GAAG,2BAA2B,CAmDlH"}