agents-relay 1.0.138 → 1.0.140

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.
@@ -373,7 +373,7 @@ function formatCredit(value,currency){if(value===null||value===undefined||!Numbe
373
373
  function creditProvider(provider){const row=element('section','usage-provider');row.append(element('h3','',text(provider.label)));const credits=provider.credits||{};if(!credits.available||!Array.isArray(credits.balances)||!credits.balances.length){row.append(element('p','empty',text(credits.reason||'Credit balance is unavailable.')));return row;}const balance=credits.balances[0];row.append(element('div','detail-value',text(formatCredit(balance.remaining,balance.currency))));return row;}
374
374
  function githubBucket(label,bucket){const row=element('div','usage-row');const heading=element('div','usage-label');heading.append(element('span','',text(label)),element('span','',text(bucket.remaining+' remaining')));row.append(heading);row.append(element('div','subtle',text(bucket.used+' used / '+bucket.limit+' limit')));const reset=resetText({resetsAt:bucket.reset});if(reset)row.append(element('div','subtle',text(reset)));return row;}
375
375
  function githubCounterLine(label,values){const entries=Object.entries(values||{}).sort((a,b)=>Number(b[1])-Number(a[1])).slice(0,8);return entries.length?element('div','subtle',text(label+' · '+entries.map(([key,count])=>key+' '+count).join(' · '))):null;}
376
- function githubUsageCard(snapshot,status){const card=element('section','usage-provider');card.append(element('h3','',text('GitHub API')));const resources=snapshot?.resources||{};const bars=element('div','usage-bars');if(resources.core)bars.append(githubBucket('REST core',resources.core));if(resources.graphql)bars.append(githubBucket('GraphQL',resources.graphql));if(resources.search)bars.append(githubBucket('Search',resources.search));if(!bars.children.length)bars.append(element('p','empty',text('GitHub quota telemetry is unavailable.')));if(status?.limited)bars.append(element('div','error-state',text('Relay cooldown active'+(status.cooldownUntil?' until '+new Date(status.cooldownUntil).toLocaleString():''))));else bars.append(element('div','subtle',text('Relay cooldown inactive')));if(status?.calls)bars.append(element('div','subtle',text('Calls since start · GraphQL '+(status.calls.graphql||0)+' · REST '+(status.calls.rest||0)+' · quota '+(status.calls.rateLimit||0))));const operations=githubCounterLine('GraphQL operations',status?.graphql?.operations);if(operations)bars.append(operations);const contexts=githubCounterLine('GraphQL contexts',status?.graphql?.contexts);if(contexts)bars.append(contexts);if(status?.readPaths)bars.append(element('div','subtle',text('Read paths · workspace scans '+(status.readPaths.workspaceScans||0)+' · PR refreshes '+(status.readPaths.prRefreshes||0))));card.append(bars);return card;}
376
+ function githubUsageCard(snapshot,status){const card=element('section','usage-provider');card.append(element('h3','',text('GitHub API')));const resources=snapshot?.resources||{};const bars=element('div','usage-bars');if(resources.core)bars.append(githubBucket('REST core',resources.core));if(resources.graphql)bars.append(githubBucket('GraphQL',resources.graphql));if(resources.search)bars.append(githubBucket('Search',resources.search));if(!bars.children.length)bars.append(element('p','empty',text('GitHub quota telemetry is unavailable.')));if(status?.limited)bars.append(element('div','error-state',text('Relay cooldown active'+(status.cooldownUntil?' until '+new Date(status.cooldownUntil).toLocaleString():''))));else bars.append(element('div','subtle',text('Relay cooldown inactive')));if(status?.calls)bars.append(element('div','subtle',text('Calls since start · GraphQL '+(status.calls.graphql||0)+' · REST '+(status.calls.rest||0)+' · quota '+(status.calls.rateLimit||0))));const operations=githubCounterLine('GraphQL operations',status?.graphql?.operations);if(operations)bars.append(operations);const contexts=githubCounterLine('GraphQL contexts',status?.graphql?.contexts);if(contexts)bars.append(contexts);if(status?.readPaths){bars.append(element('div','subtle',text('Read paths · workspace scans '+(status.readPaths.workspaceScans||0)+' · PR refreshes '+(status.readPaths.prRefreshes||0)+' · coalesced '+(status.readPaths.prRefreshCoalesced||0))));const triggers=githubCounterLine('PR refresh triggers',status.readPaths.prRefreshesByTrigger);if(triggers)bars.append(triggers);const coalesced=githubCounterLine('Coalesced refresh triggers',status.readPaths.prRefreshCoalescedByTrigger);if(coalesced)bars.append(coalesced);}card.append(bars);return card;}
377
377
  async function drawUsage(){try{const overview=await loadJSON('/api/usage');usageRoot.replaceChildren();usageMeta.textContent='Real account telemetry when available';const providers=Array.isArray(overview.providers)?overview.providers:[];const credits=Array.isArray(overview.credits)?overview.credits:[];const grid=element('div','usage-providers');if(overview.github||overview.githubStatus)grid.append(githubUsageCard(overview.github,overview.githubStatus));for(const provider of providers)grid.append(usageProvider(provider));for(const provider of credits)grid.append(creditProvider(provider));if(!grid.children.length){usageRoot.append(element('p','empty',text('Usage telemetry is unavailable.')));return;}usageRoot.append(grid);}catch(error){usageMeta.textContent='Unavailable';usageRoot.replaceChildren(element('p','error-state',text('Usage unavailable: '+error.message)));}}
378
378
  async function drawRules(){rulesStatus.textContent='';try{const rules=await loadJSON('/api/model-router-rules');rulesText.value=typeof rules.text==='string'?rules.text:'';rulesMeta.textContent=rules.hash&&rules.hash!=='none'?'Loaded · '+rules.hash.slice(0,12):'No custom rules saved';}catch(error){rulesMeta.textContent='Unavailable';rulesStatus.textContent='Could not load rules: '+error.message;}}
379
379
  async function saveRules(){rulesSave.disabled=true;rulesStatus.textContent='Saving…';try{const response=await fetch('/api/model-router-rules',{method:'PUT',headers:{'content-type':'text/plain; charset=utf-8'},body:rulesText.value});if(!response.ok)throw new Error(response.status+' '+await response.text());const rules=await response.json();rulesMeta.textContent='Saved · '+rules.hash.slice(0,12);rulesStatus.textContent='Saved. New routing decisions will load these rules.';}catch(error){rulesStatus.textContent='Save failed: '+error.message;}finally{rulesSave.disabled=false;}}
package/dist/dashboard.js CHANGED
@@ -68,7 +68,7 @@ export function serveDashboard(store, port = 8787, bus, refreshMs = 300000, jobI
68
68
  const discoveredJobs = async (forceRefresh = false) => {
69
69
  try {
70
70
  const jobs = overviewProvider
71
- ? (await overviewProvider(forceRefresh)).map(item => ({ ...item.job, githubState: item.githubState, runtimeStatus: deriveJobRuntimeStatus(item.job, item.githubState), mergedAt: item.mergedAt, closedAt: item.closedAt, draft: item.draft, publication: item.publication }))
71
+ ? (await overviewProvider(forceRefresh, forceRefresh ? 'dashboard.jobs-refresh' : 'internal.unknown')).map(item => ({ ...item.job, githubState: item.githubState, runtimeStatus: deriveJobRuntimeStatus(item.job, item.githubState), mergedAt: item.mergedAt, closedAt: item.closedAt, draft: item.draft, publication: item.publication }))
72
72
  : jobId ? [await store.load(jobId)] : [];
73
73
  lastKnownJobs = jobs;
74
74
  return jobs;
package/dist/pool.js CHANGED
@@ -28,6 +28,7 @@ export class RepositoryWorkerPool {
28
28
  reconcileLocks = new Map();
29
29
  targetedReconcileFlights = new Map();
30
30
  jobTargets = new Map();
31
+ targetSeeds = new Map();
31
32
  constructor(client, repository, trustedAuthors, concurrency, makeReconciler, isRateLimited = () => false) {
32
33
  this.client = client;
33
34
  this.trustedAuthors = trustedAuthors;
@@ -99,6 +100,11 @@ export class RepositoryWorkerPool {
99
100
  this.running = false;
100
101
  }
101
102
  }
103
+ seedTargetSnapshot(repository, prNumber, jobId, seed) {
104
+ if (!this.repositories.includes(repository))
105
+ return;
106
+ this.targetSeeds.set(`${repository}#${prNumber}:${jobId}`, seed);
107
+ }
102
108
  async reconcileTarget(repository, prNumber, jobId) {
103
109
  if (this.isRateLimited() || !this.repositories.includes(repository))
104
110
  return;
@@ -108,8 +114,11 @@ export class RepositoryWorkerPool {
108
114
  if (existing)
109
115
  return existing;
110
116
  const flight = this.reconcileExclusive(jobId, async () => {
111
- const store = new GitHubStore(this.client, repository, prNumber, this.trustedAuthors);
112
- await withGitHubRequestContext(this.client, 'pool.targeted-reconcile', () => this.makeReconciler(store, Math.max(1, this.concurrency)).reconcile(jobId));
117
+ const seedKey = `${repository}#${prNumber}:${jobId}`;
118
+ const seed = this.targetSeeds.get(seedKey) ?? null;
119
+ this.targetSeeds.delete(seedKey);
120
+ const store = new GitHubStore(this.client, repository, prNumber, this.trustedAuthors, seed);
121
+ await withGitHubRequestContext(this.client, seed ? 'pool.targeted-reconcile.seeded' : 'pool.targeted-reconcile', () => this.makeReconciler(store, Math.max(1, this.concurrency)).reconcile(jobId));
113
122
  });
114
123
  this.targetedReconcileFlights.set(key, flight);
115
124
  try {
package/dist/relayd.js CHANGED
@@ -9,7 +9,7 @@ import { serveDashboard } from './dashboard.js';
9
9
  import { NatsEventBus } from './events.js';
10
10
  import { RepositoryWorkerPool, aggregateManagedGitHubJobs } from './pool.js';
11
11
  import { Reconciler } from './reconciler.js';
12
- import { dashboardManagedJobs, GitHubStore, InMemoryStore, loadManagedGitHubPullRequestGraphql, withGitHubRequestContext } from './store.js';
12
+ import { dashboardManagedJobs, GitHubStore, InMemoryStore, loadManagedGitHubPullRequestSnapshotGraphql, withGitHubRequestContext } from './store.js';
13
13
  import { githubAuthContext } from './github-auth.js';
14
14
  import { dashboardJobAction, isEntrypoint, runtimePlanner, runtimeReviewer, startService, SERVICE_DEFAULTS } from './cli.js';
15
15
  import { dashboardTaskAction, dashboardTaskSetState } from './dashboard-actions.js';
@@ -19,6 +19,10 @@ import { deriveJobRuntimeStatus } from './runtime-status.js';
19
19
  import { decisionMode, LayaCliDecisionEngine } from './decision.js';
20
20
  import { JsonlDecisionAuditStore } from './decision-audit.js';
21
21
  import { summarizeRuntime } from './runtime-summary.js';
22
+ const PR_REFRESH_COALESCE_MS = 1000;
23
+ export function shouldCoalescePullRequestRefresh(completedAt, now = Date.now(), force = false) {
24
+ return !force && completedAt !== null && now >= completedAt && now - completedAt < PR_REFRESH_COALESCE_MS;
25
+ }
22
26
  export const daemonHelp = `Agents Relay daemon runs the dashboard and worker pool.
23
27
 
24
28
  Usage:
@@ -97,7 +101,7 @@ export async function runDaemon(argv) {
97
101
  const usageRegistry = codexAndZaiUsageRegistry();
98
102
  const decisionAuditStore = new JsonlDecisionAuditStore(process.env.AGENTS_RELAY_DECISION_AUDIT_PATH ?? join(homedir(), '.agents-relay', 'decision-audit.jsonl'));
99
103
  let cachedOverviews = [];
100
- const githubReadPaths = { workspaceScans: 0, prRefreshes: 0 };
104
+ const githubReadPaths = { workspaceScans: 0, prRefreshes: 0, prRefreshesByTrigger: {}, prRefreshCoalesced: 0, prRefreshCoalescedByTrigger: {} };
101
105
  let dormantProbeIndex = 0;
102
106
  const makeReconciler = (store, maxConcurrent) => new Reconciler(store, { owner: `relayd-${process.pid}`, maxConcurrent, leaseMs: Number(value(argv, '--lease-ms', '300000')), adapters: [new CodexAdapter(value(argv, '--codex', 'codex')), new ChatGptAdapter()], continuations: [new CodexThreadContinuation(value(argv, '--codex', 'codex')), new CommandContinuation(), new WebhookContinuation()], planner: runtimePlanner(argv), usageSnapshotProvider: () => usageRegistry.snapshot(), runtimeSummaryProvider: () => ({ chatgptRunning: summarizeRuntime(cachedOverviews.map(item => item.job)).workers.chatgpt }), decisionEngine: new LayaCliDecisionEngine(), decisionMode: decisionMode(), decisionAuditStore, eventBus: bus, reviewer: runtimeReviewer(), projectPathForJob: async (job) => resolveRepositoryProjectPath(repositoryPaths.get(job.repository) ?? await resolveManagedProjectPath(job.repository, explicitProjectPath, workspaceRoot), job.projectPath) });
103
107
  if (repositories.length === 0)
@@ -134,7 +138,7 @@ export async function runDaemon(argv) {
134
138
  return cachedOverviews;
135
139
  }
136
140
  };
137
- const overviews = async (forceRefresh = false) => forceRefresh ? refreshAll(true) : cachedOverviews;
141
+ const overviews = async (forceRefresh = false, _trigger = 'internal.unknown') => forceRefresh ? refreshAll(true) : cachedOverviews;
138
142
  const persistCache = async () => {
139
143
  try {
140
144
  await mkdir(cacheDirectory, { recursive: true });
@@ -143,21 +147,34 @@ export async function runDaemon(argv) {
143
147
  catch { /* cache is best effort */ }
144
148
  };
145
149
  const pullRequestRefreshFlights = new Map();
146
- const refreshPullRequest = async (repository, prNumber) => {
150
+ const pullRequestRefreshCache = new Map();
151
+ const incrementTrigger = (counts, trigger) => { counts[trigger] = (counts[trigger] ?? 0) + 1; };
152
+ const refreshPullRequestSnapshot = async (repository, prNumber, trigger = 'internal', options = {}) => {
147
153
  if (client.rateLimitStatus().limited)
148
- return cachedOverviews.filter(item => item.job.repository === repository && item.job.prNumber === prNumber);
154
+ return { overviews: cachedOverviews.filter(item => item.job.repository === repository && item.job.prNumber === prNumber), seed: { prNode: {}, comments: null } };
149
155
  const key = `${repository}#${prNumber}`;
156
+ incrementTrigger(githubReadPaths.prRefreshesByTrigger, trigger);
150
157
  const existing = pullRequestRefreshFlights.get(key);
151
- if (existing)
158
+ if (existing) {
159
+ githubReadPaths.prRefreshCoalesced += 1;
160
+ incrementTrigger(githubReadPaths.prRefreshCoalescedByTrigger, trigger);
152
161
  return existing;
162
+ }
163
+ const cached = pullRequestRefreshCache.get(key);
164
+ if (cached && shouldCoalescePullRequestRefresh(cached.completedAt, Date.now(), options.force === true)) {
165
+ githubReadPaths.prRefreshCoalesced += 1;
166
+ incrementTrigger(githubReadPaths.prRefreshCoalescedByTrigger, trigger);
167
+ return cached.snapshot;
168
+ }
153
169
  const flight = (async () => {
154
170
  githubReadPaths.prRefreshes += 1;
155
- const refreshed = await withGitHubRequestContext(client, 'relayd.pr-refresh', () => loadManagedGitHubPullRequestGraphql(client, repository, prNumber, trusted));
171
+ const snapshot = await withGitHubRequestContext(client, `relayd.pr-refresh:${trigger}`, () => loadManagedGitHubPullRequestSnapshotGraphql(client, repository, prNumber, trusted));
156
172
  cachedOverviews = cachedOverviews.filter(item => item.job.repository !== repository || item.job.prNumber !== prNumber);
157
- cachedOverviews.push(...refreshed);
173
+ cachedOverviews.push(...snapshot.overviews);
158
174
  cachedOverviews = dashboardManagedJobs(cachedOverviews);
159
175
  await persistCache();
160
- return refreshed;
176
+ pullRequestRefreshCache.set(key, { snapshot, completedAt: Date.now() });
177
+ return snapshot;
161
178
  })();
162
179
  pullRequestRefreshFlights.set(key, flight);
163
180
  try {
@@ -168,6 +185,7 @@ export async function runDaemon(argv) {
168
185
  pullRequestRefreshFlights.delete(key);
169
186
  }
170
187
  };
188
+ const refreshPullRequest = async (repository, prNumber, trigger, options) => (await refreshPullRequestSnapshot(repository, prNumber, trigger, options)).overviews;
171
189
  const initial = cachedOverviews;
172
190
  const fallback = initial[0]?.job;
173
191
  const dashboardStore = fallback ? new GitHubStore(client, fallback.repository, fallback.prNumber, trusted) : new InMemoryStore();
@@ -195,7 +213,7 @@ export async function runDaemon(argv) {
195
213
  throw new Error(`Managed job ${jobId} not found`);
196
214
  const store = new GitHubStore(client, overview.job.repository, overview.job.prNumber, trusted);
197
215
  const result = await dashboardTaskSetState(store)(jobId, taskId, state);
198
- await refreshPullRequest(overview.job.repository, overview.job.prNumber);
216
+ await refreshPullRequest(overview.job.repository, overview.job.prNumber, 'dashboard.task-state', { force: true });
199
217
  return result;
200
218
  };
201
219
  const secretFile = value(argv, '--github-webhook-secret-file');
@@ -206,8 +224,10 @@ export async function runDaemon(argv) {
206
224
  refresh: async (event) => {
207
225
  if (!event.repository || !event.pullRequest)
208
226
  return [];
209
- const refreshed = await refreshPullRequest(event.repository, event.pullRequest);
210
- return refreshed.map(item => item.job.id);
227
+ const snapshot = await refreshPullRequestSnapshot(event.repository, event.pullRequest, `github.webhook:${event.event}${event.action ? ':' + event.action : ''}`);
228
+ for (const item of snapshot.overviews)
229
+ pool.seedTargetSnapshot(event.repository, event.pullRequest, item.job.id, snapshot.seed);
230
+ return snapshot.overviews.map(item => item.job.id);
211
231
  },
212
232
  wake: async (event) => {
213
233
  if (!event.repository || !event.pullRequest)
@@ -230,7 +250,7 @@ export async function runDaemon(argv) {
230
250
  if (client.rateLimitStatus().limited)
231
251
  return cachedJob();
232
252
  try {
233
- const refreshed = (await refreshPullRequest(cached.job.repository, cached.job.prNumber)).find(item => item.job.id === jobId);
253
+ const refreshed = (await refreshPullRequest(cached.job.repository, cached.job.prNumber, 'dashboard.selected-job', { force: true })).find(item => item.job.id === jobId);
234
254
  if (!refreshed)
235
255
  return null;
236
256
  return { ...refreshed.job, githubState: refreshed.githubState, runtimeStatus: deriveJobRuntimeStatus(refreshed.job, refreshed.githubState), mergedAt: refreshed.mergedAt, closedAt: refreshed.closedAt, draft: refreshed.draft, publication: refreshed.publication };
@@ -11,11 +11,17 @@ export class GitHubStore {
11
11
  commentsCache = null;
12
12
  commentsFlight = null;
13
13
  clearReadSnapshot() { this.prNodeCache = null; this.commentsCache = null; this.commentsFlight = null; }
14
- constructor(client, repository, prNumber, trustedAuthors = new Set()) {
14
+ constructor(client, repository, prNumber, trustedAuthors = new Set(), seed = null) {
15
15
  this.client = client;
16
16
  this.repository = repository;
17
17
  this.prNumber = prNumber;
18
18
  this.trustedAuthors = trustedAuthors;
19
+ if (seed) {
20
+ if (Object.keys(seed.prNode).length > 0)
21
+ this.prNodeCache = Promise.resolve(seed.prNode);
22
+ if (seed.comments)
23
+ this.commentsCache = [...seed.comments];
24
+ }
19
25
  }
20
26
  taskKey(task) { return `${this.repository}#${this.prNumber}:${task.jobId ?? ''}/${task.id}`; }
21
27
  taskKeyFromBody(body) { const task = parseTask(body); return task ? this.taskKey(task) : null; }
package/dist/store.js CHANGED
@@ -12,28 +12,39 @@ function publicationFromComments(rows, trustedAuthors) {
12
12
  });
13
13
  return allowed.map(item => parsePackagePublicationMarker(String(item.body ?? ''))).filter((item) => item !== null).at(-1) ?? null;
14
14
  }
15
- export async function loadManagedGitHubPullRequestGraphql(client, repository, prNumber, trustedAuthors) {
15
+ export async function loadManagedGitHubPullRequestSnapshotGraphql(client, repository, prNumber, trustedAuthors) {
16
16
  const [owner, name] = repository.split('/', 2);
17
17
  if (!owner || !name || !Number.isInteger(prNumber) || prNumber <= 0 || !client.graphql)
18
- return [];
19
- const query = 'query($owner:String!,$name:String!,$number:Int!){repository(owner:$owner,name:$name){pullRequest(number:$number){state isDraft merged mergedAt closedAt body comments(last:100){nodes{databaseId url author{login} body}}}}}';
18
+ return { overviews: [], seed: { prNode: {}, comments: null } };
19
+ const query = 'query($owner:String!,$name:String!,$number:Int!){repository(owner:$owner,name:$name){pullRequest(number:$number){id state isDraft merged mergedAt closedAt body headRefName headRefOid comments(last:100){totalCount nodes{id databaseId url updatedAt author{login} body}}}}}';
20
20
  const data = await client.graphql(query, { owner, name, number: prNumber });
21
21
  const repo = data.repository;
22
22
  const pr = repo?.pullRequest;
23
23
  if (!pr)
24
- return [];
25
- const comments = pr.comments?.nodes;
26
- const trusted = (Array.isArray(comments) ? comments : []).filter(item => {
27
- const row = item;
24
+ return { overviews: [], seed: { prNode: {}, comments: null } };
25
+ const commentConnection = pr.comments;
26
+ const commentRows = (Array.isArray(commentConnection?.nodes) ? commentConnection.nodes : []);
27
+ const totalCount = Number(commentConnection?.totalCount ?? commentRows.length);
28
+ const seedComments = Number.isFinite(totalCount) && totalCount <= commentRows.length ? commentRows.map(row => {
28
29
  const author = row.author;
30
+ return {
31
+ id: Number(row.databaseId ?? 0),
32
+ nodeId: typeof row.id === 'string' ? row.id : undefined,
33
+ url: typeof row.url === 'string' ? row.url : undefined,
34
+ body: String(row.body ?? ''),
35
+ updatedAt: String(row.updatedAt ?? ''),
36
+ authorLogin: typeof author?.login === 'string' ? author.login : null,
37
+ };
38
+ }) : null;
39
+ const trusted = commentRows.filter(item => {
40
+ const author = item.author;
29
41
  return typeof author?.login === 'string' && trustedAuthors.has(author.login);
30
42
  });
31
43
  const bodyJob = parseJob(typeof pr.body === 'string' ? pr.body : '');
32
44
  const commentJobs = trusted.map(item => parseJob(String(item.body ?? ''))).filter((item) => item !== null);
33
45
  const jobs = bodyJob ? [bodyJob] : commentJobs;
34
- const publication = publicationFromComments((Array.isArray(comments) ? comments : []), trustedAuthors);
35
- const tasks = trusted.map(item => {
36
- const row = item;
46
+ const publication = publicationFromComments(commentRows, trustedAuthors);
47
+ const tasks = trusted.map(row => {
37
48
  const task = parseTask(String(row.body ?? ''));
38
49
  if (!task)
39
50
  return null;
@@ -42,12 +53,18 @@ export async function loadManagedGitHubPullRequestGraphql(client, repository, pr
42
53
  }).filter((item) => item !== null);
43
54
  const state = String(pr.state ?? '').toUpperCase();
44
55
  const githubState = pr.merged === true ? 'MERGED' : state === 'CLOSED' ? 'CLOSED' : 'OPEN';
45
- return jobs.map(job => {
56
+ const overviews = jobs.map(job => {
46
57
  job.tasks = tasks.filter(task => task.jobId === job.id || (!task.jobId && jobs.length === 1));
47
58
  if (!job.description && typeof pr.body === 'string' && pr.body)
48
59
  job.description = pr.body;
60
+ if (typeof pr.headRefOid === 'string' && pr.headRefOid)
61
+ job.headSha = pr.headRefOid;
49
62
  return { job, githubState, draft: pr.isDraft === true, mergedAt: typeof pr.mergedAt === 'string' ? pr.mergedAt : null, closedAt: typeof pr.closedAt === 'string' ? pr.closedAt : null, publication };
50
63
  });
64
+ return { overviews, seed: { prNode: pr, comments: seedComments } };
65
+ }
66
+ export async function loadManagedGitHubPullRequestGraphql(client, repository, prNumber, trustedAuthors) {
67
+ return (await loadManagedGitHubPullRequestSnapshotGraphql(client, repository, prNumber, trustedAuthors)).overviews;
51
68
  }
52
69
  export async function loadManagedGitHubJobGraphql(client, repository, prNumber, jobId, trustedAuthors) {
53
70
  return (await loadManagedGitHubPullRequestGraphql(client, repository, prNumber, trustedAuthors)).find(item => item.job.id === jobId) ?? null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agents-relay",
3
- "version": "1.0.138",
3
+ "version": "1.0.140",
4
4
  "description": "Durable async agent jobs coordinated through GitHub pull requests",
5
5
  "type": "module",
6
6
  "bin": {