agents-relay 2.0.110 → 2.0.111

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/dashboard.js CHANGED
@@ -207,17 +207,24 @@ export function serveDashboard(store, port = 8787, bus, refreshMs = 300000, jobI
207
207
  res.end('Ignored');
208
208
  return;
209
209
  }
210
- if (!webhookDeliveries.accept(event.deliveryId)) {
210
+ // A 202 is an acknowledgement of durable handling, not merely receipt of HTTP bytes.
211
+ // Do not suppress a retry when the SQLite wake fails after webhook authentication.
212
+ if (webhookDeliveries.has(event.deliveryId)) {
211
213
  res.writeHead(202);
212
214
  res.end('Duplicate');
213
215
  return;
214
216
  }
215
- res.writeHead(202);
216
- res.end('Accepted');
217
- queueMicrotask(() => {
218
- void Promise.resolve(webhook.wake(event))
219
- .catch(error => process.stderr.write(`warning: webhook wake unavailable: ${error instanceof Error ? error.message : String(error)}\n`));
220
- });
217
+ try {
218
+ await webhook.wake(event);
219
+ webhookDeliveries.accept(event.deliveryId);
220
+ res.writeHead(202);
221
+ res.end('Accepted');
222
+ }
223
+ catch (error) {
224
+ process.stderr.write(`warning: webhook wake unavailable: ${error instanceof Error ? error.message : String(error)}\n`);
225
+ res.writeHead(503);
226
+ res.end('Webhook handling unavailable; redelivery required');
227
+ }
221
228
  return;
222
229
  }
223
230
  if (new URL(req.url ?? '/', 'http://127.0.0.1').pathname === '/api/jobs') {
@@ -144,6 +144,9 @@ export class DeliveryDeduper {
144
144
  this.ttlMs = ttlMs;
145
145
  this.maxEntries = maxEntries;
146
146
  }
147
+ has(id, now = Date.now()) {
148
+ return (this.seen.get(id) ?? 0) > now;
149
+ }
147
150
  accept(id, now = Date.now()) {
148
151
  for (const [key, expires] of this.seen)
149
152
  if (expires <= now)
@@ -7,6 +7,9 @@ export function normalizeLegacyJobPrNumber(input) {
7
7
  input.prNumber = Number(legacy);
8
8
  delete record[legacyKey];
9
9
  for (const task of input.tasks ?? []) {
10
+ if (task.id.startsWith('final-review-') && task.agentName === 'reviewer' && /^Job ID:\s*\d+$/i.test(task.title?.trim() ?? '')) {
11
+ task.title = `Final review: ${input.title}`;
12
+ }
10
13
  if (!task.title?.trim())
11
14
  task.title = deriveTaskTitle(task.input, task.agentName ? `${task.agentName} task` : 'Task');
12
15
  }
@@ -145,7 +145,7 @@ export class ReconcilerReviewBase extends ReconcilerRuntimeBase {
145
145
  'Do not treat historical failed attempts as current artifact defects; distinguish preserved baseline files from implementation.',
146
146
  ].join('\n\n');
147
147
  const task = createTaskEntity(job, {
148
- jobId: job.id, id, kind: 'work', priority: job.priority, storySize: 'M', projectName: job.title, agentName: 'reviewer',
148
+ jobId: job.id, id, title: `Final review: ${job.title}`, kind: 'work', priority: job.priority, storySize: 'M', projectName: job.title, agentName: 'reviewer',
149
149
  parentTaskId: null, dependencies: reviewGateTasks.map(item => item.id), capabilities: ['review'],
150
150
  workerSelection: reviewerRouting.selection, adapter: reviewerRouting.candidate.adapter,
151
151
  input,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agents-relay",
3
- "version": "2.0.110",
3
+ "version": "2.0.111",
4
4
  "description": "Durable async agent jobs coordinated through GitHub pull requests",
5
5
  "repository": {
6
6
  "type": "git",