agents-relay 1.0.88 → 1.0.89

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.
Files changed (2) hide show
  1. package/dist/reconciler.js +17 -3
  2. package/package.json +1 -1
@@ -72,6 +72,15 @@ Before your final return, verify that a terminal event for this exact Job/Task w
72
72
  If no terminal event has been published, publish task.failed before exiting.`;
73
73
  }
74
74
  const taskLaunchClaims = new Set();
75
+ const terminalExecutionClaims = new Set();
76
+ function claimTerminalExecution(sourceId) {
77
+ terminalExecutionClaims.add(sourceId);
78
+ const cleanup = setTimeout(() => terminalExecutionClaims.delete(sourceId), 60000);
79
+ cleanup.unref();
80
+ }
81
+ function terminalExecutionClaimed(jobId, taskId, executionId) {
82
+ return terminalExecutionClaims.has(executionEventSource(jobId, taskId, executionId));
83
+ }
75
84
  export class Reconciler {
76
85
  store;
77
86
  options;
@@ -766,7 +775,7 @@ export class Reconciler {
766
775
  if (!task || task.executionId !== executionId)
767
776
  return;
768
777
  await this.emit(eventFor(jobId, taskId, task.parentTaskId, task.adapter === 'chatgpt' ? 'task.process.async_exited' : 'task.process.exited', error ? 'failed' : 'succeeded', error ?? result?.summary ?? 'Worker runtime exited', 'orchestrator', { authoritative: false, waitingForTerminalEvent: true, processError: error }, { agent: 'relay', component: 'agents-relay' }));
769
- if (['QUEUED', 'RUNNING'].includes(task.state) && task.adapter === 'codex')
778
+ if (['QUEUED', 'RUNNING'].includes(task.state) && task.adapter === 'codex' && !terminalExecutionClaimed(jobId, taskId, executionId))
770
779
  this.scheduleUnexpectedExit(jobId, taskId, executionId, workerStarted === true);
771
780
  }
772
781
  scheduleUnexpectedExit(jobId, taskId, executionId, workerStarted) {
@@ -778,9 +787,11 @@ export class Reconciler {
778
787
  this.terminalExitGraceTimers.set(taskId, timer);
779
788
  }
780
789
  async failUnexpectedExit(jobId, taskId, executionId, workerStarted) {
790
+ if (terminalExecutionClaimed(jobId, taskId, executionId))
791
+ return;
781
792
  const job = await this.store.load(jobId);
782
793
  const task = job.tasks.find(item => item.id === taskId);
783
- if (!task || task.executionId !== executionId || !['QUEUED', 'RUNNING'].includes(task.state))
794
+ if (!task || task.executionId !== executionId || !['QUEUED', 'RUNNING'].includes(task.state) || terminalExecutionClaimed(jobId, taskId, executionId))
784
795
  return;
785
796
  const reason = workerStarted ? 'worker_exit_without_terminal_event' : 'worker_exit_before_start';
786
797
  const message = workerStarted
@@ -848,7 +859,10 @@ export class Reconciler {
848
859
  const terminalWaiter = this.terminalWaiters.get(task.id);
849
860
  if (terminalWaiter && (!event.timestamp || new Date(event.timestamp).getTime() < terminalWaiter.launchBarrier))
850
861
  return;
851
- this.clearExecution(task.id, task.executionId);
862
+ const acceptedExecutionId = task.executionId;
863
+ if (modelBacked && acceptedExecutionId)
864
+ claimTerminalExecution(executionEventSource(job.id, task.id, acceptedExecutionId));
865
+ this.clearExecution(task.id, acceptedExecutionId);
852
866
  task.leaseOwner = null;
853
867
  task.leaseExpiresAt = null;
854
868
  task.updatedAt = event.timestamp || new Date().toISOString();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agents-relay",
3
- "version": "1.0.88",
3
+ "version": "1.0.89",
4
4
  "description": "Durable async agent jobs coordinated through GitHub pull requests",
5
5
  "type": "module",
6
6
  "bin": {