agents-relay 2.0.113 → 2.0.115

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.
@@ -100,7 +100,7 @@ export class ReconcilerRuntimeBase {
100
100
  const task = (await this.store.load(event.job_id)).tasks.find(item => item.id === event.task_id);
101
101
  const normalized = task?.agentName === 'reviewer' ? normalizeReviewerTerminalEvent(event) : null;
102
102
  if (normalized)
103
- return this.applyReviewEvent(normalized);
103
+ return normalized.type === 'task.failed' ? this.applyTerminalEvent(normalized) : this.applyReviewEvent(normalized);
104
104
  }
105
105
  if (['task.completed', 'task.failed', 'task.blocked', 'task.cancelled'].includes(event.type)) {
106
106
  const result = await this.applyTerminalEvent(event);
@@ -110,7 +110,9 @@ export class ReconcilerRuntimeBase {
110
110
  return durable?.state === 'FAILED' && durable.executionHistory?.some(history => history.id === event.data?.executionId)
111
111
  ? { applied: true, reconcile: true, reason: 'watchdog.failure-fact' } : result;
112
112
  }
113
- if (['review.started', 'review.approved', 'review.changes_required', 'review.blocked'].includes(event.type))
113
+ if (event.type === 'review.blocked')
114
+ return this.applyTerminalEvent({ ...event, type: 'task.failed', data: { ...event.data, reason: 'review_evidence_unavailable' } });
115
+ if (['review.started', 'review.approved', 'review.changes_required'].includes(event.type))
114
116
  return this.applyReviewEvent(event);
115
117
  if (event.type === 'task.interactive_decision')
116
118
  return this.applyHostInvocationEvent(event);
@@ -399,6 +401,8 @@ export class ReconcilerRuntimeBase {
399
401
  eligible: (_job, task) => {
400
402
  if (!['codex', 'chatgpt', 'orchestrator'].includes(task.adapter))
401
403
  return false;
404
+ if (event.type === 'task.blocked' && (!trustedInternal || task.agentName !== 'troubleshooter') && task.interactiveInvocation?.status !== 'awaiting_decision')
405
+ return false;
402
406
  if (task.agentName === 'reviewer' && event.type === 'task.completed')
403
407
  return false;
404
408
  if (event.type === 'task.completed' && task.interactiveInvocation?.status !== undefined && task.interactiveInvocation.status !== 'approved')
@@ -474,7 +478,7 @@ export class ReconcilerRuntimeBase {
474
478
  if (event.type === 'task.completed')
475
479
  await this.deliverContinuation(transition.job, transition.task);
476
480
  const reviewWorker = transition.task.agentName === 'reviewer';
477
- return { applied: true, reconcile: !reviewWorker, reason: reviewWorker ? `${event.type}.review-worker` : event.type, reconcileMode: reviewWorker ? undefined : 'detached' };
481
+ return { applied: true, reconcile: !reviewWorker || event.type === 'task.failed', reason: reviewWorker ? `${event.type}.review-worker` : event.type, reconcileMode: reviewWorker ? undefined : 'detached' };
478
482
  }
479
483
  terminalWait(taskId, executionId, launchBarrier) {
480
484
  let resolve;
@@ -9,7 +9,7 @@ export function normalizeReviewerTerminalEvent(event) {
9
9
  const raw = data.verdict;
10
10
  const type = raw === 'approved' || raw === 'pass' ? 'review.approved'
11
11
  : raw === 'changes-required' || raw === 'changes_required' ? 'review.changes_required'
12
- : raw === 'blocked' ? 'review.blocked' : null;
12
+ : raw === 'blocked' ? 'task.failed' : null;
13
13
  if (!type)
14
14
  return null;
15
15
  const outcome = record(data.outcome);
@@ -24,6 +24,8 @@ export function normalizeReviewerTerminalEvent(event) {
24
24
  return null;
25
25
  const existingFindings = outcome.blocking_findings ?? outcome.blockingFindings;
26
26
  const findings = existingFindings ?? (Array.isArray(data.findings) ? data.findings : undefined);
27
+ if (type === 'task.failed')
28
+ return { ...event, type, message: event.message || 'Reviewer validation unavailable', data: { ...data, reason: 'review_evidence_unavailable' } };
27
29
  return { ...event, type, data: { ...data, outcome: {
28
30
  ...outcome, ...(head ? { head_sha: head } : {}),
29
31
  ...(findings !== undefined && existingFindings === undefined ? { blocking_findings: findings } : {}),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agents-relay",
3
- "version": "2.0.113",
3
+ "version": "2.0.115",
4
4
  "description": "Durable async agent jobs coordinated through GitHub pull requests",
5
5
  "repository": {
6
6
  "type": "git",
@@ -11,19 +11,19 @@ Review exactly the immutable target/version specified by the Review Task. Do not
11
11
 
12
12
  ## Review Scope
13
13
 
14
- Reconstruct all material acceptance requirements from the durable Job objective and Task, then evaluate each with direct evidence. Check correctness, completeness, provenance, quality, regressions, explicit approvals and externally observable outcomes where applicable. Missing evidence is BLOCKED, not PASS. Keep review independent of implementation work.
14
+ Reconstruct all material acceptance requirements from the durable Job objective and Task, then evaluate each with direct evidence. Check correctness, completeness, provenance, quality, regressions, explicit approvals and externally observable outcomes where applicable. Missing evidence is not PASS. If validation cannot complete, report task.failed. Keep review independent of implementation work.
15
15
 
16
16
  ## Review Decision
17
17
 
18
- Return a concrete approved, changes-required or blocked verdict tied to the declared immutable target, with attributed findings, severity and remediation steps. Do not alter artifacts, generate approvals on behalf of users, or perform publication, merges or external actions. Relay owns the review lifecycle and Task/Job completion.
18
+ Return a concrete approved or changes-required verdict tied to the declared immutable target, with attributed findings, severity and remediation steps. Do not alter artifacts, generate approvals on behalf of users, or perform publication, merges or external actions. Relay owns the review lifecycle and Task/Job completion.
19
19
 
20
20
  ## Unified execution event
21
21
 
22
22
  After completing the review, publish **`task.completed`**, including `data.verdict`
23
- (`approved`, `changes-required`, or `blocked`), the **exact immutable**
23
+ (`approved` or `changes-required`), the **exact immutable**
24
24
  `data.head_sha`, and actionable `data.findings` for changes required. A
25
25
  negative verdict is a successful review, not `task.failed`. If review
26
- execution fails and no sound verdict can be produced, publish `task.failed`.
26
+ execution fails or required validation evidence is unavailable, publish `task.failed`.
27
27
  Never emit `review.*` as the agent's terminal event. The event source must
28
28
  match the execution identity supplied by Relay. See
29
29
  `../contracts/event-contract.md`.
@@ -16,7 +16,7 @@ Every Implementation, Reviewer, and Troubleshooter execution publishes `task.sta
16
16
  | Role | `task.completed.data` | Relay action |
17
17
  | --- | --- | --- |
18
18
  | Implementation | Evidence of output/artifact, validation, and summary as required by the Task | Persist Task success and continue dependencies/review |
19
- | Reviewer | `verdict` (`approved`, `changes-required`, or `blocked`), exact immutable `head_sha`, actionable `findings` as necessary | Validate current immutable review target and apply verdict; defects are a successful Reviewer execution, not `task.failed` |
19
+ | Reviewer | `verdict` (`approved` or `changes-required`), exact immutable `head_sha`, actionable `findings` as necessary | Validate current immutable review target and apply verdict; defects are a successful Reviewer execution, not `task.failed` |
20
20
  | Troubleshooter | Recovery action and durable evidence of verified original Task retry, blocker-Job handoff, or other authorized recovery | Complete recovery Task only; original Task state changes through separately authorized Relay operations |
21
21
 
22
22
  A Reviewer must **not** publish unstructured `task.completed`: missing verdict or immutable head is invalid. Relay normalizes an authenticated structured Reviewer terminal event into its internal Review decision; native `review.*` events may be accepted for backward compatibility.
@@ -28,3 +28,7 @@ Only a real user action merits escalation. Troubleshooter invokes the Relay esca
28
28
  When the human responds, the **Troubleshooter Task** is authoritatively unblocked and resumed with its prior context plus the response. Troubleshooter independently verifies resolution, requests an authorized original Task recovery, and only then publishes its own `task.completed`. If unresolved, it continues diagnosis or escalates again; human assertion alone is not technical verification.
29
29
 
30
30
  See [task-recovery-flows.md](task-recovery-flows.md) for unified architecture, overall flow, failure recovery, and post-blocked resumption.
31
+
32
+ Reviewer unavailable evidence is an execution failure (`task.failed`), not a blocked Task. Legacy Reviewer verdict `blocked` is normalized to failure and must dispatch Troubleshooter. Only Relay blocks a Troubleshooter during human escalation.
33
+
34
+ A host-mediated interactive invocation is a separate Relay-controlled wait. It requires an already-bound `interactiveInvocation` with status `awaiting_decision`. It is not permission for a Worker or Reviewer to choose a blocked terminal outcome.