@open-everywhere/provider-github 0.0.0-stage โ 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 +50 -2
- package/agent/channels/github-catch-up.ts +134 -0
- package/agent/channels/github-engagement.ts +381 -0
- package/agent/channels/github-identity.ts +376 -0
- package/agent/channels/github-installations.ts +152 -0
- package/agent/channels/github-message-ops.ts +62 -0
- package/agent/channels/github-origin-codec.ts +42 -0
- package/agent/channels/github-post-arm.ts +422 -0
- package/agent/channels/github-reaction-feedback.ts +489 -0
- package/agent/channels/github-reaction-scope.ts +312 -0
- package/agent/channels/github-read.ts +471 -0
- package/agent/channels/github-stop.ts +175 -0
- package/agent/channels/github-supervisor.ts +645 -0
- package/agent/channels/github-webhook-verifier.ts +56 -0
- package/agent/channels/github.ts +1458 -0
- package/agent/contributions.ts +31 -0
- package/agent/docs-generated.ts +15 -0
- package/agent/memory/memory.ts +265 -0
- package/agent/ops.ts +17 -0
- package/agent/provider-events.ts +114 -0
- package/agent/register.ts +130 -0
- package/agent/surface-parity.ts +116 -0
- package/dist/agent/channels/github-catch-up.d.ts +29 -0
- package/dist/agent/channels/github-catch-up.d.ts.map +1 -0
- package/dist/agent/channels/github-catch-up.js +105 -0
- package/dist/agent/channels/github-catch-up.js.map +1 -0
- package/dist/agent/channels/github-engagement.d.ts +223 -0
- package/dist/agent/channels/github-engagement.d.ts.map +1 -0
- package/dist/agent/channels/github-engagement.js +317 -0
- package/dist/agent/channels/github-engagement.js.map +1 -0
- package/dist/agent/channels/github-identity.d.ts +164 -0
- package/dist/agent/channels/github-identity.d.ts.map +1 -0
- package/dist/agent/channels/github-identity.js +295 -0
- package/dist/agent/channels/github-identity.js.map +1 -0
- package/dist/agent/channels/github-installations.d.ts +49 -0
- package/dist/agent/channels/github-installations.d.ts.map +1 -0
- package/dist/agent/channels/github-installations.js +114 -0
- package/dist/agent/channels/github-installations.js.map +1 -0
- package/dist/agent/channels/github-message-ops.d.ts +3 -0
- package/dist/agent/channels/github-message-ops.d.ts.map +1 -0
- package/dist/agent/channels/github-message-ops.js +58 -0
- package/dist/agent/channels/github-message-ops.js.map +1 -0
- package/dist/agent/channels/github-origin-codec.d.ts +7 -0
- package/dist/agent/channels/github-origin-codec.d.ts.map +1 -0
- package/dist/agent/channels/github-origin-codec.js +36 -0
- package/dist/agent/channels/github-origin-codec.js.map +1 -0
- package/dist/agent/channels/github-post-arm.d.ts +54 -0
- package/dist/agent/channels/github-post-arm.d.ts.map +1 -0
- package/dist/agent/channels/github-post-arm.js +339 -0
- package/dist/agent/channels/github-post-arm.js.map +1 -0
- package/dist/agent/channels/github-reaction-feedback.d.ts +97 -0
- package/dist/agent/channels/github-reaction-feedback.d.ts.map +1 -0
- package/dist/agent/channels/github-reaction-feedback.js +380 -0
- package/dist/agent/channels/github-reaction-feedback.js.map +1 -0
- package/dist/agent/channels/github-reaction-scope.d.ts +102 -0
- package/dist/agent/channels/github-reaction-scope.d.ts.map +1 -0
- package/dist/agent/channels/github-reaction-scope.js +296 -0
- package/dist/agent/channels/github-reaction-scope.js.map +1 -0
- package/dist/agent/channels/github-read.d.ts +135 -0
- package/dist/agent/channels/github-read.d.ts.map +1 -0
- package/dist/agent/channels/github-read.js +371 -0
- package/dist/agent/channels/github-read.js.map +1 -0
- package/dist/agent/channels/github-stop.d.ts +52 -0
- package/dist/agent/channels/github-stop.d.ts.map +1 -0
- package/dist/agent/channels/github-stop.js +145 -0
- package/dist/agent/channels/github-stop.js.map +1 -0
- package/dist/agent/channels/github-supervisor.d.ts +308 -0
- package/dist/agent/channels/github-supervisor.d.ts.map +1 -0
- package/dist/agent/channels/github-supervisor.js +339 -0
- package/dist/agent/channels/github-supervisor.js.map +1 -0
- package/dist/agent/channels/github-webhook-verifier.d.ts +5 -0
- package/dist/agent/channels/github-webhook-verifier.d.ts.map +1 -0
- package/dist/agent/channels/github-webhook-verifier.js +55 -0
- package/dist/agent/channels/github-webhook-verifier.js.map +1 -0
- package/dist/agent/channels/github.d.ts +139 -0
- package/dist/agent/channels/github.d.ts.map +1 -0
- package/dist/agent/channels/github.js +1232 -0
- package/dist/agent/channels/github.js.map +1 -0
- package/dist/agent/contributions.d.ts +12 -0
- package/dist/agent/contributions.d.ts.map +1 -0
- package/dist/agent/contributions.js +31 -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/memory.d.ts +54 -0
- package/dist/agent/memory/memory.d.ts.map +1 -0
- package/dist/agent/memory/memory.js +224 -0
- package/dist/agent/memory/memory.js.map +1 -0
- package/dist/agent/ops.d.ts +3 -0
- package/dist/agent/ops.d.ts.map +1 -0
- package/dist/agent/ops.js +16 -0
- package/dist/agent/ops.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 +112 -0
- package/dist/agent/provider-events.js.map +1 -0
- package/dist/agent/register.d.ts +4 -0
- package/dist/agent/register.d.ts.map +1 -0
- package/dist/agent/register.js +116 -0
- package/dist/agent/register.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 +110 -0
- package/dist/agent/surface-parity.js.map +1 -0
- package/package.json +156 -4
- package/tsconfig.build.json +22 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
// The GitHub ambient supervisor โ the eve-FREE tested core that turns every NON-mention
|
|
2
|
+
// issue/PR-timeline comment on an opted-in repo into a JUDGED wake to a standing per-REPO
|
|
3
|
+
// supervisor session. It is the SECOND non-Slack adopter of the surface-general
|
|
4
|
+
// ambient-supervision pattern (surface-adapter-port.md ยง"Ambient supervision everywhere"),
|
|
5
|
+
// deliberately THIN โ it copies notion-supervisor.ts (the template) with the two GitHub-shaped
|
|
6
|
+
// differences called out below. Linear copies this pair next.
|
|
7
|
+
//
|
|
8
|
+
// WHAT'S REUSED WHOLESALE (zero GitHub-specific logic in any of it):
|
|
9
|
+
// โข renderWake + the channel-watch preamble (the judge) โ normalized-wake.ts (containerNoun
|
|
10
|
+
// "repo"; the engagement tier rides the same field โ GitHub PLATFORM-DEFAULTS to `reserved`,
|
|
11
|
+
// memory/domain.ts engagementTierFor)
|
|
12
|
+
// โข shouldRotate โ the pure rotation policy โ slack-events.ts
|
|
13
|
+
// โข the (org, surfaceKey) channel_session registry + the :oe-sup:g spend counter โ via deps
|
|
14
|
+
// โข githubBotMentioned โ the SAME mention gate the native path wakes on โ github-identity.ts
|
|
15
|
+
//
|
|
16
|
+
// THE TWO GITHUB-SHAPED DIFFERENCES vs the Notion template:
|
|
17
|
+
// 1. MENTIONS ARE DROPPED, not `direct`. eve's native githubChannel already owns the @mention
|
|
18
|
+
// path (github.ts onComment โ session + checkout + reply); a mention reaching the ambient
|
|
19
|
+
// leg too would double-handle the comment. classifyGitHubComment reuses githubBotMentioned
|
|
20
|
+
// โ the EXACT gate the native path wakes on โ so the two legs partition every comment
|
|
21
|
+
// between them with no overlap and no gap.
|
|
22
|
+
// 2. DRIVE-BY REPOS ARE DROPPED (fail closed). A repo ambient-wakes only when it is positively
|
|
23
|
+
// private (every commenter was invited by the org โ the Notion/Slack membership analog) OR
|
|
24
|
+
// the operator has VOUCHED it internal (identity/trust.ts). On a public, un-vouched repo any
|
|
25
|
+
// drive-by stranger could wake judged turns โ an uninvited spend + prompt-injection surface
|
|
26
|
+
// the design's "GitHub repos in a domain are ~always private" assumption deliberately
|
|
27
|
+
// excludes. The VOUCH additionally widens ambient to a vouched public repo (the operator's
|
|
28
|
+
// explicit "this surface is internal" assertion), and โ separately โ is the SOLE floor for
|
|
29
|
+
// the cross-surface single WINDOW (see classifyGitHubAmbientComment): a private flag never
|
|
30
|
+
// admits the shared window, only the vouch does.
|
|
31
|
+
//
|
|
32
|
+
// The repo-session is the container-session's ORCHESTRATION HALF ONLY (surface-adapter-port
|
|
33
|
+
// ยง"container session"): a repo-scoped standing session aware of the repo's issue/PR
|
|
34
|
+
// conversations, holding repo memory, that judges every comment and replies into the triggering
|
|
35
|
+
// issue. There is NO container-conversation half โ a repo has no top-level chat to post to
|
|
36
|
+
// (containerConversation: false), so a judged reply always targets the triggering issue/PR.
|
|
37
|
+
import { renderWake, mkMessageTrigger, shouldRotate, singleWindowMentionPolicy, stampNonMemberChatAuth, seatCatchUpSinceMs, admitContextLines, engagedFollowUpContract, tailAuthorGrain, } from "@open-everywhere/core/agent/adapter-api.js";
|
|
38
|
+
import { githubBotMentioned } from "./github-identity.js";
|
|
39
|
+
/** The catch-up's REACHBACK FLOOR (hours): how far back a seat that has been quiet for a long time
|
|
40
|
+
* reads. 7 days, the same floor every other surface's bootstrap window uses, so one number governs
|
|
41
|
+
* "how much history is a seat ever worth" across the fleet. */
|
|
42
|
+
const GITHUB_CATCH_UP_REACHBACK_HOURS = 168;
|
|
43
|
+
// --- supervisor token / sentinel (carry the :oe-sup:g infix โ load-bearing) ----------
|
|
44
|
+
//
|
|
45
|
+
// hooks/spend.ts isSupervisorToken keys off the `:oe-sup:g` substring to (a) tag the usage-event
|
|
46
|
+
// `kind="supervisor"` and (b) drive the per-generation cumulative-token rotation counter, and
|
|
47
|
+
// model/routing.ts isSupervisorSession keys the cheap `supervisor` model role off the same
|
|
48
|
+
// `oe-sup:g<n>` suffix. Carry the infix and the ENTIRE cost/rotation/model-tier apparatus works
|
|
49
|
+
// for free. The token puts `owner/repo` in the channel-id slot, `github:`-namespaced (eve
|
|
50
|
+
// prefixes the channel name again on send โ harmless, mirrors Notion).
|
|
51
|
+
/** The reserved pseudo-thread sentinel for a supervisor generation (mirrors Slack's/Notion's). */
|
|
52
|
+
export function githubSupervisorSentinel(generation) {
|
|
53
|
+
return `oe-sup:g${generation}`;
|
|
54
|
+
}
|
|
55
|
+
/** The reserved continuation token for a repo's supervisor session at a given generation:
|
|
56
|
+
* `github:<owner>/<repo>:oe-sup:g<generation>`. A custom continuation token eve never
|
|
57
|
+
* re-anchors, carrying the `:oe-sup:g` infix hooks/spend.ts + model/routing.ts key on. */
|
|
58
|
+
export function githubSupervisorToken(owner, repo, generation) {
|
|
59
|
+
return `github:${owner}/${repo}:${githubSupervisorSentinel(generation)}`;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Decide how the ambient leg should treat one issue/PR-timeline comment, in cost order:
|
|
63
|
+
* 1. NO bot slug configured โ DROP (fail closed) โ self-identity is a precondition for the
|
|
64
|
+
* loop guard (the same hardening as Notion's unresolved-botUserId drop).
|
|
65
|
+
* 2. BOT-authored (`user.type === "Bot"`) โ DROP โ covers our own `<slug>[bot]` replies AND
|
|
66
|
+
* every other bot (prevents bot-to-bot loops).
|
|
67
|
+
* 3. Our own login (`<slug>[bot]`, belt-and-braces under a mislabelled type) โ DROP.
|
|
68
|
+
* 4. @mentions the bot (githubBotMentioned โ the native path's EXACT gate):
|
|
69
|
+
* โข DEFAULT (per-repo) โ DROP: the native githubChannel session owns mentions; the two legs
|
|
70
|
+
* partition, never overlap.
|
|
71
|
+
* โข SINGLE-WINDOW (`treatMentionAsDirect`) on a VOUCHED repo โ `direct`: github.ts
|
|
72
|
+
* suppresses the native session for exactly this case, so the ambient leg keeps the mention
|
|
73
|
+
* and the unified window answers it as an addressed wake. Still DROP on an un-vouched repo โ
|
|
74
|
+
* the native path is NOT suppressed there (the gate on both sides is
|
|
75
|
+
* `singleWindow && vouched`, the OPERATOR VOUCH โ NOT the repo-private flag), so letting
|
|
76
|
+
* ambient keep it would double-handle.
|
|
77
|
+
* 5. Repo neither positively private NOR operator-vouched โ DROP (fail closed) โ drive-by public
|
|
78
|
+
* repos never ambient-wake (strangers must not buy judged turns; see the header).
|
|
79
|
+
* 6. otherwise โ channel-watch (judged by the standing repo supervisor).
|
|
80
|
+
*
|
|
81
|
+
* `opts.vouched` is the OPERATOR-VOUCH fact the caller resolves (github.ts: isVouchedInternal on
|
|
82
|
+
* `github/<owner>` OR `github/<owner>/<repo>`, or OE_TRUST=internal). It is the SOLE floor for the
|
|
83
|
+
* cross-surface WINDOW (step 4's `direct` divert) โ the repo's own `repoPrivate` flag is a logged
|
|
84
|
+
* defense-in-depth SIGNAL, never a window gate. `repoPrivate` still enables the per-repo ambient
|
|
85
|
+
* path (step 5): a private repo is invite-only, so it ambient-wakes even un-vouched; the vouch
|
|
86
|
+
* WIDENS ambient to a vouched public repo, and never narrows an already-private one.
|
|
87
|
+
*/
|
|
88
|
+
export function classifyGitHubAmbientComment(c, botSlug, opts) {
|
|
89
|
+
const slug = botSlug?.trim();
|
|
90
|
+
if (!slug)
|
|
91
|
+
return { addressing: "drop", reason: "no bot slug configured โ fail-closed loop guard" };
|
|
92
|
+
if (c.authorType === "Bot")
|
|
93
|
+
return { addressing: "drop", reason: "bot-authored comment (loop guard)" };
|
|
94
|
+
if (c.authorLogin && c.authorLogin.toLowerCase() === `${slug}[bot]`.toLowerCase()) {
|
|
95
|
+
return { addressing: "drop", reason: "self-authored comment (our own reply)" };
|
|
96
|
+
}
|
|
97
|
+
const vouched = opts?.vouched === true;
|
|
98
|
+
if (githubBotMentioned(c.body, slug, { login: c.authorLogin, type: c.authorType })) {
|
|
99
|
+
// Single-window VOUCHED repos: the native session is suppressed (github.ts) so the unified
|
|
100
|
+
// window owns the mention as an addressed wake. Every other repo โ the native path owns it.
|
|
101
|
+
// The decision is the SHARED seam (unified-window.ts singleWindowMentionPolicy) โ the exact
|
|
102
|
+
// predicate the native suppression consults, so the two legs can never double-handle or gap.
|
|
103
|
+
// `isMention` is pinned true here (this branch is inside the githubBotMentioned gate); the
|
|
104
|
+
// privacy floor is the operator VOUCH, NOT the repo-private flag.
|
|
105
|
+
if (singleWindowMentionPolicy(opts?.treatMentionAsDirect === true, true, vouched).addressing === "direct-mention") {
|
|
106
|
+
return { addressing: "direct", reason: "bot @mention on a single-window vouched repo โ answered by the unified window" };
|
|
107
|
+
}
|
|
108
|
+
return { addressing: "drop", reason: "bot @mention โ owned by the native githubChannel path" };
|
|
109
|
+
}
|
|
110
|
+
// Per-repo ambient enablement: a private repo is invite-only (the existing product rule) OR the
|
|
111
|
+
// operator vouched it internal (widens to a vouched public repo). repoPrivate is now a SIGNAL โ
|
|
112
|
+
// it enables ambient but is no longer the window floor (the vouch is, above).
|
|
113
|
+
if (c.repoPrivate !== true && !vouched) {
|
|
114
|
+
return { addressing: "drop", reason: "repo neither positively private nor operator-vouched โ fail-closed (no drive-by ambient)" };
|
|
115
|
+
}
|
|
116
|
+
return { addressing: "channel-watch", reason: `ambient comment โ judged by the repo supervisor (private=${c.repoPrivate === true}, vouched=${vouched})` };
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Build the NormalizedWake for one repo-supervisor turn: platform "github", containerNoun
|
|
120
|
+
* "repo", locus.channel = `owner/repo`, locus.thread = `#<issueNumber>` (the conversation the
|
|
121
|
+
* comment belongs to), the issue title as an untrusted-wrapped context line, and the comment as
|
|
122
|
+
* the trigger. `channel-watch` (judged) by default; `direct-mention` (addressed, enveloped) when
|
|
123
|
+
* `directMention` is set โ the SINGLE-WINDOW @mention path, which keeps the wake-id envelope but
|
|
124
|
+
* swaps the silence-biased watch preamble for the addressed one (see the field doc). renderWake
|
|
125
|
+
* then emits the exact `{message, context}` every OE dispatch sends โ the SAME renderer
|
|
126
|
+
* Slack/Notion use, with NO platform branch.
|
|
127
|
+
*/
|
|
128
|
+
export function buildGitHubSupervisorWake(input) {
|
|
129
|
+
const directMention = input.directMention === true;
|
|
130
|
+
// THE SLOT, THEN THE DOMAIN'S ADMISSION POLICY OVER IT ({@link GitHubSupervisorWakeInput.admit} โ
|
|
131
|
+
// OPE-1800). `thread` is the container position: on GitHub the issue/PR IS the conversation
|
|
132
|
+
// (locus.thread below), and this line describes it. Nothing admitted โ no context block at all,
|
|
133
|
+
// never an empty one, which is the same `undefined` an absent title produces.
|
|
134
|
+
const titleLine = input.issueTitle
|
|
135
|
+
? [{ senderType: "user", ts: `#${input.issueNumber}`, text: `issue/PR #${input.issueNumber}: ${input.issueTitle}` }]
|
|
136
|
+
: undefined;
|
|
137
|
+
const admittedTitle = titleLine && input.admit ? admitContextLines(titleLine, input.admit, "thread") : titleLine;
|
|
138
|
+
const contextLines = admittedTitle && admittedTitle.length > 0 ? admittedTitle : undefined;
|
|
139
|
+
return {
|
|
140
|
+
platform: "github",
|
|
141
|
+
// A direct @mention is reason "mention" (honest, and rides the envelope); an ambient comment is
|
|
142
|
+
// "channel-activity". Both keep the full envelope, so the wake-id survives on the single-window path.
|
|
143
|
+
reason: directMention ? "mention" : "channel-activity",
|
|
144
|
+
addressing: directMention ? "direct-mention" : "channel-watch",
|
|
145
|
+
containerNoun: "repo",
|
|
146
|
+
// engagementTier is the AMBIENT-watch calibration dial (it RAISES the reply bar for a
|
|
147
|
+
// `reserved` repo); an addressed @mention is owed an answer, so it never applies to a
|
|
148
|
+
// direct-mention wake โ and is byte-identical (input.engagementTier) on the channel-watch path.
|
|
149
|
+
engagementTier: directMention ? undefined : input.engagementTier,
|
|
150
|
+
// The ENROLMENT fact ({@link GitHubSupervisorWakeInput.threadEngaged}), through the ONE shared
|
|
151
|
+
// helper every enveloped surface calls. The helper drops it on a `direct-mention` wake.
|
|
152
|
+
followUp: engagedFollowUpContract({
|
|
153
|
+
addressing: directMention ? "direct-mention" : "channel-watch",
|
|
154
|
+
threadEngaged: input.threadEngaged,
|
|
155
|
+
}),
|
|
156
|
+
// Single-window wake id โ only the singleWindow dispatch passes it (byte-identical off).
|
|
157
|
+
wakeId: input.wakeId,
|
|
158
|
+
locus: {
|
|
159
|
+
channel: { id: `${input.owner}/${input.repo}` },
|
|
160
|
+
thread: { ts: `#${input.issueNumber}` },
|
|
161
|
+
},
|
|
162
|
+
contextLines,
|
|
163
|
+
contextSource: contextLines ? "the issue/PR this comment is on" : undefined,
|
|
164
|
+
backscroll: input.tail && input.tail.length > 0 ? input.tail : undefined,
|
|
165
|
+
...(input.catchUpUnavailable !== undefined ? { catchUpUnavailable: input.catchUpUnavailable } : {}),
|
|
166
|
+
trigger: mkMessageTrigger({
|
|
167
|
+
from: "human", // self/bot already dropped in classify
|
|
168
|
+
author: input.trigger.authorLogin ?? "unknown",
|
|
169
|
+
authorId: input.trigger.authorLogin,
|
|
170
|
+
id: input.trigger.id,
|
|
171
|
+
sentAt: input.trigger.sentAt,
|
|
172
|
+
// The caller's stated per-person tier โ no default (OPE-596). Directness is the `mention`
|
|
173
|
+
// flag below, never a trust tier.
|
|
174
|
+
trust: input.trigger.trust,
|
|
175
|
+
// The @mention response-obligation flag (rendered `mention="true"`). Unset on the ambient
|
|
176
|
+
// path โ no attribute โ byte-identical render.
|
|
177
|
+
mention: directMention ? true : undefined,
|
|
178
|
+
body: input.trigger.text,
|
|
179
|
+
}),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/** The taint-clean identity fields on one admitted ambient dispatch audit row. */
|
|
183
|
+
export function githubDispatchAuditDetail(input) {
|
|
184
|
+
return {
|
|
185
|
+
addressing: input.addressing,
|
|
186
|
+
issueNumber: input.issueNumber,
|
|
187
|
+
...(input.commentId ? { commentId: input.commentId } : {}),
|
|
188
|
+
...(input.wakeId ? { wakeId: input.wakeId } : {}),
|
|
189
|
+
repoPrivate: input.repoPrivate,
|
|
190
|
+
vouched: input.vouched,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function pick(o, k) {
|
|
194
|
+
return o && typeof o === "object" ? o[k] : undefined;
|
|
195
|
+
}
|
|
196
|
+
function str(v) {
|
|
197
|
+
return typeof v === "string" && v.length > 0 ? v : undefined;
|
|
198
|
+
}
|
|
199
|
+
function num(v) {
|
|
200
|
+
return typeof v === "number" && Number.isFinite(v) ? v : undefined;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Extract the ambient-leg facts from one webhook delivery, or null when the delivery is not an
|
|
204
|
+
* ambient candidate at all: only `issue_comment` + `action: "created"` qualifies (edits/deletes
|
|
205
|
+
* are not conversational triggers v1; review-thread comments are out of scope v1 โ see
|
|
206
|
+
* {@link GitHubAmbientEvent}). Null is "not for this leg", NOT an error โ the native mention
|
|
207
|
+
* path and the routine fan-out see every delivery regardless.
|
|
208
|
+
*/
|
|
209
|
+
export function extractGitHubAmbientEvent(eventType, payload) {
|
|
210
|
+
if (eventType !== "issue_comment")
|
|
211
|
+
return null;
|
|
212
|
+
if (str(pick(payload, "action")) !== "created")
|
|
213
|
+
return null;
|
|
214
|
+
const repository = pick(payload, "repository");
|
|
215
|
+
const issue = pick(payload, "issue");
|
|
216
|
+
const comment = pick(payload, "comment");
|
|
217
|
+
const owner = str(pick(pick(repository, "owner"), "login"));
|
|
218
|
+
const repo = str(pick(repository, "name"));
|
|
219
|
+
const issueNumber = num(pick(issue, "number"));
|
|
220
|
+
if (!owner || !repo || issueNumber === undefined || !comment)
|
|
221
|
+
return null;
|
|
222
|
+
const user = pick(comment, "user");
|
|
223
|
+
const isPrivate = pick(repository, "private");
|
|
224
|
+
const commentId = num(pick(comment, "id"));
|
|
225
|
+
return {
|
|
226
|
+
owner,
|
|
227
|
+
repo,
|
|
228
|
+
repositoryFullName: str(pick(repository, "full_name")),
|
|
229
|
+
repositoryId: num(pick(repository, "id")),
|
|
230
|
+
repoPrivate: typeof isPrivate === "boolean" ? isPrivate : undefined,
|
|
231
|
+
installationId: num(pick(pick(payload, "installation"), "id")),
|
|
232
|
+
issueNumber,
|
|
233
|
+
issueTitle: str(pick(issue, "title")),
|
|
234
|
+
isPullRequest: pick(issue, "pull_request") !== undefined,
|
|
235
|
+
comment: {
|
|
236
|
+
id: commentId !== undefined ? String(commentId) : undefined,
|
|
237
|
+
body: str(pick(comment, "body")),
|
|
238
|
+
authorLogin: str(pick(user, "login")),
|
|
239
|
+
authorType: str(pick(user, "type")),
|
|
240
|
+
authorId: num(pick(user, "id")),
|
|
241
|
+
authorAssociation: str(pick(comment, "author_association")),
|
|
242
|
+
createdAt: str(pick(comment, "created_at")),
|
|
243
|
+
repoPrivate: typeof isPrivate === "boolean" ? isPrivate : undefined,
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Route one kept comment to the repo supervisor: get-or-create the session, build + render the
|
|
249
|
+
* wake for that generation, and deliver it.
|
|
250
|
+
*
|
|
251
|
+
* A DELIVERY failure (the send itself) propagates to the caller's catch. A session that dies at
|
|
252
|
+
* TURN time does not: nothing here observes it, so the trigger is lost SILENTLY, and no reactive
|
|
253
|
+
* arm can exist (OPE-174 removed the inert one; channels/session-resume.ts holds the measurement).
|
|
254
|
+
* The working remedy is PRE-DISPATCH โ the live `getOrCreateSession` claims the OPE-599
|
|
255
|
+
* runtime-change rotation before this call and hands back a session the running eve build can read.
|
|
256
|
+
*/
|
|
257
|
+
export async function handleGitHubSupervisorDispatch(d, deps) {
|
|
258
|
+
const atIso = deps.now ? deps.now() : new Date().toISOString();
|
|
259
|
+
const session = await deps.getOrCreateSession(d.surfaceKey, d.owner, d.repo, atIso);
|
|
260
|
+
const doWake = async (token, generation, tail, catchUpUnavailable) => {
|
|
261
|
+
const nw = buildGitHubSupervisorWake({
|
|
262
|
+
owner: d.owner,
|
|
263
|
+
repo: d.repo,
|
|
264
|
+
issueNumber: d.issueNumber,
|
|
265
|
+
issueTitle: d.issueTitle,
|
|
266
|
+
admit: d.admit,
|
|
267
|
+
trigger: d.trigger,
|
|
268
|
+
engagementTier: d.engagementTier,
|
|
269
|
+
wakeId: d.window?.wakeId,
|
|
270
|
+
directMention: d.directMention,
|
|
271
|
+
threadEngaged: d.threadEngaged,
|
|
272
|
+
tail,
|
|
273
|
+
catchUpUnavailable,
|
|
274
|
+
});
|
|
275
|
+
const { message, context } = renderWake(nw, deps.now);
|
|
276
|
+
// OPE-466 CARRIAGE, wired here by OPE-494. The wake renders its envelope in the INGRESS process,
|
|
277
|
+
// outside any turn, so the taint half of `ingestUntrusted` no-ops and the reading turn would
|
|
278
|
+
// otherwise run with a CLEAN signal over an outside contributor's bytes. The stamp is the same
|
|
279
|
+
// host-set, model-unforgeable auth attribute Slack, Discord, Teams and Notion already carry.
|
|
280
|
+
//
|
|
281
|
+
// POSITION: inside `doWake`, so BOTH delivery routes get it โ this channel's own `send` and the
|
|
282
|
+
// single-window divert (github.ts hands `deps.wake` the auth for either). The room leg reads the
|
|
283
|
+
// OPERATOR's trust declaration for the repo; the author leg reads `d.ingested`. On an unmarked
|
|
284
|
+
// wake the input auth returns BY REFERENCE, so no existing golden moves.
|
|
285
|
+
const auth = stampNonMemberChatAuth(d.auth, d.ingested, { surfaceKey: d.surfaceKey });
|
|
286
|
+
await deps.wake({ token, auth, message, context: context ?? [], window: d.window });
|
|
287
|
+
};
|
|
288
|
+
// A wake failure PROPAGATES to the caller's catch. (OPE-174 removed a terminal-session-death
|
|
289
|
+
// force-rotate branch here: it could never fire โ a conversation-mode session PARKS at eve's
|
|
290
|
+
// token bound rather than failing, and a turn-time error cannot reject the awaited enqueue
|
|
291
|
+
// anyway. The reachable handler is core's agent/channels/session-park.ts.)
|
|
292
|
+
// ๐ด EVERY WAKE READS THE CONVERSATION SINCE THE SEAT LAST LOOKED (OPE-1695 (b), brought to GitHub
|
|
293
|
+
// by OPE-1726). A FRESH generation renders its carried tail, as it always did; an ONGOING
|
|
294
|
+
// generation reads the timeline comments this issue/PR collected since its own `lastWakeAt`,
|
|
295
|
+
// through the ONE shared catch-up path โ same bounds, same admission policy, same cap as Slack's.
|
|
296
|
+
// FAIL-SOFT: the shared path never throws, and an unregistered fetch comes back as the UNAVAILABLE
|
|
297
|
+
// note rather than as a silent empty context.
|
|
298
|
+
const catchUp = session.freshGeneration || !deps.catchUp
|
|
299
|
+
? undefined
|
|
300
|
+
: await deps.catchUp({
|
|
301
|
+
container: `${d.owner}/${d.repo}#${d.issueNumber}`,
|
|
302
|
+
sinceMs: seatCatchUpSinceMs(session.lastWakeAt, Date.parse(atIso) || Date.now(), GITHUB_CATCH_UP_REACHBACK_HOURS),
|
|
303
|
+
untilMs: Date.parse(d.trigger.sentAt) || Date.parse(atIso) || Date.now(),
|
|
304
|
+
excludeId: d.trigger.id,
|
|
305
|
+
channelKey: d.surfaceKey,
|
|
306
|
+
});
|
|
307
|
+
await doWake(session.token, session.generation, session.freshGeneration ? session.tail : (catchUp?.messages ?? []), catchUp?.unavailable);
|
|
308
|
+
if (deps.touchSession)
|
|
309
|
+
await deps.touchSession(d.surfaceKey, atIso);
|
|
310
|
+
if (deps.recordWake) {
|
|
311
|
+
// AFTER the wake landed, and only the TRIGGER โ never the backscroll rendered beside it. That is
|
|
312
|
+
// what stops a tail inheriting a tail (channels/session-tail.ts).
|
|
313
|
+
await deps.recordWake({
|
|
314
|
+
kind: "wake",
|
|
315
|
+
at: atIso,
|
|
316
|
+
from: "human",
|
|
317
|
+
trust: d.trigger.trust,
|
|
318
|
+
// THE AUTHOR GRAIN (OPE-1747) โ `d.ingested[0]` is the TRIGGER's own facts: GitHub sends one
|
|
319
|
+
// comment per wake, so the route leg passes exactly one entry (see `ingested`'s own header),
|
|
320
|
+
// and `stampNonMemberChatAuth` above joins over that same entry. Resolved through the one core
|
|
321
|
+
// road, so this row and the join cannot disagree. A leg that passed `[]` says so on purpose, and
|
|
322
|
+
// the row then records `refuted: false` โ the SAME answer the live wake acted on, not the
|
|
323
|
+
// UNKNOWN an absent field means. UNKNOWN is reserved for a row persisted before this field
|
|
324
|
+
// existed: recording it here would withhold, in the next generation, a body this wake delivered.
|
|
325
|
+
...tailAuthorGrain(d.ingested[0] ?? { trust: d.trigger.trust }),
|
|
326
|
+
author: d.trigger.authorLogin ?? "unknown",
|
|
327
|
+
...(d.trigger.authorLogin ? { authorId: d.trigger.authorLogin } : {}),
|
|
328
|
+
id: d.trigger.id,
|
|
329
|
+
platform: "github",
|
|
330
|
+
container: d.surfaceKey,
|
|
331
|
+
threadRoot: `#${d.issueNumber}`,
|
|
332
|
+
text: d.trigger.text,
|
|
333
|
+
}, session.token, d.surfaceKey);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
// Re-export shouldRotate so the live wiring (github.ts) drives get-or-create/rotate with the
|
|
337
|
+
// SAME pure policy Slack/Notion use, without importing slack-events itself.
|
|
338
|
+
export { shouldRotate };
|
|
339
|
+
//# sourceMappingURL=github-supervisor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-supervisor.js","sourceRoot":"","sources":["../../../agent/channels/github-supervisor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type WebhookVerifier } from "@open-everywhere/core/agent/adapter-api.js";
|
|
2
|
+
/** The registry key โ what a generic hook declares as its `source` to be verified as GitHub. */
|
|
3
|
+
export declare const GITHUB_WEBHOOK_SOURCE = "github";
|
|
4
|
+
export declare const githubWebhookVerifier: WebhookVerifier;
|
|
5
|
+
//# sourceMappingURL=github-webhook-verifier.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-webhook-verifier.d.ts","sourceRoot":"","sources":["../../../agent/channels/github-webhook-verifier.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,4CAA4C,CAAC;AAEzG,gGAAgG;AAChG,eAAO,MAAM,qBAAqB,WAAW,CAAC;AAI9C,eAAO,MAAM,qBAAqB,EAAE,eAyBnC,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// GitHub's webhook signature scheme, as a registry entry (OPE-164) โ what lets a GENERIC hook
|
|
2
|
+
// (`POST /webhooks/<id>`) declare `source: "github"` and be verified exactly the way GitHub signs,
|
|
3
|
+
// instead of GitHub being expected to adopt our scheme (which it never will).
|
|
4
|
+
//
|
|
5
|
+
// ZERO NEW CRYPTO. The digest itself is core's `verifyGithubSignature` (routines/events.ts, through
|
|
6
|
+
// the adapter-api barrel) โ the same function the native github webhook route has always used, so
|
|
7
|
+
// there is one implementation of GitHub's scheme in the tree, not two that can drift.
|
|
8
|
+
//
|
|
9
|
+
// THE SCHEME (https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries):
|
|
10
|
+
// X-Hub-Signature-256: sha256=<hex> HMAC-SHA256 of the RAW body under the webhook secret.
|
|
11
|
+
// `X-Hub-Signature` (SHA-1) is legacy and deliberately NOT accepted โ accepting it would be a
|
|
12
|
+
// downgrade a sender could choose.
|
|
13
|
+
//
|
|
14
|
+
// ๐ด `signedTimestamp: false` โ GitHub signs NOTHING but the body. A captured delivery replays
|
|
15
|
+
// forever. That is GitHub's scheme, not a gap we can close in the verifier; it is declared here
|
|
16
|
+
// so it lands on the reviewed UNDEFENDED_REPLAY_SOURCES allowlist (webhook-verifier-test.ts)
|
|
17
|
+
// rather than being an unwritten fact. It is also exactly why resolution is by DECLARED source:
|
|
18
|
+
// with a weaker verifier registered alongside a timestamped one, letting the REQUEST choose the
|
|
19
|
+
// scheme would be a live downgrade primitive.
|
|
20
|
+
//
|
|
21
|
+
// NOTE this entry changes NO existing route. The native github webhook path keeps its own inline
|
|
22
|
+
// gate; registering here only makes the scheme available to the generic ingress.
|
|
23
|
+
import { verifyGithubSignature } from "@open-everywhere/core/agent/adapter-api.js";
|
|
24
|
+
/** The registry key โ what a generic hook declares as its `source` to be verified as GitHub. */
|
|
25
|
+
export const GITHUB_WEBHOOK_SOURCE = "github";
|
|
26
|
+
const GITHUB_SIGNATURE_HEADER = "x-hub-signature-256";
|
|
27
|
+
export const githubWebhookVerifier = {
|
|
28
|
+
source: GITHUB_WEBHOOK_SOURCE,
|
|
29
|
+
// Provider-owned โ routed through admitProviderRegistration: de-select the github bundle and
|
|
30
|
+
// this row never lands, so a `source: "github"` hook 401s instead of silently using another
|
|
31
|
+
// scheme.
|
|
32
|
+
provider: "github",
|
|
33
|
+
keyKind: "shared-secret",
|
|
34
|
+
// `keyOrigin: "receiver-minted"` (OPE-186) โ GitHub's webhook secret is a value the person
|
|
35
|
+
// CREATING the webhook types into GitHub's own form, not one GitHub generates and hands back:
|
|
36
|
+
// *"Setting a webhook secret allows you to ensure that POST requests sent to the payload URL are
|
|
37
|
+
// from GitHub โฆ you can create a random string"* (the same docs page linked above). That is what
|
|
38
|
+
// makes `source: "github"` legal for a CHAT-MINTED hook, whose key is an OE-minted random secret
|
|
39
|
+
// the approver pastes into that form. Sourced to GitHub's docs, not to anything in this tree.
|
|
40
|
+
keyOrigin: "receiver-minted",
|
|
41
|
+
signedTimestamp: false,
|
|
42
|
+
verify({ rawBody, headers, keys }) {
|
|
43
|
+
const signature = headers.get(GITHUB_SIGNATURE_HEADER);
|
|
44
|
+
if (!signature)
|
|
45
|
+
return { ok: false, failure: "missing-signature" };
|
|
46
|
+
for (const [i, key] of keys.entries()) {
|
|
47
|
+
// Ordered, current-first: a match at i>0 is a rotation overlap and the gate audits it.
|
|
48
|
+
// GitHub documents no overlap window of its own, so `keys[]` is how an operator gets one.
|
|
49
|
+
if (verifyGithubSignature(key, rawBody, signature))
|
|
50
|
+
return { ok: true, keyIndex: i };
|
|
51
|
+
}
|
|
52
|
+
return { ok: false, failure: "signature-mismatch" };
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
//# sourceMappingURL=github-webhook-verifier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-webhook-verifier.js","sourceRoot":"","sources":["../../../agent/channels/github-webhook-verifier.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,mGAAmG;AACnG,8EAA8E;AAC9E,EAAE;AACF,oGAAoG;AACpG,kGAAkG;AAClG,sFAAsF;AACtF,EAAE;AACF,iGAAiG;AACjG,8FAA8F;AAC9F,8FAA8F;AAC9F,mCAAmC;AACnC,EAAE;AACF,+FAA+F;AAC/F,gGAAgG;AAChG,6FAA6F;AAC7F,gGAAgG;AAChG,gGAAgG;AAChG,8CAA8C;AAC9C,EAAE;AACF,iGAAiG;AACjG,iFAAiF;AAEjF,OAAO,EAAE,qBAAqB,EAAwB,MAAM,4CAA4C,CAAC;AAEzG,gGAAgG;AAChG,MAAM,CAAC,MAAM,qBAAqB,GAAG,QAAQ,CAAC;AAE9C,MAAM,uBAAuB,GAAG,qBAAqB,CAAC;AAEtD,MAAM,CAAC,MAAM,qBAAqB,GAAoB;IACpD,MAAM,EAAE,qBAAqB;IAC7B,6FAA6F;IAC7F,4FAA4F;IAC5F,UAAU;IACV,QAAQ,EAAE,QAAQ;IAClB,OAAO,EAAE,eAAe;IACxB,2FAA2F;IAC3F,8FAA8F;IAC9F,iGAAiG;IACjG,iGAAiG;IACjG,iGAAiG;IACjG,8FAA8F;IAC9F,SAAS,EAAE,iBAAiB;IAC5B,eAAe,EAAE,KAAK;IACtB,MAAM,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE;QAC/B,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC;QACvD,IAAI,CAAC,SAAS;YAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,mBAAmB,EAAE,CAAC;QACnE,KAAK,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC;YACtC,uFAAuF;YACvF,0FAA0F;YAC1F,IAAI,qBAAqB,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,CAAC;gBAAE,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;QACvF,CAAC;QACD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC;IACtD,CAAC;CACF,CAAC"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type { RouteHandlerArgs } from "eve/channels";
|
|
2
|
+
import { type GitHubChannel, type GitHubChannelState, type GitHubInboundContext, type GitHubComment, type GitHubInboundResult } from "eve/channels/github";
|
|
3
|
+
import { type GitHubEngagementVerdict, type GitHubSeamFacts } from "./github-engagement.js";
|
|
4
|
+
import { type GitHubAmbientEvent, type GitHubSupervisorDeps } from "./github-supervisor.js";
|
|
5
|
+
import { type IngestedChatMessage, type ExternalPolicy } from "@open-everywhere/core/agent/adapter-api.js";
|
|
6
|
+
/**
|
|
7
|
+
* Does the AMBIENT leg keep this comment โ the second half of the two-leg partition (OPE-1695)?
|
|
8
|
+
* ๐ด THE CHATTINESS FLAG IS BELOW THE CLASSIFIER NOW, AND THAT IS WHAT KEEPS THE LEGS COMPLEMENTARY.
|
|
9
|
+
* It used to gate the whole leg, which was safe only while a seat REQUIRED `ambientSupervisor`: the
|
|
10
|
+
* native leg stood down (`shouldSuppressNativeMention`) exactly when this leg was live. Seats are
|
|
11
|
+
* universal since OPE-1695, so with the flag gating the whole leg a VOUCHED repo whose domain is
|
|
12
|
+
* addressed-only suppressed the native session AND returned here โ and the @mention reached NOBODY.
|
|
13
|
+
* An ADDRESSED comment is an address, and addressing never depended on the flag; only ambient talk
|
|
14
|
+
* does, and a refused ambient comment still records its skip row exactly as before.
|
|
15
|
+
*
|
|
16
|
+
* ๐ด AN ENGAGED CONTAINER'S PLAIN FOLLOW-UP IS AN ADDRESS TOO (OPE-1740/OPE-1745), AND THAT IS THE
|
|
17
|
+
* THIRD INPUT. Moving the flag below the classifier fixed the mention and left the follow-up behind:
|
|
18
|
+
* on the SAME seated, vouched, addressed-only repo the native leg still stands down for every
|
|
19
|
+
* comment (`shouldSuppressNativeMention` asks `singleWindowMentionPolicy` with `isMention` pinned
|
|
20
|
+
* true), so the engaged follow-up came here โ and a `thread-follow` addressing with the flag off was
|
|
21
|
+
* refused `domain-refused` before the seam was ever read. Measured on deploy #201: the plain
|
|
22
|
+
* follow-up in an ENGAGED issue of `oe-e2e-addressed-github` produced
|
|
23
|
+
* `github.ambient_skipped {reason: "domain-refused"}` three seconds after the mention's reply, and
|
|
24
|
+
* NO leg answered it. An @mention ENROLLED the container, so the enrolment is what addresses the
|
|
25
|
+
* comment; the domain opt-in governs UNINVITED talk and has never governed an address.
|
|
26
|
+
*
|
|
27
|
+
* Pure, so the partition is a truth table a test can walk: `drop` is the classifier's own refusal
|
|
28
|
+
* (a bot author, an un-vouched public repo, a mention the native leg owns), and nothing re-opens it.
|
|
29
|
+
* `engagedFollowUp` is REQUIRED rather than defaulted โ a caller that omitted it would silently
|
|
30
|
+
* restore the refusal this parameter exists to close. {@link githubAmbientLegAdmission} is the one
|
|
31
|
+
* place that computes it.
|
|
32
|
+
*/
|
|
33
|
+
export declare function githubAmbientLegKeeps(addressing: string, ambientEnabled: boolean, engagedFollowUp: boolean): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* The AMBIENT leg's whole admission for one comment: read the ยง2.5 seam, THEN apply the chattiness
|
|
36
|
+
* gate with the enrolment fact in hand. One call, so the order cannot drift back.
|
|
37
|
+
*
|
|
38
|
+
* ๐ด THE ORDER IS THE FIX. `resolveGitHubEngagement` runs FIRST and its verdict feeds the gate, so an
|
|
39
|
+
* engaged container's plain follow-up is admitted on an addressed-only domain. Before this, the gate
|
|
40
|
+
* decided alone and the seam below it never got to speak.
|
|
41
|
+
*
|
|
42
|
+
* ๐ด THE COST, STATED. Every kept-by-the-classifier comment now pays ONE engagement-registry read
|
|
43
|
+
* here, where an ambient-off domain used to return on the gate. The read is the seam โ a container's
|
|
44
|
+
* enrolment cannot be known without it โ and it is the SAME read the leg already paid on every
|
|
45
|
+
* ambient-ON domain. `resolveGitHubEngagement` settles a mention with no round-trip at all.
|
|
46
|
+
*
|
|
47
|
+
* `engagedFollowUp` is floored on {@link githubEngagedFollowUpReach}, so a public repo and an inline
|
|
48
|
+
* review comment are refused exactly as they are on the native leg โ this seam never widens an
|
|
49
|
+
* uninvited turn's reach, it only stops the domain flag from closing a door enrolment opened.
|
|
50
|
+
*
|
|
51
|
+
* Exported for the wiring test: `ambientDelivery` is module-private and only runs behind the route's
|
|
52
|
+
* `waitUntil`, so this is the furthest out a test can drive the decision. That the LEG calls it is
|
|
53
|
+
* covered by the `J-ENGAGED-github` e2e journey and by nothing offline. It takes NO injected deps โ
|
|
54
|
+
* the wiring test drives it over the REAL operational store and a REAL declared domain, which is the
|
|
55
|
+
* point of that file.
|
|
56
|
+
*/
|
|
57
|
+
export declare function githubAmbientLegAdmission(addressing: string, surfaceKey: string, f: GitHubSeamFacts): Promise<{
|
|
58
|
+
readonly keep: boolean;
|
|
59
|
+
readonly seam: GitHubEngagementVerdict;
|
|
60
|
+
readonly engagedFollowUp: boolean;
|
|
61
|
+
}>;
|
|
62
|
+
/**
|
|
63
|
+
* Suppress the NATIVE @mention session for a SINGLE-WINDOW VOUCHED repo? On a crossSurfaceWindow repo
|
|
64
|
+
* a @mention answered by the native per-conversation session would reply WITHOUT the shared
|
|
65
|
+
* cross-surface context โ that session is a separate eve run under a per-conversation token and can
|
|
66
|
+
* never join the single window. So we
|
|
67
|
+
* suppress it and let the ambient/unified leg answer the mention as an addressed `direct` wake
|
|
68
|
+
* instead. Gated on `singleWindow && the operator VOUCHED the repo internal` โ the EXACT predicate the
|
|
69
|
+
* ambient classifier's `treatMentionAsDirect`/`vouched` keys on, so native-suppress and ambient-keep are
|
|
70
|
+
* perfectly complementary (never a double reply, never a gap): an un-vouched single-window repo keeps
|
|
71
|
+
* its native mention agent, because the ambient leg still drops mentions there. Pure, so the gate is
|
|
72
|
+
* unit-testable without a live config.
|
|
73
|
+
*
|
|
74
|
+
* A thin GitHub shim over the SHARED seam (unified-window.ts singleWindowMentionPolicy): the
|
|
75
|
+
* caller (onComment) consults it only AFTER githubBotMentioned confirmed the comment IS a
|
|
76
|
+
* mention, so `isMention` is pinned true here; `privacyOk` is the operator-VOUCH floor
|
|
77
|
+
* (isVouchedInternal / OE_TRUST โ resolved by the caller), fail-closed on un-vouched/unknown. The
|
|
78
|
+
* repo's own `repository.private` flag is NOT consulted here โ it is a logged defense-in-depth
|
|
79
|
+
* signal at the call site, not the window gate.
|
|
80
|
+
*/
|
|
81
|
+
export declare function shouldSuppressNativeMention(singleWindow: boolean, vouched: boolean): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* THE EXTERNAL-PARTICIPANT VERDICT for one GitHub comment โ the AUTHOR grain (OPE-533 / OPE-507)
|
|
84
|
+
* and the ROOM grain (OPE-529 / OPE-504) as one pure decision, so both outcomes are tested without
|
|
85
|
+
* a webhook, a store or a live repo.
|
|
86
|
+
*
|
|
87
|
+
* The two values that are not `proceed` name WHY the agent stays quiet. GitHub owes the same act
|
|
88
|
+
* for both โ see the notice note below โ but the audit row must still say which question answered:
|
|
89
|
+
* `withheld-author` โ the comment's author holds no access grant on the repo.
|
|
90
|
+
* `silent-room` โ the room itself is classified external and the operator asked for silence.
|
|
91
|
+
*
|
|
92
|
+
* ๐ด THE AUTHOR GRAIN IS TESTED FIRST, mirroring Discord. An excluded author in a silenced room is
|
|
93
|
+
* an author verdict: the two grains are independent knobs, and a row that named the room would hide
|
|
94
|
+
* the fact that the person, not the place, decided it.
|
|
95
|
+
*
|
|
96
|
+
* THE ROOM LEG NOW READS THE AUTHOR AS EVIDENCE. `ingested` carries the comment's author, so
|
|
97
|
+
* `roomIsExternalClass`'s GUEST-PRESENT leg answers on GitHub the way it already does on Discord.
|
|
98
|
+
* `vendorExternal` is still not passed: GitHub's own room signal is `repository.private`, which is
|
|
99
|
+
* already the window-admission floor read elsewhere (`githubRepoVouched`) โ reusing it here would
|
|
100
|
+
* fold a DIFFERENT question (may this repo join the shared memory window?) into this one (should
|
|
101
|
+
* the agent stay quiet here?).
|
|
102
|
+
*/
|
|
103
|
+
export type GitHubExternalVerdict = "proceed" | "withheld-author" | "silent-room";
|
|
104
|
+
export declare function githubExternalVerdict(input: {
|
|
105
|
+
readonly policy: ExternalPolicy;
|
|
106
|
+
readonly author: IngestedChatMessage;
|
|
107
|
+
readonly surfaceKey: string;
|
|
108
|
+
}): GitHubExternalVerdict;
|
|
109
|
+
/**
|
|
110
|
+
* The dispatch shape OE's GitHub ambient/supervisor legs have always used. It was
|
|
111
|
+
* `SendFn<GitHubChannelState>` until eve 0.31 removed the public `SendFn` (the address moved from
|
|
112
|
+
* the send options onto `from(address)`); declaring it HERE keeps the whole leg below unchanged and
|
|
113
|
+
* confines the version-shaped difference to {@link githubSendFrom}.
|
|
114
|
+
*/
|
|
115
|
+
type GitHubSend = (payload: {
|
|
116
|
+
readonly message: string;
|
|
117
|
+
readonly context?: readonly string[];
|
|
118
|
+
}, options: {
|
|
119
|
+
readonly auth: unknown;
|
|
120
|
+
readonly continuationToken: string;
|
|
121
|
+
readonly state?: GitHubChannelState;
|
|
122
|
+
}) => Promise<unknown>;
|
|
123
|
+
/**
|
|
124
|
+
* Live supervisor deps (mirrors notion.ts liveNotionSupervisorDeps): create-or-fetch / rotate /
|
|
125
|
+
* force-rotate over the channel_session registry with the reserved :oe-sup:g token, and a `wake`
|
|
126
|
+
* that continues the session under that token with the supervisor seed state. Rotation reuses
|
|
127
|
+
* shouldRotate AS-IS; a rotation carries the OUTGOING generation's recorded TAIL forward (OPE-794).
|
|
128
|
+
*
|
|
129
|
+
* UNIFIED re-key (the Slack reference's exact shape, slack-events.ts liveSupervisorDeps): when the
|
|
130
|
+
* repo's domain has opted in, the registry ROW is the synthetic `oe-domain::<name>` key and the
|
|
131
|
+
* token is `oe-dom:<name>:oe-sup:g<gen>` โ one row + one token per generation for the WHOLE team,
|
|
132
|
+
* whichever surface a wake arrives on. Flag-off, `githubGrainFor` returns
|
|
133
|
+
* `{unified:false, registryKey:surfaceKey}` and the per-repo token below โ byte-identical.
|
|
134
|
+
*/
|
|
135
|
+
export declare function liveGitHubSupervisorDeps(send: GitHubSend, ev: GitHubAmbientEvent, to?: RouteHandlerArgs["to"]): GitHubSupervisorDeps;
|
|
136
|
+
export declare const githubOnComment: (ctx: GitHubInboundContext, comment: GitHubComment) => Promise<GitHubInboundResult>;
|
|
137
|
+
declare const channel: GitHubChannel;
|
|
138
|
+
export default channel;
|
|
139
|
+
//# sourceMappingURL=github.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../../agent/channels/github.ts"],"names":[],"mappings":"AAuFA,OAAO,KAAK,EAAuB,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,EAAiB,KAAK,aAAa,EAAE,KAAK,kBAAkB,EAAE,KAAK,oBAAoB,EAAE,KAAK,aAAa,EAAE,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1K,OAAO,EAOL,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACrB,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EAOL,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EAC1B,MAAM,wBAAwB,CAAC;AAMhC,OAAO,EA4CL,KAAK,mBAAmB,EAIxB,KAAK,cAAc,EASpB,MAAM,4CAA4C,CAAC;AAgFpD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,eAAe,EAAE,OAAO,GAAG,OAAO,CAGpH;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,CAAC,EAAE,eAAe,GACjB,OAAO,CAAC;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,uBAAuB,CAAC;IAAC,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;CAAE,CAAC,CAIhH;AAiCD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,2BAA2B,CAAC,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAE5F;AAUD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,iBAAiB,GAAG,aAAa,CAAC;AAElF,wBAAgB,qBAAqB,CAAC,KAAK,EAAE;IAC3C,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B,GAAG,qBAAqB,CAGxB;AA+ED;;;;;GAKG;AACH,KAAK,UAAU,GAAG,CAChB,OAAO,EAAE;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,EAC3E,OAAO,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,kBAAkB,CAAA;CAAE,KACzG,OAAO,CAAC,OAAO,CAAC,CAAC;AActB;;;;;;;;;;;GAWG;AAKH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,kBAAkB,EAAE,EAAE,CAAC,EAAE,gBAAgB,CAAC,IAAI,CAAC,GAAG,oBAAoB,CAkHpI;AAgWD,eAAO,MAAM,eAAe,QAAe,oBAAoB,WAAW,aAAa,KAAG,OAAO,CAAC,mBAAmB,CAwKpH,CAAC;AAoSF,QAAA,MAAM,OAAO,EAAE,aAAqC,CAAC;eAEtC,OAAO"}
|