@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.
- package/LICENSE +201 -0
- package/NOTICE +21 -0
- package/README.md +48 -0
- package/agent/channels/notion-acting-identity.ts +88 -0
- package/agent/channels/notion-ancestry-trust.ts +458 -0
- package/agent/channels/notion-api.ts +1290 -0
- package/agent/channels/notion-bot-user.ts +250 -0
- package/agent/channels/notion-catch-up.ts +339 -0
- package/agent/channels/notion-credentials.ts +417 -0
- package/agent/channels/notion-engagement.ts +372 -0
- package/agent/channels/notion-event-normalizer.ts +587 -0
- package/agent/channels/notion-identity.ts +271 -0
- package/agent/channels/notion-ingress-drain.ts +289 -0
- package/agent/channels/notion-ingress.ts +182 -0
- package/agent/channels/notion-message-ops.ts +140 -0
- package/agent/channels/notion-origin-codec.ts +38 -0
- package/agent/channels/notion-post-arm.ts +189 -0
- package/agent/channels/notion-question-return.ts +223 -0
- package/agent/channels/notion-read.ts +283 -0
- package/agent/channels/notion-scope.ts +560 -0
- package/agent/channels/notion-stop.ts +169 -0
- package/agent/channels/notion-supervisor.ts +856 -0
- package/agent/channels/notion-webhook-timing.ts +131 -0
- package/agent/channels/notion-webhook-verifier.ts +55 -0
- package/agent/channels/notion-webhook.ts +963 -0
- package/agent/channels/notion-work-search.ts +74 -0
- package/agent/channels/notion-work.ts +144 -0
- package/agent/channels/notion.ts +1242 -0
- package/agent/contributions.ts +39 -0
- package/agent/docs-generated.ts +15 -0
- package/agent/memory/blocks.ts +295 -0
- package/agent/memory/memory.ts +622 -0
- package/agent/provider-events.ts +198 -0
- package/agent/register.ts +144 -0
- package/agent/schedules/notion-ingress.ts +20 -0
- package/agent/surface-parity.ts +105 -0
- package/agent/tools/notion_search.ts +110 -0
- package/agent/tools/read_notion_comments.ts +250 -0
- package/agent/tools/read_notion_page.ts +351 -0
- package/dist/agent/channels/notion-acting-identity.d.ts +23 -0
- package/dist/agent/channels/notion-acting-identity.d.ts.map +1 -0
- package/dist/agent/channels/notion-acting-identity.js +85 -0
- package/dist/agent/channels/notion-acting-identity.js.map +1 -0
- package/dist/agent/channels/notion-ancestry-trust.d.ts +208 -0
- package/dist/agent/channels/notion-ancestry-trust.d.ts.map +1 -0
- package/dist/agent/channels/notion-ancestry-trust.js +331 -0
- package/dist/agent/channels/notion-ancestry-trust.js.map +1 -0
- package/dist/agent/channels/notion-api.d.ts +655 -0
- package/dist/agent/channels/notion-api.d.ts.map +1 -0
- package/dist/agent/channels/notion-api.js +938 -0
- package/dist/agent/channels/notion-api.js.map +1 -0
- package/dist/agent/channels/notion-bot-user.d.ts +107 -0
- package/dist/agent/channels/notion-bot-user.d.ts.map +1 -0
- package/dist/agent/channels/notion-bot-user.js +192 -0
- package/dist/agent/channels/notion-bot-user.js.map +1 -0
- package/dist/agent/channels/notion-catch-up.d.ts +64 -0
- package/dist/agent/channels/notion-catch-up.d.ts.map +1 -0
- package/dist/agent/channels/notion-catch-up.js +289 -0
- package/dist/agent/channels/notion-catch-up.js.map +1 -0
- package/dist/agent/channels/notion-credentials.d.ts +210 -0
- package/dist/agent/channels/notion-credentials.d.ts.map +1 -0
- package/dist/agent/channels/notion-credentials.js +360 -0
- package/dist/agent/channels/notion-credentials.js.map +1 -0
- package/dist/agent/channels/notion-engagement.d.ts +219 -0
- package/dist/agent/channels/notion-engagement.d.ts.map +1 -0
- package/dist/agent/channels/notion-engagement.js +283 -0
- package/dist/agent/channels/notion-engagement.js.map +1 -0
- package/dist/agent/channels/notion-event-normalizer.d.ts +194 -0
- package/dist/agent/channels/notion-event-normalizer.d.ts.map +1 -0
- package/dist/agent/channels/notion-event-normalizer.js +542 -0
- package/dist/agent/channels/notion-event-normalizer.js.map +1 -0
- package/dist/agent/channels/notion-identity.d.ts +119 -0
- package/dist/agent/channels/notion-identity.d.ts.map +1 -0
- package/dist/agent/channels/notion-identity.js +205 -0
- package/dist/agent/channels/notion-identity.js.map +1 -0
- package/dist/agent/channels/notion-ingress-drain.d.ts +66 -0
- package/dist/agent/channels/notion-ingress-drain.d.ts.map +1 -0
- package/dist/agent/channels/notion-ingress-drain.js +185 -0
- package/dist/agent/channels/notion-ingress-drain.js.map +1 -0
- package/dist/agent/channels/notion-ingress.d.ts +64 -0
- package/dist/agent/channels/notion-ingress.d.ts.map +1 -0
- package/dist/agent/channels/notion-ingress.js +130 -0
- package/dist/agent/channels/notion-ingress.js.map +1 -0
- package/dist/agent/channels/notion-message-ops.d.ts +3 -0
- package/dist/agent/channels/notion-message-ops.d.ts.map +1 -0
- package/dist/agent/channels/notion-message-ops.js +126 -0
- package/dist/agent/channels/notion-message-ops.js.map +1 -0
- package/dist/agent/channels/notion-origin-codec.d.ts +7 -0
- package/dist/agent/channels/notion-origin-codec.d.ts.map +1 -0
- package/dist/agent/channels/notion-origin-codec.js +32 -0
- package/dist/agent/channels/notion-origin-codec.js.map +1 -0
- package/dist/agent/channels/notion-post-arm.d.ts +3 -0
- package/dist/agent/channels/notion-post-arm.d.ts.map +1 -0
- package/dist/agent/channels/notion-post-arm.js +185 -0
- package/dist/agent/channels/notion-post-arm.js.map +1 -0
- package/dist/agent/channels/notion-question-return.d.ts +77 -0
- package/dist/agent/channels/notion-question-return.d.ts.map +1 -0
- package/dist/agent/channels/notion-question-return.js +183 -0
- package/dist/agent/channels/notion-question-return.js.map +1 -0
- package/dist/agent/channels/notion-read.d.ts +62 -0
- package/dist/agent/channels/notion-read.d.ts.map +1 -0
- package/dist/agent/channels/notion-read.js +257 -0
- package/dist/agent/channels/notion-read.js.map +1 -0
- package/dist/agent/channels/notion-scope.d.ts +188 -0
- package/dist/agent/channels/notion-scope.d.ts.map +1 -0
- package/dist/agent/channels/notion-scope.js +433 -0
- package/dist/agent/channels/notion-scope.js.map +1 -0
- package/dist/agent/channels/notion-stop.d.ts +48 -0
- package/dist/agent/channels/notion-stop.d.ts.map +1 -0
- package/dist/agent/channels/notion-stop.js +141 -0
- package/dist/agent/channels/notion-stop.js.map +1 -0
- package/dist/agent/channels/notion-supervisor.d.ts +462 -0
- package/dist/agent/channels/notion-supervisor.d.ts.map +1 -0
- package/dist/agent/channels/notion-supervisor.js +482 -0
- package/dist/agent/channels/notion-supervisor.js.map +1 -0
- package/dist/agent/channels/notion-webhook-timing.d.ts +35 -0
- package/dist/agent/channels/notion-webhook-timing.d.ts.map +1 -0
- package/dist/agent/channels/notion-webhook-timing.js +99 -0
- package/dist/agent/channels/notion-webhook-timing.js.map +1 -0
- package/dist/agent/channels/notion-webhook-verifier.d.ts +5 -0
- package/dist/agent/channels/notion-webhook-verifier.d.ts.map +1 -0
- package/dist/agent/channels/notion-webhook-verifier.js +53 -0
- package/dist/agent/channels/notion-webhook-verifier.js.map +1 -0
- package/dist/agent/channels/notion-webhook.d.ts +242 -0
- package/dist/agent/channels/notion-webhook.d.ts.map +1 -0
- package/dist/agent/channels/notion-webhook.js +675 -0
- package/dist/agent/channels/notion-webhook.js.map +1 -0
- package/dist/agent/channels/notion-work-search.d.ts +9 -0
- package/dist/agent/channels/notion-work-search.d.ts.map +1 -0
- package/dist/agent/channels/notion-work-search.js +69 -0
- package/dist/agent/channels/notion-work-search.js.map +1 -0
- package/dist/agent/channels/notion-work.d.ts +68 -0
- package/dist/agent/channels/notion-work.d.ts.map +1 -0
- package/dist/agent/channels/notion-work.js +126 -0
- package/dist/agent/channels/notion-work.js.map +1 -0
- package/dist/agent/channels/notion.d.ts +95 -0
- package/dist/agent/channels/notion.d.ts.map +1 -0
- package/dist/agent/channels/notion.js +1081 -0
- package/dist/agent/channels/notion.js.map +1 -0
- package/dist/agent/contributions.d.ts +32 -0
- package/dist/agent/contributions.d.ts.map +1 -0
- package/dist/agent/contributions.js +37 -0
- package/dist/agent/contributions.js.map +1 -0
- package/dist/agent/docs-generated.d.ts +3 -0
- package/dist/agent/docs-generated.d.ts.map +1 -0
- package/dist/agent/docs-generated.js +13 -0
- package/dist/agent/docs-generated.js.map +1 -0
- package/dist/agent/memory/blocks.d.ts +58 -0
- package/dist/agent/memory/blocks.d.ts.map +1 -0
- package/dist/agent/memory/blocks.js +264 -0
- package/dist/agent/memory/blocks.js.map +1 -0
- package/dist/agent/memory/memory.d.ts +157 -0
- package/dist/agent/memory/memory.d.ts.map +1 -0
- package/dist/agent/memory/memory.js +471 -0
- package/dist/agent/memory/memory.js.map +1 -0
- package/dist/agent/provider-events.d.ts +3 -0
- package/dist/agent/provider-events.d.ts.map +1 -0
- package/dist/agent/provider-events.js +196 -0
- package/dist/agent/provider-events.js.map +1 -0
- package/dist/agent/register.d.ts +5 -0
- package/dist/agent/register.d.ts.map +1 -0
- package/dist/agent/register.js +128 -0
- package/dist/agent/register.js.map +1 -0
- package/dist/agent/schedules/notion-ingress.d.ts +6 -0
- package/dist/agent/schedules/notion-ingress.d.ts.map +1 -0
- package/dist/agent/schedules/notion-ingress.js +16 -0
- package/dist/agent/schedules/notion-ingress.js.map +1 -0
- package/dist/agent/surface-parity.d.ts +3 -0
- package/dist/agent/surface-parity.d.ts.map +1 -0
- package/dist/agent/surface-parity.js +99 -0
- package/dist/agent/surface-parity.js.map +1 -0
- package/dist/agent/tools/notion_search.d.ts +56 -0
- package/dist/agent/tools/notion_search.d.ts.map +1 -0
- package/dist/agent/tools/notion_search.js +100 -0
- package/dist/agent/tools/notion_search.js.map +1 -0
- package/dist/agent/tools/read_notion_comments.d.ts +57 -0
- package/dist/agent/tools/read_notion_comments.d.ts.map +1 -0
- package/dist/agent/tools/read_notion_comments.js +236 -0
- package/dist/agent/tools/read_notion_comments.js.map +1 -0
- package/dist/agent/tools/read_notion_page.d.ts +53 -0
- package/dist/agent/tools/read_notion_page.d.ts.map +1 -0
- package/dist/agent/tools/read_notion_page.js +327 -0
- package/dist/agent/tools/read_notion_page.js.map +1 -0
- package/package.json +231 -0
- package/tsconfig.build.json +22 -0
- 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":"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"}
|
|
@@ -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"}
|