agents-relay 1.0.26 → 1.0.27

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/cli.js CHANGED
@@ -61,7 +61,7 @@ Example:
61
61
  'job create': `Create or reuse an open PR and persist one durable job marker.
62
62
 
63
63
  Required: --repo OWNER/REPO, --id JOB_ID, and --head BRANCH.
64
- Common: --base BRANCH (default: main), --title TEXT, --body TEXT, --mode fixed|autonomous.
64
+ Common: --base BRANCH (default: main), --title TEXT, --body TEXT, --mode fixed|autonomous, --no-auto-merge.
65
65
  With --start, also require --adapter codex|chatgpt, --model MODEL, --output task-pr|file,
66
66
  and --input TEXT. The initial task ID defaults to 'initial'; use --task-id to override it.
67
67
 
@@ -70,7 +70,7 @@ Example:
70
70
  'job adopt': `Adopt an existing PR as a durable job.
71
71
 
72
72
  Required: --repo OWNER/REPO, --pr NUMBER, and --id JOB_ID.
73
- Common: --title TEXT, --objective TEXT, --mode fixed|autonomous.
73
+ Common: --title TEXT, --objective TEXT, --mode fixed|autonomous, --no-auto-merge.
74
74
 
75
75
  Example:
76
76
  npx agents-relay job adopt --repo OWNER/REPO --pr 12 --id job-12 --title "Objective"`,
@@ -247,7 +247,7 @@ export async function ensureManagedGitHubJob(client, repositoryName, trustedAuth
247
247
  if (!(error instanceof Error) || !error.message.includes('has no durable'))
248
248
  throw error;
249
249
  const now = new Date().toISOString();
250
- job = { id: options.id, title: options.title || pr.title || 'Agents Relay job', objective: options.objective || pr.body || undefined, description: pr.body || undefined, priority: options.priority ?? 'P2', executionMode: options.executionMode ?? 'fixed', prNumber: pr.number, repository: repositoryName, state: 'OPEN', continuation: options.continuation ?? null, createdAt: now, updatedAt: now, tasks: [] };
250
+ job = { id: options.id, title: options.title || pr.title || 'Agents Relay job', objective: options.objective || pr.body || undefined, description: pr.body || undefined, priority: options.priority ?? 'P2', executionMode: options.executionMode ?? 'fixed', autoMerge: options.autoMerge ?? true, prNumber: pr.number, repository: repositoryName, state: 'OPEN', continuation: options.continuation ?? null, createdAt: now, updatedAt: now, tasks: [] };
251
251
  }
252
252
  if (!job.description && pr.body)
253
253
  job.description = pr.body;
@@ -255,6 +255,8 @@ export async function ensureManagedGitHubJob(client, repositoryName, trustedAuth
255
255
  job.objective = options.objective;
256
256
  if (options.executionMode)
257
257
  job.executionMode = options.executionMode;
258
+ if (options.autoMerge !== undefined)
259
+ job.autoMerge = options.autoMerge;
258
260
  if (options.priority)
259
261
  job.priority = options.priority;
260
262
  else if (!job.priority)
@@ -405,6 +407,7 @@ async function runJobCommand(action, args) {
405
407
  objective: arg(args, '--objective') || undefined,
406
408
  priority: priority(args),
407
409
  executionMode: requestedExecutionMode(args),
410
+ autoMerge: !hasArg(args, '--no-auto-merge'),
408
411
  continuation: continuation(args),
409
412
  prNumber,
410
413
  head: arg(args, '--head') || undefined,
@@ -518,7 +521,7 @@ async function main() {
518
521
  }
519
522
  if (command === 'init') {
520
523
  const now = new Date().toISOString();
521
- const job = { id: arg(args, '--id', randomUUID()), title: arg(args, '--title', 'Agents Relay job'), objective: arg(args, '--objective') || undefined, priority: priority(args), executionMode: executionMode(args), prNumber: Number(arg(args, '--pr', '0')), repository: arg(args, '--repo'), state: 'OPEN', continuation: continuation(args), createdAt: now, updatedAt: now, tasks: [] };
524
+ const job = { id: arg(args, '--id', randomUUID()), title: arg(args, '--title', 'Agents Relay job'), objective: arg(args, '--objective') || undefined, priority: priority(args), executionMode: executionMode(args), autoMerge: !hasArg(args, '--no-auto-merge'), prNumber: Number(arg(args, '--pr', '0')), repository: arg(args, '--repo'), state: 'OPEN', continuation: continuation(args), createdAt: now, updatedAt: now, tasks: [] };
522
525
  let store;
523
526
  let file = null;
524
527
  if (arg(args, '--repo') && arg(args, '--pr'))
@@ -203,7 +203,7 @@ export class Reconciler {
203
203
  return;
204
204
  const id = `final-review-${headSha.slice(0, 12)}`;
205
205
  const now = new Date().toISOString();
206
- const task = { jobId: job.id, id, kind: 'work', priority: job.priority, projectName: job.title, agentName: 'reviewer', parentTaskId: null, dependencies: job.tasks.filter(item => item.agentName !== 'reviewer' && !this.isSupersededTask(item)).map(item => item.id), capabilities: ['review'], adapter: this.options.reviewer?.adapter ?? 'codex', input: `Review PR #${job.prNumber} at head SHA ${headSha}. Objective: ${job.objective ?? job.description ?? job.title}. First publish task.started as the worker start acknowledgement with data.worker_owned=true, then verify the deterministic pre-review gates have passed and inspect the completed work. Emit review.started, then exactly one of review.approved, review.changes_required, or review.blocked with data.review.headSha set to ${headSha}. Do not modify code and do not merge.`, output: { kind: 'task_pr' }, routing: this.options.reviewer?.routing, continuation: null, continuationDeliveredAt: null, reviewHeadSha: headSha, state: 'QUEUED', attempt: 0, maxAttempts: this.options.reviewer?.maxAttempts ?? 2, leaseOwner: null, leaseExpiresAt: null, executionId: null, threadId: null, result: null, plannerResult: null, error: null, timeoutMs: this.options.reviewer?.timeoutMs ?? 300000, createdAt: now, updatedAt: now };
206
+ const task = { jobId: job.id, id, kind: 'work', priority: job.priority, projectName: job.title, agentName: 'reviewer', parentTaskId: null, dependencies: job.tasks.filter(item => item.agentName !== 'reviewer' && !this.isSupersededTask(item)).map(item => item.id), capabilities: ['review'], adapter: this.options.reviewer?.adapter ?? 'codex', input: `Review PR #${job.prNumber} at head SHA ${headSha}. Objective: ${job.objective ?? job.description ?? job.title}. First publish task.started as the worker start acknowledgement with data.worker_owned=true, then verify the deterministic pre-review gates have passed and inspect the completed work. Emit review.started, then exactly one of review.approved, review.changes_required, or review.blocked with data.review.headSha set to ${headSha}. Do not modify code. When auto-merge is enabled, Relay will merge this exact reviewed head after review.approved.`, output: { kind: 'task_pr' }, routing: this.options.reviewer?.routing, continuation: null, continuationDeliveredAt: null, reviewHeadSha: headSha, state: 'QUEUED', attempt: 0, maxAttempts: this.options.reviewer?.maxAttempts ?? 2, leaseOwner: null, leaseExpiresAt: null, executionId: null, threadId: null, result: null, plannerResult: null, error: null, timeoutMs: this.options.reviewer?.timeoutMs ?? 300000, createdAt: now, updatedAt: now };
207
207
  await this.store.appendTask(task);
