@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.
@@ -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.getActivationTarget(targetId);
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.getActivationTarget(input.targetId);
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 = new uxc_daemon_client_1.UxcDaemonClient({ env: process.env })) {
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 = asString(event.id);
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 number = asNumber(issue.number) ?? asNumber(pullRequest.number);
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
- const GITHUB_REPO_MODULE = {
137
- id: "builtin.github_repo",
138
- listDeliveryOperations(input) {
139
- return (0, github_1.githubDeliveryOperationsForHandle)(input.handle);
140
- },
141
- async invokeDeliveryOperation(input) {
142
- return (0, github_1.invokeGithubDeliveryOperation)(input.handle, input.operation, input.input);
143
- },
144
- describeCapabilities(source) {
145
- const config = (0, github_1.parseGithubSourceConfig)(source);
146
- return {
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
- groupable: true,
223
- resourceRef: `pr:${number}`,
224
- eventFamily: "reviews",
225
- summaryHint: `reviews on PR #${number}`,
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
- if (variant.startsWith("IssueCommentEvent.")) {
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
- groupable: true,
231
- resourceRef: item.metadata.isPullRequest === true ? `pr:${number}` : `issue:${number}`,
232
- eventFamily: "comments",
233
- summaryHint: item.metadata.isPullRequest === true ? `comments on PR #${number}` : `comments on issue #${number}`,
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
- if (variant.startsWith("PullRequestEvent.closed")) {
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
- groupable: true,
239
- resourceRef: `pr:${number}`,
240
- eventFamily: "lifecycle",
241
- summaryHint: `PR #${number} lifecycle updates`,
242
- flushClass: "immediate",
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
- return null;
246
- },
247
- summarizeDigestThread(items, _source, grouping) {
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() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@holon-run/agentinbox",
3
- "version": "1.0.0-beta.1",
3
+ "version": "1.0.0-beta.2",
4
4
  "description": "Local event subscription and delivery service for agents.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {