@holon-run/agentinbox 1.0.0-beta.1 → 1.0.0-beta.2
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/dist/src/service.js +45 -2
- package/dist/src/sources/github.js +124 -8
- package/dist/src/sources/remote.js +1 -1
- package/dist/src/sources/remote_modules.js +162 -157
- package/package.json +1 -1
package/dist/src/service.js
CHANGED
|
@@ -1784,6 +1784,11 @@ class AgentInboxService {
|
|
|
1784
1784
|
buffer.inFlight = true;
|
|
1785
1785
|
const entries = buffer.pending.splice(0, buffer.pending.length);
|
|
1786
1786
|
try {
|
|
1787
|
+
if (!this.activationTargetExists(buffer.agentId, buffer.targetId)) {
|
|
1788
|
+
this.notificationBuffers.delete(key);
|
|
1789
|
+
buffer.inFlight = false;
|
|
1790
|
+
return;
|
|
1791
|
+
}
|
|
1787
1792
|
const state = this.store.getActivationDispatchState(buffer.agentId, buffer.targetId);
|
|
1788
1793
|
const shouldAttemptDispatch = !state || (state.status === "dirty" && state.leaseExpiresAt == null);
|
|
1789
1794
|
if (shouldAttemptDispatch) {
|
|
@@ -1921,7 +1926,11 @@ class AgentInboxService {
|
|
|
1921
1926
|
if (!state) {
|
|
1922
1927
|
return;
|
|
1923
1928
|
}
|
|
1924
|
-
const target = this.
|
|
1929
|
+
const target = this.getActivationTargetIfExists(agentId, targetId);
|
|
1930
|
+
if (!target) {
|
|
1931
|
+
this.store.deleteActivationDispatchState(agentId, targetId);
|
|
1932
|
+
return;
|
|
1933
|
+
}
|
|
1925
1934
|
const inbox = this.ensureInboxForAgent(agentId);
|
|
1926
1935
|
const unacked = this.store.listInboxEntries(inbox.inboxId, { includeAcked: false }).length;
|
|
1927
1936
|
if (unacked === 0) {
|
|
@@ -1988,7 +1997,10 @@ class AgentInboxService {
|
|
|
1988
1997
|
}
|
|
1989
1998
|
}
|
|
1990
1999
|
async dispatchActivationTarget(input) {
|
|
1991
|
-
const target = this.
|
|
2000
|
+
const target = this.getActivationTargetIfExists(input.agentId, input.targetId);
|
|
2001
|
+
if (!target) {
|
|
2002
|
+
return "offline";
|
|
2003
|
+
}
|
|
1992
2004
|
if (target.status === "offline") {
|
|
1993
2005
|
return "offline";
|
|
1994
2006
|
}
|
|
@@ -2029,6 +2041,9 @@ class AgentInboxService {
|
|
|
2029
2041
|
try {
|
|
2030
2042
|
if (target.kind === "terminal") {
|
|
2031
2043
|
const gate = await this.activationGate.evaluate(target);
|
|
2044
|
+
if (!this.activationTargetExists(input.agentId, input.targetId)) {
|
|
2045
|
+
return "offline";
|
|
2046
|
+
}
|
|
2032
2047
|
this.logger.debug("activation.gate_decision", {
|
|
2033
2048
|
agentId: input.agentId,
|
|
2034
2049
|
targetId: target.targetId,
|
|
@@ -2049,6 +2064,9 @@ class AgentInboxService {
|
|
|
2049
2064
|
return "offline";
|
|
2050
2065
|
}
|
|
2051
2066
|
const reminder = await this.buildTerminalReminder(target, inbox.inboxId, totalUnackedCount, input.summary, input.entries);
|
|
2067
|
+
if (!this.activationTargetExists(input.agentId, input.targetId)) {
|
|
2068
|
+
return "offline";
|
|
2069
|
+
}
|
|
2052
2070
|
const prompt = reminder.prompt;
|
|
2053
2071
|
const promptFingerprint = reminder.fingerprint;
|
|
2054
2072
|
terminalPrompt = prompt;
|
|
@@ -2134,6 +2152,9 @@ class AgentInboxService {
|
|
|
2134
2152
|
subscriptionIds: input.subscriptionIds,
|
|
2135
2153
|
});
|
|
2136
2154
|
await this.terminalDispatcher.dispatch(target, prompt);
|
|
2155
|
+
if (!this.activationTargetExists(input.agentId, input.targetId)) {
|
|
2156
|
+
return "offline";
|
|
2157
|
+
}
|
|
2137
2158
|
this.logger.debug("activation.terminal_dispatch_succeeded", {
|
|
2138
2159
|
agentId: input.agentId,
|
|
2139
2160
|
targetId: target.targetId,
|
|
@@ -2162,6 +2183,9 @@ class AgentInboxService {
|
|
|
2162
2183
|
deliveredAt: null,
|
|
2163
2184
|
};
|
|
2164
2185
|
await this.activationDispatcher.dispatch(target.url, activation);
|
|
2186
|
+
if (!this.activationTargetExists(input.agentId, input.targetId)) {
|
|
2187
|
+
return "offline";
|
|
2188
|
+
}
|
|
2165
2189
|
this.store.insertActivation(activation);
|
|
2166
2190
|
}
|
|
2167
2191
|
this.markActivationTargetDelivered(target.targetId);
|
|
@@ -2196,8 +2220,14 @@ class AgentInboxService {
|
|
|
2196
2220
|
error,
|
|
2197
2221
|
message,
|
|
2198
2222
|
});
|
|
2223
|
+
if (!this.activationTargetExists(input.agentId, input.targetId)) {
|
|
2224
|
+
return "offline";
|
|
2225
|
+
}
|
|
2199
2226
|
if (target.kind === "terminal") {
|
|
2200
2227
|
const exists = await this.terminalDispatcher.probe(target);
|
|
2228
|
+
if (!this.activationTargetExists(input.agentId, input.targetId)) {
|
|
2229
|
+
return "offline";
|
|
2230
|
+
}
|
|
2201
2231
|
this.logger.debug("activation.terminal_probe_after_failure", {
|
|
2202
2232
|
agentId: input.agentId,
|
|
2203
2233
|
targetId: target.targetId,
|
|
@@ -2219,6 +2249,19 @@ class AgentInboxService {
|
|
|
2219
2249
|
return "retryable_failure";
|
|
2220
2250
|
}
|
|
2221
2251
|
}
|
|
2252
|
+
getActivationTargetIfExists(agentId, targetId) {
|
|
2253
|
+
if (!this.store.getAgent(agentId)) {
|
|
2254
|
+
return null;
|
|
2255
|
+
}
|
|
2256
|
+
const target = this.store.getActivationTarget(targetId);
|
|
2257
|
+
if (!target || target.agentId !== agentId) {
|
|
2258
|
+
return null;
|
|
2259
|
+
}
|
|
2260
|
+
return target;
|
|
2261
|
+
}
|
|
2262
|
+
activationTargetExists(agentId, targetId) {
|
|
2263
|
+
return this.getActivationTargetIfExists(agentId, targetId) != null;
|
|
2264
|
+
}
|
|
2222
2265
|
upsertDirtyDispatchState(agentId, targetId, entries) {
|
|
2223
2266
|
this.store.upsertActivationDispatchState({
|
|
2224
2267
|
agentId,
|
|
@@ -4,6 +4,7 @@ exports.GithubDeliveryAdapter = exports.GithubUxcClient = exports.DEFAULT_GITHUB
|
|
|
4
4
|
exports.githubDeliveryOperationsForHandle = githubDeliveryOperationsForHandle;
|
|
5
5
|
exports.invokeGithubDeliveryOperation = invokeGithubDeliveryOperation;
|
|
6
6
|
exports.normalizeGithubRepoEvent = normalizeGithubRepoEvent;
|
|
7
|
+
exports.hydrateGithubRepoEventIfNeeded = hydrateGithubRepoEventIfNeeded;
|
|
7
8
|
exports.canonicalGithubPullRequestRef = canonicalGithubPullRequestRef;
|
|
8
9
|
exports.deriveGithubTrackedResource = deriveGithubTrackedResource;
|
|
9
10
|
exports.githubSubscriptionShortcutSpec = githubSubscriptionShortcutSpec;
|
|
@@ -12,6 +13,14 @@ exports.projectGithubLifecycleSignal = projectGithubLifecycleSignal;
|
|
|
12
13
|
exports.parseGithubSourceConfig = parseGithubSourceConfig;
|
|
13
14
|
const uxc_daemon_client_1 = require("@holon-run/uxc-daemon-client");
|
|
14
15
|
exports.GITHUB_ENDPOINT = "https://api.github.com";
|
|
16
|
+
const DEFAULT_GITHUB_CALL_CLIENT = new uxc_daemon_client_1.UxcDaemonClient({ env: process.env });
|
|
17
|
+
const GITHUB_EVENT_HYDRATION_MIN_PER_PAGE = 10;
|
|
18
|
+
const GITHUB_EVENT_HYDRATION_MAX_PER_PAGE = 30;
|
|
19
|
+
const GITHUB_EVENT_HYDRATION_MAX_PAGES = 3;
|
|
20
|
+
const HYDRATABLE_GITHUB_REPO_EVENT_TYPES = new Set([
|
|
21
|
+
"PullRequestReviewEvent",
|
|
22
|
+
"PullRequestReviewCommentEvent",
|
|
23
|
+
]);
|
|
15
24
|
exports.DEFAULT_GITHUB_EVENT_TYPES = [
|
|
16
25
|
"IssuesEvent",
|
|
17
26
|
"IssueCommentEvent",
|
|
@@ -21,7 +30,7 @@ exports.DEFAULT_GITHUB_EVENT_TYPES = [
|
|
|
21
30
|
];
|
|
22
31
|
class GithubUxcClient {
|
|
23
32
|
client;
|
|
24
|
-
constructor(client =
|
|
33
|
+
constructor(client = DEFAULT_GITHUB_CALL_CLIENT) {
|
|
25
34
|
this.client = client;
|
|
26
35
|
}
|
|
27
36
|
async createIssueComment(input) {
|
|
@@ -222,7 +231,7 @@ function normalizeGithubRepoEvent(source, config, raw) {
|
|
|
222
231
|
if (!eventType || !(config.eventTypes ?? exports.DEFAULT_GITHUB_EVENT_TYPES).includes(eventType)) {
|
|
223
232
|
return null;
|
|
224
233
|
}
|
|
225
|
-
const eventId =
|
|
234
|
+
const eventId = asIdString(event.id);
|
|
226
235
|
if (!eventId) {
|
|
227
236
|
return null;
|
|
228
237
|
}
|
|
@@ -234,18 +243,17 @@ function normalizeGithubRepoEvent(source, config, raw) {
|
|
|
234
243
|
const review = asRecord(payload.review);
|
|
235
244
|
const comment = asRecord(payload.comment);
|
|
236
245
|
const action = asString(payload.action) ?? "observed";
|
|
237
|
-
const
|
|
246
|
+
const url = extractGithubRepoEventUrl(event, issue, pullRequest, review, comment);
|
|
247
|
+
const number = asNumber(issue.number)
|
|
248
|
+
?? asNumber(pullRequest.number)
|
|
249
|
+
?? asNumber(payload.number)
|
|
250
|
+
?? extractGithubNumberFromUrl(url);
|
|
238
251
|
const isPullRequest = eventType.startsWith("PullRequest");
|
|
239
252
|
const title = asString(issue.title) ?? asString(pullRequest.title) ?? null;
|
|
240
253
|
const reviewState = asString(review.state) ?? null;
|
|
241
254
|
const body = asString(comment.body) ?? asString(review.body) ?? asString(issue.body) ?? asString(pullRequest.body) ?? null;
|
|
242
255
|
const labels = extractLabels(issue.labels);
|
|
243
256
|
const mentions = extractMentions(title, body);
|
|
244
|
-
const url = asString(comment.html_url) ??
|
|
245
|
-
asString(review.html_url) ??
|
|
246
|
-
asString(issue.html_url) ??
|
|
247
|
-
asString(pullRequest.html_url) ??
|
|
248
|
-
asString(event.url);
|
|
249
257
|
const deliveryHandle = buildGithubDeliveryHandle(config, eventType, number, comment);
|
|
250
258
|
return {
|
|
251
259
|
sourceId: source.sourceId,
|
|
@@ -283,6 +291,47 @@ function normalizeGithubRepoEvent(source, config, raw) {
|
|
|
283
291
|
deliveryHandle,
|
|
284
292
|
};
|
|
285
293
|
}
|
|
294
|
+
async function hydrateGithubRepoEventIfNeeded(config, raw, client = DEFAULT_GITHUB_CALL_CLIENT) {
|
|
295
|
+
if (!shouldHydrateGithubRepoEvent(raw)) {
|
|
296
|
+
return raw;
|
|
297
|
+
}
|
|
298
|
+
const eventId = asIdString(raw.id);
|
|
299
|
+
if (!eventId) {
|
|
300
|
+
return raw;
|
|
301
|
+
}
|
|
302
|
+
const perPage = computeGithubHydrationPerPage(config.perPage);
|
|
303
|
+
for (let page = 1; page <= GITHUB_EVENT_HYDRATION_MAX_PAGES; page += 1) {
|
|
304
|
+
let response;
|
|
305
|
+
try {
|
|
306
|
+
response = await client.call({
|
|
307
|
+
endpoint: exports.GITHUB_ENDPOINT,
|
|
308
|
+
operation: "get:/repos/{owner}/{repo}/events",
|
|
309
|
+
payload: {
|
|
310
|
+
owner: config.owner,
|
|
311
|
+
repo: config.repo,
|
|
312
|
+
per_page: perPage,
|
|
313
|
+
page,
|
|
314
|
+
},
|
|
315
|
+
options: { auth: config.uxcAuth },
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
catch {
|
|
319
|
+
return raw;
|
|
320
|
+
}
|
|
321
|
+
const candidates = Array.isArray(response.data) ? response.data : [];
|
|
322
|
+
if (candidates.length === 0) {
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
for (const candidateValue of candidates) {
|
|
326
|
+
const candidate = asRecord(candidateValue);
|
|
327
|
+
if (asIdString(candidate.id) !== eventId) {
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
return hasGithubRepoEventMetadata(candidate) ? candidate : raw;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return raw;
|
|
334
|
+
}
|
|
286
335
|
function canonicalGithubPullRequestRef(source, number) {
|
|
287
336
|
const config = parseGithubSourceConfig(source);
|
|
288
337
|
return `repo:${config.owner}/${config.repo}:pr:${number}`;
|
|
@@ -433,6 +482,64 @@ function extractMentions(...values) {
|
|
|
433
482
|
}
|
|
434
483
|
return Array.from(seen.values()).sort();
|
|
435
484
|
}
|
|
485
|
+
function computeGithubHydrationPerPage(perPage) {
|
|
486
|
+
const configured = Number.isInteger(perPage) && perPage && perPage > 0
|
|
487
|
+
? perPage
|
|
488
|
+
: GITHUB_EVENT_HYDRATION_MIN_PER_PAGE;
|
|
489
|
+
return Math.min(GITHUB_EVENT_HYDRATION_MAX_PER_PAGE, Math.max(GITHUB_EVENT_HYDRATION_MIN_PER_PAGE, configured));
|
|
490
|
+
}
|
|
491
|
+
function extractGithubRepoEventUrl(event, issue, pullRequest, review, comment) {
|
|
492
|
+
return (asString(comment.html_url)
|
|
493
|
+
?? asString(review.html_url)
|
|
494
|
+
?? asString(issue.html_url)
|
|
495
|
+
?? asString(pullRequest.html_url)
|
|
496
|
+
?? asString(comment.pull_request_url)
|
|
497
|
+
?? asString(review.pull_request_url)
|
|
498
|
+
?? asString(event.url));
|
|
499
|
+
}
|
|
500
|
+
function extractGithubNumberFromUrl(url) {
|
|
501
|
+
if (!url) {
|
|
502
|
+
return null;
|
|
503
|
+
}
|
|
504
|
+
const match = url.match(/\/(?:pull|pulls|issues)\/(\d+)(?:$|[/?#])/);
|
|
505
|
+
return match ? Number(match[1]) : null;
|
|
506
|
+
}
|
|
507
|
+
function shouldHydrateGithubRepoEvent(raw) {
|
|
508
|
+
const eventType = asString(raw.type);
|
|
509
|
+
if (!eventType || !HYDRATABLE_GITHUB_REPO_EVENT_TYPES.has(eventType)) {
|
|
510
|
+
return false;
|
|
511
|
+
}
|
|
512
|
+
const payload = asRecord(raw.payload);
|
|
513
|
+
const issue = asRecord(payload.issue);
|
|
514
|
+
const pullRequest = asRecord(payload.pull_request);
|
|
515
|
+
const review = asRecord(payload.review);
|
|
516
|
+
const comment = asRecord(payload.comment);
|
|
517
|
+
const url = extractGithubRepoEventUrl(raw, issue, pullRequest, review, comment);
|
|
518
|
+
const number = asNumber(issue.number)
|
|
519
|
+
?? asNumber(pullRequest.number)
|
|
520
|
+
?? asNumber(payload.number)
|
|
521
|
+
?? extractGithubNumberFromUrl(url);
|
|
522
|
+
if (number) {
|
|
523
|
+
return false;
|
|
524
|
+
}
|
|
525
|
+
return [pullRequest, review, comment].some(isUxcPreviewTruncatedObject);
|
|
526
|
+
}
|
|
527
|
+
function hasGithubRepoEventMetadata(raw) {
|
|
528
|
+
const payload = asRecord(raw.payload);
|
|
529
|
+
const issue = asRecord(payload.issue);
|
|
530
|
+
const pullRequest = asRecord(payload.pull_request);
|
|
531
|
+
const review = asRecord(payload.review);
|
|
532
|
+
const comment = asRecord(payload.comment);
|
|
533
|
+
return Boolean(asNumber(issue.number)
|
|
534
|
+
?? asNumber(pullRequest.number)
|
|
535
|
+
?? asNumber(payload.number)
|
|
536
|
+
?? extractGithubNumberFromUrl(extractGithubRepoEventUrl(raw, issue, pullRequest, review, comment))
|
|
537
|
+
?? asString(review.state)
|
|
538
|
+
?? extractGithubRepoEventUrl(raw, issue, pullRequest, review, comment));
|
|
539
|
+
}
|
|
540
|
+
function isUxcPreviewTruncatedObject(value) {
|
|
541
|
+
return value._uxc_preview_truncated === true;
|
|
542
|
+
}
|
|
436
543
|
function parseTargetRef(targetRef) {
|
|
437
544
|
const match = targetRef.match(/^([^/]+)\/([^#]+)#(\d+)$/);
|
|
438
545
|
if (!match) {
|
|
@@ -495,6 +602,15 @@ function asRecord(value) {
|
|
|
495
602
|
function asString(value) {
|
|
496
603
|
return typeof value === "string" ? value : null;
|
|
497
604
|
}
|
|
605
|
+
function asIdString(value) {
|
|
606
|
+
if (typeof value === "string") {
|
|
607
|
+
return value;
|
|
608
|
+
}
|
|
609
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
610
|
+
return String(value);
|
|
611
|
+
}
|
|
612
|
+
return null;
|
|
613
|
+
}
|
|
498
614
|
function asNumber(value) {
|
|
499
615
|
return typeof value === "number" ? value : null;
|
|
500
616
|
}
|
|
@@ -300,7 +300,7 @@ class RemoteSourceRuntime {
|
|
|
300
300
|
if (Object.keys(rawPayload).length === 0) {
|
|
301
301
|
continue;
|
|
302
302
|
}
|
|
303
|
-
const mapped = module.mapRawEvent(rawPayload, moduleSource);
|
|
303
|
+
const mapped = await module.mapRawEvent(rawPayload, moduleSource);
|
|
304
304
|
if (!mapped) {
|
|
305
305
|
continue;
|
|
306
306
|
}
|
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.RemoteSourceModuleRegistry = void 0;
|
|
7
7
|
exports.builtInModuleIdForSourceType = builtInModuleIdForSourceType;
|
|
8
|
+
exports.createGithubRepoRemoteModule = createGithubRepoRemoteModule;
|
|
8
9
|
exports.moduleConfigForSource = moduleConfigForSource;
|
|
9
10
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
10
11
|
const node_path_1 = __importDefault(require("node:path"));
|
|
@@ -133,169 +134,173 @@ function asNonEmptyString(value) {
|
|
|
133
134
|
const trimmed = value.trim();
|
|
134
135
|
return trimmed.length > 0 ? trimmed : null;
|
|
135
136
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
sourceKind: "github_repo",
|
|
148
|
-
aliases: ["github_repo"],
|
|
149
|
-
configSchema: [
|
|
150
|
-
{ name: "owner", type: "string", required: true, description: "GitHub repository owner." },
|
|
151
|
-
{ name: "repo", type: "string", required: true, description: "GitHub repository name." },
|
|
152
|
-
{ name: "uxcAuth", type: "string", required: false, description: "Optional uxc auth profile." },
|
|
153
|
-
{ name: "eventTypes", type: "string[]", required: false, description: "Optional GitHub event type allowlist." },
|
|
154
|
-
{ name: "perPage", type: "number", required: false, description: `Repository events requested per poll. Default ${config.perPage ?? 10}.` },
|
|
155
|
-
{ name: "pollIntervalSecs", type: "number", required: false, description: `Polling interval in seconds. Default ${config.pollIntervalSecs ?? 30}.` },
|
|
156
|
-
],
|
|
157
|
-
metadataFields: [
|
|
158
|
-
{ name: "eventType", type: "string", description: "GitHub event type such as IssueCommentEvent." },
|
|
159
|
-
{ name: "action", type: "string", description: "GitHub event action suffix such as created." },
|
|
160
|
-
{ name: "author", type: "string|null", description: "Actor login for the event." },
|
|
161
|
-
{ name: "isPullRequest", type: "boolean", description: "Whether the event targets a pull request surface." },
|
|
162
|
-
{ name: "reviewState", type: "string|null", description: "Review decision state for PullRequestReviewEvent such as approved or changes_requested." },
|
|
163
|
-
{ name: "labels", type: "string[]", description: "Labels extracted from the issue or pull request." },
|
|
164
|
-
{ name: "mentions", type: "string[]", description: "Mention handles extracted from title/body/comment text." },
|
|
165
|
-
{ name: "number", type: "number|null", description: "Issue or pull request number when present." },
|
|
166
|
-
{ name: "repoFullName", type: "string", description: "Repository full name in owner/repo form." },
|
|
167
|
-
{ name: "title", type: "string|null", description: "Issue, pull request, or comment title." },
|
|
168
|
-
{ name: "body", type: "string|null", description: "Issue, pull request, or comment body text." },
|
|
169
|
-
{ name: "url", type: "string|null", description: "Primary GitHub HTML URL for the event target." },
|
|
170
|
-
],
|
|
171
|
-
payloadExamples: [
|
|
172
|
-
{
|
|
173
|
-
id: "1234567891",
|
|
174
|
-
type: "PullRequestReviewEvent",
|
|
175
|
-
action: "created",
|
|
176
|
-
actor: "Copilot",
|
|
177
|
-
pull_request: { number: 67, title: "feat: add remote module capability hooks" },
|
|
178
|
-
review: { state: "commented", body: "review summary" },
|
|
179
|
-
},
|
|
180
|
-
{
|
|
181
|
-
id: "1234567892",
|
|
182
|
-
type: "PullRequestEvent",
|
|
183
|
-
action: "closed",
|
|
184
|
-
actor: "jolestar",
|
|
185
|
-
pull_request: { number: 72, title: "feat: add cleanup policy lifecycle engine", merged: true },
|
|
186
|
-
},
|
|
187
|
-
],
|
|
188
|
-
eventVariantExamples: [
|
|
189
|
-
"IssueCommentEvent.created",
|
|
190
|
-
"PullRequestEvent.opened",
|
|
191
|
-
"PullRequestEvent.closed",
|
|
192
|
-
"PullRequestReviewEvent.created",
|
|
193
|
-
"PullRequestReviewCommentEvent.created",
|
|
194
|
-
],
|
|
195
|
-
};
|
|
196
|
-
},
|
|
197
|
-
listSubscriptionShortcuts() {
|
|
198
|
-
return (0, github_1.githubSubscriptionShortcutSpec)();
|
|
199
|
-
},
|
|
200
|
-
expandSubscriptionShortcut(input) {
|
|
201
|
-
return (0, github_1.expandGithubSubscriptionShortcut)(input);
|
|
202
|
-
},
|
|
203
|
-
deriveTrackedResource(filter, source) {
|
|
204
|
-
return (0, github_1.deriveGithubTrackedResource)(filter, source);
|
|
205
|
-
},
|
|
206
|
-
deriveNotificationGrouping(item) {
|
|
207
|
-
const number = typeof item.metadata.number === "number" ? item.metadata.number : null;
|
|
208
|
-
if (!number) {
|
|
209
|
-
return null;
|
|
210
|
-
}
|
|
211
|
-
const variant = item.eventVariant;
|
|
212
|
-
if (variant.startsWith("PullRequestReviewCommentEvent.")) {
|
|
213
|
-
return {
|
|
214
|
-
groupable: true,
|
|
215
|
-
resourceRef: `pr:${number}`,
|
|
216
|
-
eventFamily: "review_comments",
|
|
217
|
-
summaryHint: `review comments on PR #${number}`,
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
if (variant.startsWith("PullRequestReviewEvent.")) {
|
|
137
|
+
function createGithubRepoRemoteModule(options) {
|
|
138
|
+
return {
|
|
139
|
+
id: "builtin.github_repo",
|
|
140
|
+
listDeliveryOperations(input) {
|
|
141
|
+
return (0, github_1.githubDeliveryOperationsForHandle)(input.handle);
|
|
142
|
+
},
|
|
143
|
+
async invokeDeliveryOperation(input) {
|
|
144
|
+
return (0, github_1.invokeGithubDeliveryOperation)(input.handle, input.operation, input.input);
|
|
145
|
+
},
|
|
146
|
+
describeCapabilities(source) {
|
|
147
|
+
const config = (0, github_1.parseGithubSourceConfig)(source);
|
|
221
148
|
return {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
149
|
+
sourceKind: "github_repo",
|
|
150
|
+
aliases: ["github_repo"],
|
|
151
|
+
configSchema: [
|
|
152
|
+
{ name: "owner", type: "string", required: true, description: "GitHub repository owner." },
|
|
153
|
+
{ name: "repo", type: "string", required: true, description: "GitHub repository name." },
|
|
154
|
+
{ name: "uxcAuth", type: "string", required: false, description: "Optional uxc auth profile." },
|
|
155
|
+
{ name: "eventTypes", type: "string[]", required: false, description: "Optional GitHub event type allowlist." },
|
|
156
|
+
{ name: "perPage", type: "number", required: false, description: `Repository events requested per poll. Default ${config.perPage ?? 10}.` },
|
|
157
|
+
{ name: "pollIntervalSecs", type: "number", required: false, description: `Polling interval in seconds. Default ${config.pollIntervalSecs ?? 30}.` },
|
|
158
|
+
],
|
|
159
|
+
metadataFields: [
|
|
160
|
+
{ name: "eventType", type: "string", description: "GitHub event type such as IssueCommentEvent." },
|
|
161
|
+
{ name: "action", type: "string", description: "GitHub event action suffix such as created." },
|
|
162
|
+
{ name: "author", type: "string|null", description: "Actor login for the event." },
|
|
163
|
+
{ name: "isPullRequest", type: "boolean", description: "Whether the event targets a pull request surface." },
|
|
164
|
+
{ name: "reviewState", type: "string|null", description: "Review decision state for PullRequestReviewEvent such as approved or changes_requested." },
|
|
165
|
+
{ name: "labels", type: "string[]", description: "Labels extracted from the issue or pull request." },
|
|
166
|
+
{ name: "mentions", type: "string[]", description: "Mention handles extracted from title/body/comment text." },
|
|
167
|
+
{ name: "number", type: "number|null", description: "Issue or pull request number when present." },
|
|
168
|
+
{ name: "repoFullName", type: "string", description: "Repository full name in owner/repo form." },
|
|
169
|
+
{ name: "title", type: "string|null", description: "Issue, pull request, or comment title." },
|
|
170
|
+
{ name: "body", type: "string|null", description: "Issue, pull request, or comment body text." },
|
|
171
|
+
{ name: "url", type: "string|null", description: "Primary GitHub HTML URL for the event target." },
|
|
172
|
+
],
|
|
173
|
+
payloadExamples: [
|
|
174
|
+
{
|
|
175
|
+
id: "1234567891",
|
|
176
|
+
type: "PullRequestReviewEvent",
|
|
177
|
+
action: "created",
|
|
178
|
+
actor: "Copilot",
|
|
179
|
+
pull_request: { number: 67, title: "feat: add remote module capability hooks" },
|
|
180
|
+
review: { state: "commented", body: "review summary" },
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
id: "1234567892",
|
|
184
|
+
type: "PullRequestEvent",
|
|
185
|
+
action: "closed",
|
|
186
|
+
actor: "jolestar",
|
|
187
|
+
pull_request: { number: 72, title: "feat: add cleanup policy lifecycle engine", merged: true },
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
eventVariantExamples: [
|
|
191
|
+
"IssueCommentEvent.created",
|
|
192
|
+
"PullRequestEvent.opened",
|
|
193
|
+
"PullRequestEvent.closed",
|
|
194
|
+
"PullRequestReviewEvent.created",
|
|
195
|
+
"PullRequestReviewCommentEvent.created",
|
|
196
|
+
],
|
|
226
197
|
};
|
|
227
|
-
}
|
|
228
|
-
|
|
198
|
+
},
|
|
199
|
+
listSubscriptionShortcuts() {
|
|
200
|
+
return (0, github_1.githubSubscriptionShortcutSpec)();
|
|
201
|
+
},
|
|
202
|
+
expandSubscriptionShortcut(input) {
|
|
203
|
+
return (0, github_1.expandGithubSubscriptionShortcut)(input);
|
|
204
|
+
},
|
|
205
|
+
deriveTrackedResource(filter, source) {
|
|
206
|
+
return (0, github_1.deriveGithubTrackedResource)(filter, source);
|
|
207
|
+
},
|
|
208
|
+
deriveNotificationGrouping(item) {
|
|
209
|
+
const number = typeof item.metadata.number === "number" ? item.metadata.number : null;
|
|
210
|
+
if (!number) {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
const variant = item.eventVariant;
|
|
214
|
+
if (variant.startsWith("PullRequestReviewCommentEvent.")) {
|
|
215
|
+
return {
|
|
216
|
+
groupable: true,
|
|
217
|
+
resourceRef: `pr:${number}`,
|
|
218
|
+
eventFamily: "review_comments",
|
|
219
|
+
summaryHint: `review comments on PR #${number}`,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
if (variant.startsWith("PullRequestReviewEvent.")) {
|
|
223
|
+
return {
|
|
224
|
+
groupable: true,
|
|
225
|
+
resourceRef: `pr:${number}`,
|
|
226
|
+
eventFamily: "reviews",
|
|
227
|
+
summaryHint: `reviews on PR #${number}`,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
if (variant.startsWith("IssueCommentEvent.")) {
|
|
231
|
+
return {
|
|
232
|
+
groupable: true,
|
|
233
|
+
resourceRef: item.metadata.isPullRequest === true ? `pr:${number}` : `issue:${number}`,
|
|
234
|
+
eventFamily: "comments",
|
|
235
|
+
summaryHint: item.metadata.isPullRequest === true ? `comments on PR #${number}` : `comments on issue #${number}`,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
if (variant.startsWith("PullRequestEvent.closed")) {
|
|
239
|
+
return {
|
|
240
|
+
groupable: true,
|
|
241
|
+
resourceRef: `pr:${number}`,
|
|
242
|
+
eventFamily: "lifecycle",
|
|
243
|
+
summaryHint: `PR #${number} lifecycle updates`,
|
|
244
|
+
flushClass: "immediate",
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
return null;
|
|
248
|
+
},
|
|
249
|
+
summarizeDigestThread(items, _source, grouping) {
|
|
250
|
+
if (!grouping.summaryHint || items.length === 0) {
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
const count = items.length;
|
|
254
|
+
return `${count} ${grouping.summaryHint}`;
|
|
255
|
+
},
|
|
256
|
+
projectLifecycleSignal(rawPayload, source) {
|
|
257
|
+
return (0, github_1.projectGithubLifecycleSignal)(rawPayload, source);
|
|
258
|
+
},
|
|
259
|
+
validateConfig(source) {
|
|
260
|
+
(0, github_1.parseGithubSourceConfig)(source);
|
|
261
|
+
},
|
|
262
|
+
buildManagedSourceSpec(source) {
|
|
263
|
+
const config = (0, github_1.parseGithubSourceConfig)(source);
|
|
229
264
|
return {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
265
|
+
endpoint: github_1.GITHUB_ENDPOINT,
|
|
266
|
+
operation_id: "get:/repos/{owner}/{repo}/events",
|
|
267
|
+
args: {
|
|
268
|
+
owner: config.owner,
|
|
269
|
+
repo: config.repo,
|
|
270
|
+
per_page: config.perPage ?? 10,
|
|
271
|
+
},
|
|
272
|
+
mode: "poll",
|
|
273
|
+
poll_config: {
|
|
274
|
+
interval_secs: config.pollIntervalSecs ?? 30,
|
|
275
|
+
extract_items_pointer: "",
|
|
276
|
+
checkpoint_strategy: {
|
|
277
|
+
type: "item_key",
|
|
278
|
+
item_key_pointer: "/id",
|
|
279
|
+
seen_window: 1024,
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
options: { auth: config.uxcAuth },
|
|
234
283
|
};
|
|
235
|
-
}
|
|
236
|
-
|
|
284
|
+
},
|
|
285
|
+
async mapRawEvent(rawPayload, source) {
|
|
286
|
+
const config = (0, github_1.parseGithubSourceConfig)(source);
|
|
287
|
+
const hydratedPayload = await (0, github_1.hydrateGithubRepoEventIfNeeded)(config, rawPayload, options?.callClient);
|
|
288
|
+
const normalized = (0, github_1.normalizeGithubRepoEvent)(source, config, hydratedPayload);
|
|
289
|
+
if (!normalized) {
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
237
292
|
return {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
293
|
+
sourceNativeId: normalized.sourceNativeId,
|
|
294
|
+
eventVariant: normalized.eventVariant,
|
|
295
|
+
metadata: normalized.metadata ?? {},
|
|
296
|
+
rawPayload: normalized.rawPayload ?? hydratedPayload,
|
|
297
|
+
occurredAt: normalized.occurredAt,
|
|
298
|
+
deliveryHandle: normalized.deliveryHandle,
|
|
243
299
|
};
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (!grouping.summaryHint || items.length === 0) {
|
|
249
|
-
return null;
|
|
250
|
-
}
|
|
251
|
-
const count = items.length;
|
|
252
|
-
return `${count} ${grouping.summaryHint}`;
|
|
253
|
-
},
|
|
254
|
-
projectLifecycleSignal(rawPayload, source) {
|
|
255
|
-
return (0, github_1.projectGithubLifecycleSignal)(rawPayload, source);
|
|
256
|
-
},
|
|
257
|
-
validateConfig(source) {
|
|
258
|
-
(0, github_1.parseGithubSourceConfig)(source);
|
|
259
|
-
},
|
|
260
|
-
buildManagedSourceSpec(source) {
|
|
261
|
-
const config = (0, github_1.parseGithubSourceConfig)(source);
|
|
262
|
-
return {
|
|
263
|
-
endpoint: github_1.GITHUB_ENDPOINT,
|
|
264
|
-
operation_id: "get:/repos/{owner}/{repo}/events",
|
|
265
|
-
args: {
|
|
266
|
-
owner: config.owner,
|
|
267
|
-
repo: config.repo,
|
|
268
|
-
per_page: config.perPage ?? 10,
|
|
269
|
-
},
|
|
270
|
-
mode: "poll",
|
|
271
|
-
poll_config: {
|
|
272
|
-
interval_secs: config.pollIntervalSecs ?? 30,
|
|
273
|
-
extract_items_pointer: "",
|
|
274
|
-
checkpoint_strategy: {
|
|
275
|
-
type: "item_key",
|
|
276
|
-
item_key_pointer: "/id",
|
|
277
|
-
seen_window: 1024,
|
|
278
|
-
},
|
|
279
|
-
},
|
|
280
|
-
options: { auth: config.uxcAuth },
|
|
281
|
-
};
|
|
282
|
-
},
|
|
283
|
-
mapRawEvent(rawPayload, source) {
|
|
284
|
-
const config = (0, github_1.parseGithubSourceConfig)(source);
|
|
285
|
-
const normalized = (0, github_1.normalizeGithubRepoEvent)(source, config, rawPayload);
|
|
286
|
-
if (!normalized) {
|
|
287
|
-
return null;
|
|
288
|
-
}
|
|
289
|
-
return {
|
|
290
|
-
sourceNativeId: normalized.sourceNativeId,
|
|
291
|
-
eventVariant: normalized.eventVariant,
|
|
292
|
-
metadata: normalized.metadata ?? {},
|
|
293
|
-
rawPayload: normalized.rawPayload ?? rawPayload,
|
|
294
|
-
occurredAt: normalized.occurredAt,
|
|
295
|
-
deliveryHandle: normalized.deliveryHandle,
|
|
296
|
-
};
|
|
297
|
-
},
|
|
298
|
-
};
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
const GITHUB_REPO_MODULE = createGithubRepoRemoteModule();
|
|
299
304
|
const GITHUB_REPO_CI_MODULE = {
|
|
300
305
|
id: "builtin.github_repo_ci",
|
|
301
306
|
describeCapabilities() {
|