208
208
  job.review = { outcome: 'pending', headSha, taskId: id, evidence: null, updatedAt: now };
209
209
  job.state = 'RUNNING';
@@ -246,6 +246,10 @@ export class Reconciler {
246
246
  }
247
247
  job.updatedAt = job.review.updatedAt;
248
248
  await this.store.saveJob(job);
249
+ if (outcome === 'approved' && job.autoMerge !== false && this.store.mergePullRequest) {
250
+ await this.store.mergePullRequest(headSha);
251
+ await this.emit(eventFor(job.id, task.id, task.parentTaskId, 'review.merged', 'succeeded', 'Reviewer-approved PR #' + job.prNumber + ' merged', 'user', { headSha, autoMerge: true }, { agent: 'reviewer', component: 'agents-relay' }));
252
+ }
249
253
  }
250
254
  async recoverLostExecutions(job, now) {
251
255
  for (const task of job.tasks.filter(item => item.state === 'RUNNING')) {
package/dist/store.js CHANGED
@@ -461,6 +461,17 @@ export class GitHubStore {
461
461
  return 'MERGED';
462
462
  return String(pr.state ?? '').toUpperCase() === 'CLOSED' ? 'CLOSED' : 'OPEN';
463
463
  }
464
+ async mergePullRequest(expectedHeadSha) {
465
+ const payload = { merge_method: 'squash' };
466
+ if (expectedHeadSha)
467
+ payload.sha = expectedHeadSha;
468
+ const result = await this.client.request('repos/' + this.repository + '/pulls/' + this.prNumber + '/merge', {
469
+ method: 'PUT',
470
+ body: JSON.stringify(payload),
471
+ });
472
+ if (result.merged !== true)
473
+ throw new Error(typeof result.message === 'string' ? result.message : 'Pull request #' + this.prNumber + ' was not merged');
474
+ }
464
475
  async headSha() {
465
476
  const { owner, name } = this.repoParts();
466
477
  const data = await this.graphql('query($owner:String!,$name:String!,$number:Int!){repository(owner:$owner,name:$name){pullRequest(number:$number){headRefOid}}}', { owner, name, number: this.prNumber });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agents-relay",
3
- "version": "1.0.26",
3
+ "version": "1.0.27",
4
4
  "description": "Durable async agent jobs coordinated through GitHub pull requests",
5
5
  "type": "module",
6
6
  "bin": {