@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,462 @@
|
|
|
1
|
+
import { type VendorEventTime, shouldRotate, type ExternalPolicy, type IngestedChatMessage, type NormalizedWake, type WakeEngagementTier, type WakeMessageInput, type WakeTrust, type UnifiedWindowWake, type SessionTailWakeRow, type SeatCatchUpRequest, type SeatCatchUpResult } from "@open-everywhere/core/agent/adapter-api.js";
|
|
2
|
+
import { type NotionComment } from "./notion-api.js";
|
|
3
|
+
import type { NotionOwnBotIds } from "./notion-bot-user.js";
|
|
4
|
+
import type { OpenEverywhereNotionAuth } from "./notion-identity.js";
|
|
5
|
+
import type { NotionDeliveryTrust } from "./notion-ancestry-trust.js";
|
|
6
|
+
import type { NotionSessionState } from "./notion-webhook.js";
|
|
7
|
+
import type { NotionVia } from "./notion-scope.js";
|
|
8
|
+
/** The reserved pseudo-thread sentinel for a supervisor generation (mirrors Slack's). */
|
|
9
|
+
export declare function notionSupervisorSentinel(generation: number): string;
|
|
10
|
+
/** The reserved continuation token for a page's supervisor session at a given generation:
|
|
11
|
+
* `notion:<pageId>:oe-sup:g<generation>`. Contains the `:oe-sup:g` infix hooks/spend.ts keys on,
|
|
12
|
+
* and is stable+explicit by construction (a custom-channel token eve never re-anchors — so,
|
|
13
|
+
* UNLIKE Slack, no non-empty state.threadTs re-anchor sentinel is needed; design §3.1). */
|
|
14
|
+
export declare function notionSupervisorToken(pageId: string, generation: number): string;
|
|
15
|
+
/** How a comment addresses the doc supervisor. `drop` = never wake (loop/bot guards); `direct` =
|
|
16
|
+
* a structured bot @mention, the guaranteed-engage conversation-driver treatment; `channel-watch`
|
|
17
|
+
* = judged by the standing supervisor (the ambient default — most comments). */
|
|
18
|
+
export type NotionAddressing = "direct" | "channel-watch" | "drop";
|
|
19
|
+
export interface NotionClassification {
|
|
20
|
+
readonly addressing: NotionAddressing;
|
|
21
|
+
readonly reason: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The Notion synthetic-mention (name-drop) convention. Notion CANNOT @mention an internal
|
|
25
|
+
* integration (notion-webhook.ts — the structured `mentionsUser` path is STRUCTURALLY DEAD for the
|
|
26
|
+
* agent), so a person addresses the doc agent by NAME instead: the deployment's configured names
|
|
27
|
+
* (OE_AGENT_NAME / OE_AGENT_SHORT_NAME, defaulting to the brand "open everywhere" / "oe" when unset)
|
|
28
|
+
* or the platform-resolved integration name (botName). This is the ONLY ambient-INDEPENDENT directed
|
|
29
|
+
* input Notion has, so — unlike Slack/GitHub/Linear, where a name-drop stays a judged input because a
|
|
30
|
+
* real @mention is available — here it is promoted to `direct`.
|
|
31
|
+
*
|
|
32
|
+
* The name set + word-boundary matcher live in core (identity/bot-name.ts matchesAgentName), which
|
|
33
|
+
* is ALSO what the addressed link-claim recogniser strips its leading token with (hooks/link-claim.ts)
|
|
34
|
+
* — one source of truth for "what does this agent answer to". Word-boundary matched (`\b`) so the
|
|
35
|
+
* short "oe" token never fires inside "Zoe"/"poet"/"does". A hit flows through the SAME seam a real
|
|
36
|
+
* @mention would: planNotionSingleWindow stamps `direct-mention`, which marks the wake as OWED a
|
|
37
|
+
* reply and tells the seat so. 🔴 THE STAMP DELIVERS NOTHING BY ITSELF (OPE-1982): the terminal-prose
|
|
38
|
+
* bridge it used to arm is deleted, so the reply lands via createCommentReply only when the seat
|
|
39
|
+
* calls `reply` — on its own turn, or on the ONE corrective re-drive a prose ending earns.
|
|
40
|
+
*/
|
|
41
|
+
export declare function notionNameDrop(text: string, botName?: string): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Decide how the doc supervisor should treat one comment. The self-reply loop guard is HARDENED
|
|
44
|
+
* here (design §4.2), in cost order:
|
|
45
|
+
* 1. UNRESOLVED PRIMARY bot user id ⇒ DROP (fail closed) — self-identity is a precondition for the
|
|
46
|
+
* loop guard; without it the agent would loop on its own replies (each createCommentReply fires a
|
|
47
|
+
* fresh comment.created). This is the critical hardening over the old `if (botUserId && …)`.
|
|
48
|
+
* 2. SELF-authored ⇒ DROP — our own posted reply. The comparison is `created_by.id` against
|
|
49
|
+
* {@link NotionOwnBotIds.all}: EVERY configured integration's bot user id, not only the one that
|
|
50
|
+
* verified this delivery (OPE-1921). THAT IS THE WHOLE GUARD.
|
|
51
|
+
* 🔴 THE SET IS WHAT MAKES IT A GUARD ON A DEPLOYMENT RATHER THAN ON ONE INTEGRATION. Two
|
|
52
|
+
* integrations can both see one page, so a reply integration A posted is delivered on B's
|
|
53
|
+
* subscription too. Compared against B's id alone it matched nothing, read as a person and woke
|
|
54
|
+
* a turn; A's subscription then saw B's answer. Each reply is a NEW comment id, so the OPE-1906
|
|
55
|
+
* once-guard cannot bound that loop — only recognising our own words can.
|
|
56
|
+
* 3. UNIDENTIFIED AUTHOR (no `created_by.id`) ⇒ DROP (fail closed) — step 2 is the only check that
|
|
57
|
+
* can recognise our own words, so a comment whose author id we cannot read defeats it.
|
|
58
|
+
* 🔴 WITHOUT THIS STEP THE GUARD FALLS BACK ON `created_by.type`, A FIELD NOTION MAY OMIT
|
|
59
|
+
* (OPE-1857, notion-api.ts NotionComment): a comment's `created_by` is a PARTIAL user, so an
|
|
60
|
+
* id-less author reached step 4 with nothing to test, read as a person, and woke a turn. That
|
|
61
|
+
* is the same fail-closed posture step 1 takes for the other half of the comparison — an
|
|
62
|
+
* identity we cannot establish never wakes the agent, in EITHER direction.
|
|
63
|
+
* 4. BOT/integration-authored (created_by.type present and not "person") ⇒ DROP — the Slack
|
|
64
|
+
* authorIsBot analog, prevents bot-to-bot loops. A BACKSTOP for a foreign bot, never the
|
|
65
|
+
* self-guard: an ABSENT `type` says nothing, and steps 2-3 have already answered "is it ours".
|
|
66
|
+
* 5. INCOMPLETE OWN-ID SET ⇒ DROP (fail closed, OPE-1921) — one configured integration's bot user
|
|
67
|
+
* id did not resolve, so step 2 could not have recognised that integration's own reply. An
|
|
68
|
+
* author Notion states is a `person` is still admitted; an author it says NOTHING about is
|
|
69
|
+
* exactly the ambiguity the loop rides, so it drops.
|
|
70
|
+
* 🔴 IT COSTS AVAILABILITY AND THE TRADE IS DELIBERATE — see {@link NotionOwnBotIds.complete}.
|
|
71
|
+
* A deployment that cannot resolve one integration's id is deaf to every comment Notion does not
|
|
72
|
+
* mark `person`-authored until it can. 🔴 THAT IS NOT ONLY AMBIENT CHATTER: this step sits ABOVE
|
|
73
|
+
* steps 6 and 7, so an addressed structured @mention, an addressed name-drop and an
|
|
74
|
+
* `oe link <code>` identity claim all drop with it. Failing OPEN instead buys an unbounded reply
|
|
75
|
+
* loop between two of our own integrations, which no once-guard can stop.
|
|
76
|
+
* 6. STRUCTURED bot @mention ⇒ direct (guaranteed engage). Structurally unreachable for an
|
|
77
|
+
* internal integration today, but kept as the canonical addressed signal. Tested against the
|
|
78
|
+
* PRIMARY id alone: a structured mention names ONE bot user, and answering a mention of a
|
|
79
|
+
* sibling integration would put this integration in a conversation it was not addressed in.
|
|
80
|
+
* 7. NAME-DROP ⇒ direct ({@link notionNameDrop}). Notion's ONLY addressed path (no integration
|
|
81
|
+
* @mention), so a brand/name hit is promoted to the owed-a-reply treatment, not left as a
|
|
82
|
+
* judged input — this is the deliberate Notion-specific divergence from Slack/GitHub/Linear.
|
|
83
|
+
* 8. otherwise ⇒ channel-watch (judged by the standing doc supervisor).
|
|
84
|
+
*
|
|
85
|
+
* 🔴 THIS FUNCTION GUARDS ONE COMMENT, NOT ONE WAKE. It cannot tell a second delivery of the SAME
|
|
86
|
+
* comment from the first, because both carry identical bytes. The delivery once-guard is the other
|
|
87
|
+
* half of the loop defence and lives at the ingress
|
|
88
|
+
* (notion-engagement.ts `firstNotionDispatchOfComment`, called by notion-webhook.ts).
|
|
89
|
+
*/
|
|
90
|
+
export declare function classifyNotionComment(comment: NotionComment, own: NotionOwnBotIds, botName?: string): NotionClassification;
|
|
91
|
+
/** The host-frozen reply-target facts one comment.created delivery resolves (the webhook core's
|
|
92
|
+
* dispatch fields) — everything {@link planNotionSingleWindow} needs to mint the wake origin. */
|
|
93
|
+
export interface NotionSingleWindowTarget {
|
|
94
|
+
/** The trigger comment's inline discussion thread — createCommentReply's discussion_id target.
|
|
95
|
+
* Empty ⇒ no freezable reply target ⇒ NO window (fail closed, never an unresolvable wake). */
|
|
96
|
+
readonly discussionId: string;
|
|
97
|
+
readonly pageId?: string;
|
|
98
|
+
readonly workspaceId?: string | null;
|
|
99
|
+
/** The Notion integration this delivery VERIFIED under (OPE-467) — frozen into the origin so the
|
|
100
|
+
* unified reply posts under the credential that fetched the trigger comment. */
|
|
101
|
+
readonly credentialId?: string;
|
|
102
|
+
/** The first latency anchor (OPE-1716), recorded on the ingress row. A Notion delivery carries no
|
|
103
|
+
* vendor clock this seam can read, so the caller passes its own FRONT-DOOR RECEIPT — which makes
|
|
104
|
+
* the vendor→ingress segment a lower bound, and the row's `vendorTsSource` says so. */
|
|
105
|
+
readonly vendorTs?: VendorEventTime;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The ONE single-window decision for a Notion delivery (channels/unified-window.ts
|
|
109
|
+
* singleWindowMentionPolicy is the SHARED seam — the exact function the GitHub reference's
|
|
110
|
+
* native-suppression AND classifier consult, github.ts:261 / github-supervisor.ts:139): given
|
|
111
|
+
* the grain's `singleWindow`, whether the classified comment is a structured @mention
|
|
112
|
+
* (classifyNotionComment returned `direct`), and the PRIVACY FLOOR `privacyOk`, return the
|
|
113
|
+
* {@link UnifiedWindowWake} the dispatch should carry, or undefined for the byte-identical
|
|
114
|
+
* native path.
|
|
115
|
+
*
|
|
116
|
+
* `privacyOk` is the OPERATOR-VOUCH fact the caller resolves (notion.ts): identity/trust.ts
|
|
117
|
+
* isVouchedInternal on the page's workspaceKey/surfaceKey (or the OE_TRUST=internal blanket
|
|
118
|
+
* switch), fail-closed when unregistered. This is the SAME per-surface allowlist that gates
|
|
119
|
+
* Notion's shared MEMORY (notion-identity.ts / bindNotion), so a page is admitted to the shared
|
|
120
|
+
* cross-surface WINDOW exactly when the operator has vouched it internal — never inferred from a
|
|
121
|
+
* misleading API field (a Notion page's `public_url` is set ONLY by publish-to-web and is null
|
|
122
|
+
* for the common guest-shared vector, so it would admit an external guest's mention; the vouch
|
|
123
|
+
* register is the doctrine-correct floor — operator vouches, code enforces, no inference).
|
|
124
|
+
*
|
|
125
|
+
* Notion's mention signal lives in ONE webhook delivery (unlike Slack's dual-event), and its
|
|
126
|
+
* "native" handler IS this same supervisor dispatch — so suppress-native and divert are the SAME
|
|
127
|
+
* act here: a window-carrying wake is delivered through the unified channel instead of the notion
|
|
128
|
+
* channel's own send (notion.ts's wake dep), and both legs read THIS one verdict off the one
|
|
129
|
+
* parsed event — structurally never a double-handle, never a gap:
|
|
130
|
+
* • @mention on a singleWindow, VOUCHED-internal page → the seam suppresses the native session
|
|
131
|
+
* and stamps `addressing:"direct-mention"` (the terminal-prose delivery bridge's arm);
|
|
132
|
+
* • ambient (channel-watch) comment on the same page → Leg A: the window WITHOUT the addressed
|
|
133
|
+
* stamp (judged treatment in the shared window, prose stays hard-dropped);
|
|
134
|
+
* • un-vouched page, or singleWindow off, or no discussion id → undefined (the native
|
|
135
|
+
* per-doc/domain path, byte-identical — a mention there keeps today's `direct` wake).
|
|
136
|
+
*/
|
|
137
|
+
export declare function planNotionSingleWindow(singleWindow: boolean, isMention: boolean, privacyOk: boolean, target: NotionSingleWindowTarget): UnifiedWindowWake | undefined;
|
|
138
|
+
/** One comment reshaped for the wake — the trigger, or a discussion-sibling backscroll message.
|
|
139
|
+
* `from` is resolved by the caller (self when the bot authored it, else human). */
|
|
140
|
+
export interface NotionWakeMessage {
|
|
141
|
+
readonly from: "human" | "self";
|
|
142
|
+
readonly id: string;
|
|
143
|
+
readonly authorId?: string;
|
|
144
|
+
readonly sentAt: string;
|
|
145
|
+
readonly text: string;
|
|
146
|
+
/** Authenticated parent block of an inline comment. Absent for page-level discussions. */
|
|
147
|
+
readonly anchorId?: string;
|
|
148
|
+
/**
|
|
149
|
+
* Resolved per-message trust tier (identity/wake-trust.ts — the audit-wedge F3 assignment). The
|
|
150
|
+
* live dispatch (notion.ts) resolves it off the binding gate + admin rosters, for the trigger AND
|
|
151
|
+
* every discussion sibling.
|
|
152
|
+
*
|
|
153
|
+
* 🔴 REQUIRED, NOT OPTIONAL (OPE-596) — see LinearSupervisorWakeInput.trigger.trust. The trigger
|
|
154
|
+
* used to default to `principal` on the direct path and a sibling to `peer`, both fail-OPEN, and
|
|
155
|
+
* since OPE-577 the tier decides the `<untrusted_external_data>` fence. A caller that cannot
|
|
156
|
+
* resolve a tier states the fail-closed one itself: `relay` for a machine author, `unauth` for an
|
|
157
|
+
* unvouchable human.
|
|
158
|
+
*/
|
|
159
|
+
readonly trust: WakeTrust;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* The SAME message BEFORE any author tier is resolved — what notion-webhook.ts produces (OPE-596).
|
|
163
|
+
*
|
|
164
|
+
* The webhook core reads the comment payload; it holds no binding context, so it cannot answer "what
|
|
165
|
+
* standing does this author carry?". The ROUTE answers that, once, for the trigger and every
|
|
166
|
+
* sibling alike (notion.ts `trustOf`, fail-closed to `relay`). This type states that split instead
|
|
167
|
+
* of leaving it to a convention: an untiered message is NOT a {@link NotionWakeMessage} and the
|
|
168
|
+
* compiler will not let one reach {@link buildNotionSupervisorWake}. Before OPE-596 the same two
|
|
169
|
+
* shapes shared one type and the builder defaulted the missing tier UPWARD, so a route leg that
|
|
170
|
+
* forgot the stamp would have rendered a stranger's comment as a member's.
|
|
171
|
+
*/
|
|
172
|
+
export type NotionUntieredWakeMessage = Omit<NotionWakeMessage, "trust">;
|
|
173
|
+
export interface NotionSupervisorWakeInput {
|
|
174
|
+
/** direct (guaranteed) or channel-watch (judged) — `drop` never reaches here. */
|
|
175
|
+
readonly addressing: Exclude<NotionAddressing, "drop">;
|
|
176
|
+
readonly pageId: string;
|
|
177
|
+
readonly discussionId: string;
|
|
178
|
+
readonly trigger: NotionWakeMessage;
|
|
179
|
+
/** The OTHER comments in the same discussion, oldest-first — envelope backscroll siblings.
|
|
180
|
+
* Re-sent every wake (cheap at Notion volume) so the judge always has full thread context,
|
|
181
|
+
* even mid-session (design §3.2). Ignored on the `direct` path (renderWake returns the body
|
|
182
|
+
* verbatim there). */
|
|
183
|
+
readonly threadComments?: readonly NotionWakeMessage[];
|
|
184
|
+
/** The OUTGOING generation's recorded TAIL (OPE-794) — rendered BEFORE this wake's own thread
|
|
185
|
+
* comments, so the envelope reads oldest-first across the rotation boundary. Only a FRESH
|
|
186
|
+
* generation's first wake carries one. */
|
|
187
|
+
readonly tail?: readonly WakeMessageInput[];
|
|
188
|
+
/** This surface could not read its own page discussion since the seat's last wake (core
|
|
189
|
+
* channels/seat-catch-up.ts). Rendered as a trusted `<system-note>`; unset ⇒ no note. */
|
|
190
|
+
readonly catchUpUnavailable?: string;
|
|
191
|
+
/** Engagement calibration for the judged preamble (memory/domain.ts engagementTierFor —
|
|
192
|
+
* resolved by the caller; this core stays config-free). Unset ⇒ neutral ⇒ byte-identical. */
|
|
193
|
+
readonly engagementTier?: WakeEngagementTier;
|
|
194
|
+
/** Host-minted wake id (rendered `<wake wake-id=…>`) — SINGLE-WINDOW path only (channels/
|
|
195
|
+
* unified-window.ts): the id the model echoes to `reply` so the host resolves this wake's
|
|
196
|
+
* origin from the map. Unset ⇒ byte-identical (no attribute) — mirrors
|
|
197
|
+
* GitHubSupervisorWakeInput.wakeId. */
|
|
198
|
+
readonly wakeId?: string;
|
|
199
|
+
/** SINGLE-WINDOW addressed @mention (mirrors GitHubSupervisorWakeInput.directMention): render
|
|
200
|
+
* the wake as `direct-mention` — the full judged ENVELOPE (so the `wake-id` survives for the
|
|
201
|
+
* reply tool, the ONLY reply route on the unified channel) with the ADDRESSED preamble instead
|
|
202
|
+
* of the silence-biased watch one. Only meaningful alongside `addressing: "direct"` (the plan
|
|
203
|
+
* only sets it for a real @mention); unset ⇒ today's render, byte-identical — a plain `direct`
|
|
204
|
+
* wake stays the verbatim body. */
|
|
205
|
+
readonly directMention?: boolean;
|
|
206
|
+
/**
|
|
207
|
+
* This page carries an ENGAGED registry row — the seam's `deliver` verdict — so a JUDGED wake
|
|
208
|
+
* states `follow-up="thread-engaged"`, the envelope's own words for "you are already in this
|
|
209
|
+
* conversation and `stand_down` is how you leave" (instructions/access.ts's standing block).
|
|
210
|
+
*
|
|
211
|
+
* 🔴 THE ROW ALREADY SAYS IT AND THE ENVELOPE DID NOT (OPE-1711). `notionWakeAddressing` promotes
|
|
212
|
+
* the `notion.dispatched` row to `thread-follow` off this same verdict, so the deployment knows the
|
|
213
|
+
* page is enrolled and tells its audit trail — while the seat reads bare ambient activity. Ship
|
|
214
|
+
* #197 measured the cost of that gap on LINEAR, whose seam has the identical shape: a standing
|
|
215
|
+
* session declined an engaged follow-up as `user_requested_silence` on a stop asked in an earlier
|
|
216
|
+
* conversation. The fix is OPE-1673's, made on Slack and ported to both ticket surfaces.
|
|
217
|
+
*
|
|
218
|
+
* Like Slack's it does NOT touch `addressing`: `channel-watch` stays the honest silence bar for a
|
|
219
|
+
* standing session, and enrolment is a different fact from that bar. Unset/false ⇒ no attribute.
|
|
220
|
+
*/
|
|
221
|
+
readonly threadEngaged?: boolean;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* Build the NormalizedWake for one doc-supervisor turn. platform "notion", containerNoun "doc",
|
|
225
|
+
* locus.channel = the page, locus.thread = the discussion, backscroll = the discussion's other
|
|
226
|
+
* comments as oldest-first envelope siblings, ending at the triggering comment. renderWake then
|
|
227
|
+
* emits the exact `{message, context}` shape every OE dispatch sends — the SAME renderer Slack
|
|
228
|
+
* uses, with NO platform branch. A `direct` wake carries no preamble/backscroll (the body is the
|
|
229
|
+
* driver, verbatim); a `channel-watch` wake gets the judged envelope + doc-noun preamble.
|
|
230
|
+
*/
|
|
231
|
+
export declare function buildNotionSupervisorWake(input: NotionSupervisorWakeInput): NormalizedWake;
|
|
232
|
+
/** The live supervisor session pointer for a page, as the deps report it. `freshGeneration` is
|
|
233
|
+
* true for a brand-new OR just-rotated generation — its first wake carries the carried tail. */
|
|
234
|
+
export interface NotionSupervisorSession {
|
|
235
|
+
readonly token: string;
|
|
236
|
+
readonly generation: number;
|
|
237
|
+
readonly freshGeneration: boolean;
|
|
238
|
+
/** The OUTGOING generation's recorded tail, oldest-first — empty unless this is a fresh
|
|
239
|
+
* generation's first wake (OPE-794). */
|
|
240
|
+
readonly tail: readonly WakeMessageInput[];
|
|
241
|
+
/** The session's PREVIOUS wake time (the stored lastWakeAt, read BEFORE touchSession advances it),
|
|
242
|
+
* or null/absent when there is no prior delivered wake (a fresh/rotated generation). The cursor
|
|
243
|
+
* the memory-delta push (deps.memoryDelta) diffs against; unset ⇒ no delta computed. */
|
|
244
|
+
readonly lastWakeAt?: string | null;
|
|
245
|
+
}
|
|
246
|
+
/** Deps for {@link handleNotionSupervisorDispatch} — mirror SupervisorRouteDeps (slack-events.ts),
|
|
247
|
+
* injected so the core is unit-testable over a mock store + a capture `wake`. org is baked into
|
|
248
|
+
* the live impl's closures (a deployment fact — notion.ts), so it never crosses this boundary. */
|
|
249
|
+
export interface NotionSupervisorDeps {
|
|
250
|
+
/** Create-or-fetch the live supervisor session for this page's surfaceKey: mint generation 1 on
|
|
251
|
+
* first sighting, rotate to generation+1 when {@link shouldRotate} says it aged out, else
|
|
252
|
+
* return the current generation. */
|
|
253
|
+
readonly getOrCreateSession: (surfaceKey: string, pageId: string, atIso: string) => Promise<NotionSupervisorSession>;
|
|
254
|
+
/** Record one wake on the current generation (lastWakeAt/turnCount). Best-effort. */
|
|
255
|
+
readonly touchSession?: (surfaceKey: string, atIso: string) => Promise<void>;
|
|
256
|
+
/**
|
|
257
|
+
* THE SINCE-LAST-WAKE CATCH-UP, through the ONE shared path every surface takes
|
|
258
|
+
* (channels/seat-catch-up.ts `seatCatchUp`). Optional so older test doubles keep compiling
|
|
259
|
+
* (absent ⇒ no catch-up, the pre-OPE-1726 behaviour); fail-soft by construction.
|
|
260
|
+
*/
|
|
261
|
+
readonly catchUp?: (req: SeatCatchUpRequest) => Promise<SeatCatchUpResult>;
|
|
262
|
+
/**
|
|
263
|
+
* THE §2.5 ENGAGEMENT MARK for an ADDRESSED wake (OPE-1501) — defaults to
|
|
264
|
+
* `channels/notion-engagement.ts`'s `markNotionEngaged`, which writes the live registry.
|
|
265
|
+
*
|
|
266
|
+
* 🔴 IT IS A DEP BECAUSE OF WHERE IT HAS TO RUN, not for testability alone. Notion is the one
|
|
267
|
+
* surface whose EXTERNAL-AUTHOR gate is not at the ingress: the webhook core dispatches, and the
|
|
268
|
+
* decision is made HERE, one step later. Marking at the ingress would let an author the operator
|
|
269
|
+
* excluded ENROL a page by naming the agent on it — no reply, but every later plain comment from
|
|
270
|
+
* anyone would then wake a turn — and would let that same suppressed name-drop CLEAR a
|
|
271
|
+
* stand-down, because `participating: true` lifts the `stopped` flag. So the mark sits below the
|
|
272
|
+
* suppression check, where the answer already exists, and this dep is how the webhook core's own
|
|
273
|
+
* test can drive it. Absent in a unit test ⇒ the live writer.
|
|
274
|
+
*/
|
|
275
|
+
readonly markEngaged?: (d: NotionSupervisorDispatch) => Promise<void>;
|
|
276
|
+
/**
|
|
277
|
+
* Record this delivered wake onto the LIVE generation's TAIL ring (OPE-794). Injected because the
|
|
278
|
+
* entry has to be filed under an ORG and a registry key, and this core deliberately holds neither
|
|
279
|
+
* — the live wiring does. Absent (a unit test) ⇒ nothing is recorded and the dispatch is
|
|
280
|
+
* unchanged, which is the same posture `touchSession` takes.
|
|
281
|
+
*/
|
|
282
|
+
readonly recordWake?: (entry: SessionTailWakeRow, token: string, surfaceKey: string) => Promise<void>;
|
|
283
|
+
/** Start/continue the supervisor session under its reserved token, posting the rendered wake.
|
|
284
|
+
* `window` is present ONLY on the SINGLE-WINDOW path (the dispatch input carried it): the
|
|
285
|
+
* wiring (notion.ts) then DIVERTS the wake through eve's cross-channel `receive` into the
|
|
286
|
+
* synthetic `unified` channel instead of this channel's own send. Absent ⇒ byte-identical. */
|
|
287
|
+
readonly wake: (input: {
|
|
288
|
+
token: string;
|
|
289
|
+
auth: OpenEverywhereNotionAuth;
|
|
290
|
+
message: string;
|
|
291
|
+
context: readonly string[];
|
|
292
|
+
state: NotionSessionState;
|
|
293
|
+
window?: UnifiedWindowWake;
|
|
294
|
+
}) => Promise<void>;
|
|
295
|
+
/** Compute the "changed since your last wake" `<system-reminder>` PUSH (memory/wake-delta.ts):
|
|
296
|
+
* given the session's previous wake time (`sinceIso`), this wake's time (`atIso`), and this
|
|
297
|
+
* session's own key (`selfKey` = its continuation token, to exclude its own writes), return the
|
|
298
|
+
* ready block or null. Injected so the pure dispatch stays testable; the live wiring (notion.ts)
|
|
299
|
+
* sets it ONLY when armed (supervisor + unified org gate), so unset ⇒ byte-identical. */
|
|
300
|
+
readonly memoryDelta?: (sinceIso: string, atIso: string, selfKey?: string) => Promise<string | null>;
|
|
301
|
+
/** Clock seam (defaults to the real clock). */
|
|
302
|
+
readonly now?: () => string;
|
|
303
|
+
}
|
|
304
|
+
/** Everything notion-webhook hands the supervisor dispatch: the classification + the wake parts +
|
|
305
|
+
* the identity/session facts. Built by the webhook core, consumed here. */
|
|
306
|
+
export interface NotionSupervisorDispatch {
|
|
307
|
+
readonly surfaceKey: string;
|
|
308
|
+
/**
|
|
309
|
+
* The Notion channel credential this delivery runs under (OPE-467) — the integration whose
|
|
310
|
+
* secret verified the signature, else (dev hatch only) the page's domain binding. Selected ONCE, by the webhook core,
|
|
311
|
+
* and carried here so the route's own Notion calls (the spend gate's visible refusal comment)
|
|
312
|
+
* cannot re-derive a DIFFERENT one. Nothing in this module reads it; it exists so there is
|
|
313
|
+
* exactly one selection per delivery rather than two rules that agree by luck.
|
|
314
|
+
*/
|
|
315
|
+
readonly credentialId: string;
|
|
316
|
+
/** direct | channel-watch — `drop` is filtered upstream (never dispatched). */
|
|
317
|
+
readonly addressing: Exclude<NotionAddressing, "drop">;
|
|
318
|
+
readonly pageId: string;
|
|
319
|
+
readonly discussionId: string;
|
|
320
|
+
readonly workspaceId: string | null;
|
|
321
|
+
readonly auth: OpenEverywhereNotionAuth;
|
|
322
|
+
readonly trigger: NotionWakeMessage;
|
|
323
|
+
readonly threadComments: readonly NotionWakeMessage[];
|
|
324
|
+
/** Engagement calibration tier for the judged preamble (resolved by the webhook core). */
|
|
325
|
+
readonly engagementTier?: WakeEngagementTier;
|
|
326
|
+
/**
|
|
327
|
+
* The EXTERNAL-PARTICIPANT POLICY for this page (OPE-507), RESOLVED BY THE CALLER — this core
|
|
328
|
+
* stays config-free, exactly as {@link NotionSupervisorDispatch.engagementTier} does. The live
|
|
329
|
+
* wiring (notion.ts) resolves it as `resolveExternalPolicy(externalPolicyOverrideFor(surfaceKey,
|
|
330
|
+
* domains))`, so the org default and the domain override are both applied before it arrives here.
|
|
331
|
+
*
|
|
332
|
+
* Unset ⇒ the ORG DEFAULT alone (`resolveExternalPolicy()`), which ships `taint` ⇒ byte-identical
|
|
333
|
+
* to before this field existed. Every pure/test caller therefore stays unchanged.
|
|
334
|
+
*/
|
|
335
|
+
readonly externalPolicy?: ExternalPolicy;
|
|
336
|
+
/** UNIFIED cross-surface grain resolved by the route leg (supervisorGrainFor, fail-soft false) —
|
|
337
|
+
* stamps the wake's platform address + P5 disclosure AND marks the session state `oeUnified`
|
|
338
|
+
* (the message-tool-only gate's key in notion.ts). Unset ⇒ byte-identical per-doc path. */
|
|
339
|
+
readonly unified?: boolean;
|
|
340
|
+
/** SINGLE-WINDOW wake plumbing, minted by the route leg via {@link planNotionSingleWindow} when
|
|
341
|
+
* the grain says `singleWindow` AND the page passed the privacy floor: the wakeId is rendered
|
|
342
|
+
* into the envelope by this core; the whole object is passed through to deps.wake, whose
|
|
343
|
+
* wiring registers the map entry and diverts into the unified channel. Unset ⇒ byte-identical. */
|
|
344
|
+
readonly window?: UnifiedWindowWake;
|
|
345
|
+
/**
|
|
346
|
+
* The ANCESTOR-TRUST verdict for this delivery's page (OPE-492), resolved ONCE by the webhook core
|
|
347
|
+
* and carried here for the ROUTE. Nothing in THIS module reads it. The route reads it twice:
|
|
348
|
+
* • the AUDIT ROW records WHICH refusal happened (`database-boundary`, `hop-limit`, `cycle`,
|
|
349
|
+
* `unresolvable`, `workspace-root`, `no-roots`) rather than only that the page was untrusted;
|
|
350
|
+
* • the SINGLE-WINDOW PRIVACY FLOOR accepts `trusted: true` as an internal signal (OPE-540 —
|
|
351
|
+
* notion.ts `notionSingleWindowPrivacyOk`), so the floor and the memory plane agree on one
|
|
352
|
+
* page. The floor only READS this verdict; it never re-derives it, so it stays synchronous.
|
|
353
|
+
* The trust also travels on {@link NotionSupervisorDispatch.auth} as the `trust_root` attribute,
|
|
354
|
+
* which is what `bindNotion` binds.
|
|
355
|
+
*
|
|
356
|
+
* Unset on a caller that declares no roots — the resolver returns `no-roots` with no I/O, and the
|
|
357
|
+
* webhook core omits the field. Both readers must treat the absent field as NOT trusted.
|
|
358
|
+
*/
|
|
359
|
+
readonly trust?: NotionDeliveryTrust;
|
|
360
|
+
/** SINGLE-WINDOW addressed @mention (the plan's window carries `addressing:"direct-mention"`):
|
|
361
|
+
* render the wake as the addressed `direct-mention` variant (envelope kept, watch preamble
|
|
362
|
+
* swapped for the addressed one). Unset ⇒ today's renders, byte-identical. */
|
|
363
|
+
readonly directMention?: boolean;
|
|
364
|
+
/**
|
|
365
|
+
* The §2.5 ENGAGEMENT SEAM is what admitted this delivery (OPE-1510): the page carries an
|
|
366
|
+
* engagement row, and this PLAIN comment is a follow-up on it. Resolved ONCE by the webhook core —
|
|
367
|
+
* the one place that reads the registry — and carried here for the ROUTE, exactly as {@link
|
|
368
|
+
* NotionSupervisorDispatch.trust} is.
|
|
369
|
+
*
|
|
370
|
+
* 🔴 IT DOES NOT CHANGE HOW THE WAKE IS JUDGED. `addressing` stays `channel-watch`, so the
|
|
371
|
+
* silence bar is untouched. The field has two readers, and both only STATE the enrolment:
|
|
372
|
+
* • the route's `notion.dispatched` row says WHAT WOKE IT (`notionWakeAddressing`,
|
|
373
|
+
* channels/notion-engagement.ts);
|
|
374
|
+
* • the WAKE ENVELOPE says it to the seat as `follow-up="thread-engaged"` (OPE-1711 — it reaches
|
|
375
|
+
* {@link NotionSupervisorWakeInput.threadEngaged}, whose 🔴 carries the measurement).
|
|
376
|
+
* One verdict feeds both, so the row and the envelope can never disagree. Unset ⇒ the row reports
|
|
377
|
+
* the classifier's own value and the envelope states no contract, byte-identical to before.
|
|
378
|
+
*/
|
|
379
|
+
readonly engagedFollowUp?: boolean;
|
|
380
|
+
/**
|
|
381
|
+
* HOW THE OPERATOR'S OWN CONFIG HOLDS THIS PAGE (OPE-1510): `roster` — a domain lists the page by
|
|
382
|
+
* id; `container` — a domain reaches it by a CONTAINER entry, which is the whole workspace or
|
|
383
|
+
* a same-level page glob (memory/domain.ts); `none` — no domain holds it. Resolved
|
|
384
|
+
* ONCE by the webhook core, by the same `notionPageDomain` read its addressed door gates on
|
|
385
|
+
* (channels/notion-scope.ts), and carried here for the ROUTE exactly as {@link
|
|
386
|
+
* NotionSupervisorDispatch.engagedFollowUp} is.
|
|
387
|
+
*
|
|
388
|
+
* 🔴 NOTHING IN THIS MODULE READS IT. It only lets the route's `notion.dispatched` row record the
|
|
389
|
+
* DOMAIN ORIGIN beside what woke the delivery, so a name-drop admitted by a claim is
|
|
390
|
+
* distinguishable from one on a page an operator listed. Unset ⇒ the row reads `none`.
|
|
391
|
+
*/
|
|
392
|
+
readonly domainVia?: NotionVia;
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* The dispatch AS THE WEBHOOK CORE BUILDS IT (OPE-596) — every field of {@link
|
|
396
|
+
* NotionSupervisorDispatch} except that its messages carry no author tier yet (see {@link
|
|
397
|
+
* NotionUntieredWakeMessage}). The route leg (notion.ts) stamps the tier on the trigger and on every
|
|
398
|
+
* sibling, and that stamp is what turns this into a {@link NotionSupervisorDispatch} the supervisor
|
|
399
|
+
* core will accept. The two types are what stop the stamp being an optional step.
|
|
400
|
+
*/
|
|
401
|
+
export interface NotionUntieredSupervisorDispatch extends Omit<NotionSupervisorDispatch, "trigger" | "threadComments"> {
|
|
402
|
+
readonly trigger: NotionUntieredWakeMessage;
|
|
403
|
+
readonly threadComments: readonly NotionUntieredWakeMessage[];
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* THE INGESTED SET for one Notion wake, graded for the non-member taint join (OPE-466 / OPE-516).
|
|
407
|
+
*
|
|
408
|
+
* 🔴 NOTION HAD NO AUTHOR LEG AT ALL BEFORE OPE-516, and it could not have had one. The join's
|
|
409
|
+
* `refuted` field is the SURFACE's positive refutation — Slack's `is_restricted`, Teams' Entra
|
|
410
|
+
* `userType: "guest"` — and Notion publishes NO membership fact about a comment author. So the
|
|
411
|
+
* question is answered from the operator's own AUTHOR-TRUST REGISTER instead
|
|
412
|
+
* (identity/author-trust.ts): on a workspace the operator has armed, an author they did not declare
|
|
413
|
+
* grades `external`. On an unarmed workspace the leg contributes nothing and this is a no-op.
|
|
414
|
+
*
|
|
415
|
+
* `refuted` is deliberately never set here. Leaving it absent is the honest statement — Notion has
|
|
416
|
+
* no verdict to give, and a hardcoded `false` would read as "we asked and it was fine".
|
|
417
|
+
*
|
|
418
|
+
* NOTION HAS NO GROUP CONCEPT, so no memberships are contributed and a Notion declaration is
|
|
419
|
+
* principal-keys-only. That is a platform fact, not a gap in this function.
|
|
420
|
+
*
|
|
421
|
+
* A `self` author (the bot's own prior comment) carries no `authorId` from the webhook core, and its
|
|
422
|
+
* resolved tier is the machine floor, so it can never reach the register leg — the join requires a
|
|
423
|
+
* positively-human tier.
|
|
424
|
+
*
|
|
425
|
+
* Pure and exported so the grading is testable without a store or a live wake.
|
|
426
|
+
*/
|
|
427
|
+
export declare function notionIngestedMessages(d: {
|
|
428
|
+
readonly workspaceId: string | null;
|
|
429
|
+
readonly trigger: NotionWakeMessage;
|
|
430
|
+
readonly threadComments: readonly NotionWakeMessage[];
|
|
431
|
+
}): readonly IngestedChatMessage[];
|
|
432
|
+
/**
|
|
433
|
+
* THE EXTERNAL-AUTHOR DECISION for one Notion dispatch (OPE-507). Pure, exported so the three
|
|
434
|
+
* behaviours are testable without a store or a live wake.
|
|
435
|
+
*
|
|
436
|
+
* Notion's three answers, and why they are what they are:
|
|
437
|
+
* • the TRIGGER's author is excluded ⇒ NO WAKE AT ALL (`suppressed`). That covers behaviour 2
|
|
438
|
+
* (ambient never triggers on their comment) and behaviour 1 (their @mention is not acted on).
|
|
439
|
+
* • a SIBLING's author is excluded ⇒ the wake proceeds and that sibling's body is WITHHELD
|
|
440
|
+
* (behaviour 3): a permitted member's later comment still wakes the agent, and the excluded
|
|
441
|
+
* author's earlier comment reaches the model as the arrived-marker.
|
|
442
|
+
* • nothing is excluded, or the policy is `taint` ⇒ `withheldIds` is empty and every caller path
|
|
443
|
+
* is byte-identical to before this existed.
|
|
444
|
+
*
|
|
445
|
+
* 🔴 NOTION POSTS NO PRIVATE NOTICE, AND THAT IS DELIBERATE. Behaviour 1 owes the excluded sender a
|
|
446
|
+
* PRIVATE notice, and Notion has no private channel back to a commenter — a comment reply lands in
|
|
447
|
+
* the shared discussion, where everyone reading the doc sees it. Announcing "I will not act on your
|
|
448
|
+
* message" into a shared doc is a disclosure, not a notice, so the Notion answer is to do nothing.
|
|
449
|
+
* The surfaces that HAVE a private channel (a Discord DM, a Slack DM) post the notice there.
|
|
450
|
+
*/
|
|
451
|
+
export declare function notionExternalAuthorDecision(d: {
|
|
452
|
+
readonly trigger: NotionWakeMessage;
|
|
453
|
+
readonly threadComments: readonly NotionWakeMessage[];
|
|
454
|
+
readonly ingested: readonly IngestedChatMessage[];
|
|
455
|
+
readonly policy: ExternalPolicy;
|
|
456
|
+
}): {
|
|
457
|
+
readonly suppressed: boolean;
|
|
458
|
+
readonly withheldIds: ReadonlySet<string>;
|
|
459
|
+
};
|
|
460
|
+
export declare function handleNotionSupervisorDispatch(d: NotionSupervisorDispatch, deps: NotionSupervisorDeps): Promise<void>;
|
|
461
|
+
export { shouldRotate };
|
|
462
|
+
//# sourceMappingURL=notion-supervisor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notion-supervisor.d.ts","sourceRoot":"","sources":["../../../agent/channels/notion-supervisor.ts"],"names":[],"mappings":"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"}
|