agent-relay-server 0.111.0 → 0.112.0

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 (69) hide show
  1. package/docs/openapi.json +1 -1
  2. package/package.json +2 -2
  3. package/public/assets/{MessageBubble-WYqSyKPM.js → MessageBubble-DtLlfBAK.js} +2 -2
  4. package/public/assets/{MessageBubble-WYqSyKPM.js.map → MessageBubble-DtLlfBAK.js.map} +1 -1
  5. package/public/assets/{PlanGraphPanel-Dvu17jGe.js → PlanGraphPanel-pcKJ4q_4.js} +2 -2
  6. package/public/assets/{PlanGraphPanel-Dvu17jGe.js.map → PlanGraphPanel-pcKJ4q_4.js.map} +1 -1
  7. package/public/assets/{activity-wAN_upn7.js → activity-DRAeL0Jq.js} +2 -2
  8. package/public/assets/{activity-wAN_upn7.js.map → activity-DRAeL0Jq.js.map} +1 -1
  9. package/public/assets/{agent-profiles-D8WLdTCs.js → agent-profiles-cnWPANzx.js} +2 -2
  10. package/public/assets/{agent-profiles-D8WLdTCs.js.map → agent-profiles-cnWPANzx.js.map} +1 -1
  11. package/public/assets/{agents-C5FajNPw.js → agents-DgUjmk_D.js} +2 -2
  12. package/public/assets/{agents-C5FajNPw.js.map → agents-DgUjmk_D.js.map} +1 -1
  13. package/public/assets/{automation-ShdXg0pX.js → automation-CuTlgY1E.js} +2 -2
  14. package/public/assets/{automation-ShdXg0pX.js.map → automation-CuTlgY1E.js.map} +1 -1
  15. package/public/assets/{chat-aRCk0pLJ.js → chat-C7XCmaZz.js} +2 -2
  16. package/public/assets/{chat-aRCk0pLJ.js.map → chat-C7XCmaZz.js.map} +1 -1
  17. package/public/assets/{formatted-body-impl-DZc7C4-2.js → formatted-body-impl-rLDPgdmJ.js} +2 -2
  18. package/public/assets/{formatted-body-impl-DZc7C4-2.js.map → formatted-body-impl-rLDPgdmJ.js.map} +1 -1
  19. package/public/assets/{index-DXEYz_JV.js → index-DGyeTLPg.js} +5 -5
  20. package/public/assets/index-DGyeTLPg.js.map +1 -0
  21. package/public/assets/{insights-FzTX2wNh.js → insights-C0dDL4El.js} +2 -2
  22. package/public/assets/{insights-FzTX2wNh.js.map → insights-C0dDL4El.js.map} +1 -1
  23. package/public/assets/{maintenance-C8Uw8XiI.js → maintenance-DkOkcEkb.js} +2 -2
  24. package/public/assets/{maintenance-C8Uw8XiI.js.map → maintenance-DkOkcEkb.js.map} +1 -1
  25. package/public/assets/{managed-agents-DVbJ2hqh.js → managed-agents-C7Czinaz.js} +2 -2
  26. package/public/assets/{managed-agents-DVbJ2hqh.js.map → managed-agents-C7Czinaz.js.map} +1 -1
  27. package/public/assets/{markdown-preview-impl-C5D-B-9S.js → markdown-preview-impl-BZ7sigEc.js} +2 -2
  28. package/public/assets/{markdown-preview-impl-C5D-B-9S.js.map → markdown-preview-impl-BZ7sigEc.js.map} +1 -1
  29. package/public/assets/{memory-Bp-D_V80.js → memory-Bys-M7GK.js} +2 -2
  30. package/public/assets/{memory-Bp-D_V80.js.map → memory-Bys-M7GK.js.map} +1 -1
  31. package/public/assets/{messages-DE1QSeHI.js → messages-COSJ6trN.js} +2 -2
  32. package/public/assets/{messages-DE1QSeHI.js.map → messages-COSJ6trN.js.map} +1 -1
  33. package/public/assets/{orchestrators-B0u2lQfw.js → orchestrators-BjLl1Z0L.js} +2 -2
  34. package/public/assets/{orchestrators-B0u2lQfw.js.map → orchestrators-BjLl1Z0L.js.map} +1 -1
  35. package/public/assets/{overview-DpaEvvLT.js → overview-BmU7twic.js} +2 -2
  36. package/public/assets/{overview-DpaEvvLT.js.map → overview-BmU7twic.js.map} +1 -1
  37. package/public/assets/{pairs-DRp_qAu8.js → pairs-xclXjw-c.js} +2 -2
  38. package/public/assets/{pairs-DRp_qAu8.js.map → pairs-xclXjw-c.js.map} +1 -1
  39. package/public/assets/{prompt-settings-CNoBiymk.js → prompt-settings-CtQd0nz0.js} +2 -2
  40. package/public/assets/{prompt-settings-CNoBiymk.js.map → prompt-settings-CtQd0nz0.js.map} +1 -1
  41. package/public/assets/{registry-0A4QFMWC.js → registry-CDe6vjKI.js} +2 -2
  42. package/public/assets/{registry-0A4QFMWC.js.map → registry-CDe6vjKI.js.map} +1 -1
  43. package/public/assets/{security-C9P6yiQ3.js → security-C9mBNQMZ.js} +2 -2
  44. package/public/assets/{security-C9P6yiQ3.js.map → security-C9mBNQMZ.js.map} +1 -1
  45. package/public/assets/{select-B1PVUj6D.js → select-BJDKQk4q.js} +2 -2
  46. package/public/assets/{select-B1PVUj6D.js.map → select-BJDKQk4q.js.map} +1 -1
  47. package/public/assets/{settings-TnHlB9-t.js → settings-BmZjWJkI.js} +2 -2
  48. package/public/assets/{settings-TnHlB9-t.js.map → settings-BmZjWJkI.js.map} +1 -1
  49. package/public/assets/{tasks-1oK_ggFC.js → tasks-DNjZKSM2.js} +2 -2
  50. package/public/assets/tasks-DNjZKSM2.js.map +1 -0
  51. package/public/assets/{teams-BBb0ZYiF.js → teams-KNzmdolE.js} +2 -2
  52. package/public/assets/{teams-BBb0ZYiF.js.map → teams-KNzmdolE.js.map} +1 -1
  53. package/public/assets/{terminal-viewer-impl-DzvaRjkp.js → terminal-viewer-impl-DnFJDl9N.js} +2 -2
  54. package/public/assets/{terminal-viewer-impl-DzvaRjkp.js.map → terminal-viewer-impl-DnFJDl9N.js.map} +1 -1
  55. package/public/assets/{user-avatar-X1pfizLv.js → user-avatar-C8sbjQke.js} +2 -2
  56. package/public/assets/{user-avatar-X1pfizLv.js.map → user-avatar-C8sbjQke.js.map} +1 -1
  57. package/public/assets/{work-queue-DQMbkSD8.js → work-queue-DINvPVPn.js} +2 -2
  58. package/public/assets/{work-queue-DQMbkSD8.js.map → work-queue-DINvPVPn.js.map} +1 -1
  59. package/public/assets/{workspaces-BNLP4IlV.js → workspaces-CSNdUg15.js} +2 -2
  60. package/public/assets/{workspaces-BNLP4IlV.js.map → workspaces-CSNdUg15.js.map} +1 -1
  61. package/public/index.html +1 -1
  62. package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
  63. package/src/mcp-task-tools.ts +2 -1
  64. package/src/services/spawn-registration-watchdog.ts +5 -5
  65. package/src/services/task-done-when.ts +21 -0
  66. package/src/services/task-entity.ts +10 -1
  67. package/src/services/task-lifecycle.ts +9 -39
  68. package/public/assets/index-DXEYz_JV.js.map +0 -1
  69. package/public/assets/tasks-1oK_ggFC.js.map +0 -1
@@ -0,0 +1,21 @@
1
+ import { TASK_DELIVERABLE_STAGES } from "agent-relay-sdk";
2
+ import type { TaskDeliverableStage } from "../types";
3
+
4
+ const STAGE_INDEX = new Map<string, number>(TASK_DELIVERABLE_STAGES.map((stage, i) => [stage, i]));
5
+
6
+ // `doneWhen` is free text, but these lifecycle tokens have durable semantics.
7
+ export function doneWhenTargetStage(doneWhen: string | undefined): TaskDeliverableStage {
8
+ const value = doneWhen?.trim().toLowerCase();
9
+ if (!value) return "landed-on-main";
10
+ if (value === "deliverable-attached" || value.includes("deliverable attached")) return "committed-on-branch";
11
+ if (value === "landed" || value === "landed-on-main" || value.includes("landed")) return "landed-on-main";
12
+ if (value.includes("deploying")) return "deploying";
13
+ if (value.includes("deploy")) return "deployed";
14
+ return "landed-on-main";
15
+ }
16
+
17
+ export function stageSatisfiesDoneWhen(stage: TaskDeliverableStage, doneWhen: string | undefined): boolean {
18
+ const current = STAGE_INDEX.get(stage) ?? 0;
19
+ const target = STAGE_INDEX.get(doneWhenTargetStage(doneWhen)) ?? STAGE_INDEX.get("landed-on-main")!;
20
+ return current >= target;
21
+ }
@@ -30,6 +30,7 @@ import {
30
30
  import { emitTaskChanged } from "../sse";
31
31
  import type { Task, TaskDetail, TaskEvent, TaskGateState } from "../types";
32
32
  import { removeSubscriptionsForTask } from "../db/notification-subscriptions";
33
+ import { stageSatisfiesDoneWhen } from "./task-done-when";
33
34
  import { autoSubscribeTaskCoordinator, emitTaskSemanticNotification, taskTransitionForStage, taskTransitionForStatus } from "./task-semantic-events";
34
35
 
35
36
  export function createTaskEntity(input: CreateDeliverableTaskInput): TaskDetail {
@@ -82,7 +83,7 @@ function isTerminalTaskStatus(status: Task["status"]): boolean {
82
83
 
83
84
  export function setTaskEntityStage(id: number, input: SetTaskStageInput): TaskDetail {
84
85
  const before = getTaskDetail(id);
85
- const detail = setTaskStage(id, input);
86
+ let detail = setTaskStage(id, input);
86
87
  emitTaskChanged(detail, "task.stage");
87
88
  const transition = taskTransitionForStage(detail.deliverableStage);
88
89
  if (input.actor !== "task-lifecycle" && transition && before?.deliverableStage !== detail.deliverableStage) {
@@ -94,6 +95,14 @@ export function setTaskEntityStage(id: number, input: SetTaskStageInput): TaskDe
94
95
  occurrenceKey: latestTaskEventOccurrence(detail.id, `stage:${detail.deliverableStage}`),
95
96
  });
96
97
  }
98
+ const deliverableStage = detail.deliverableStage ?? "none";
99
+ if (detail.status !== "done" && stageSatisfiesDoneWhen(deliverableStage, detail.doneWhen)) {
100
+ detail = setTaskEntityStatus(id, {
101
+ status: "done",
102
+ note: `doneWhen reached at stage ${deliverableStage}`,
103
+ actor: input.actor,
104
+ });
105
+ }
97
106
  return detail;
98
107
  }
99
108
 
@@ -21,7 +21,7 @@
21
21
  // owns execution telemetry. Issue CLOSE is the canonical #482/#474 path (which runs just before
22
22
  // this in `notifyBranchLanded`) — we do NOT re-close; we PROJECT it via #833's `taskCloseBridge`.
23
23
  // The one write-through we make is the single milestone summary (#833's milestone exception).
24
- import { errMessage, TASK_DELIVERABLE_STAGES } from "agent-relay-sdk";
24
+ import { errMessage } from "agent-relay-sdk";
25
25
  import {
26
26
  deliverableTaskIdsByBranch,
27
27
  deliverableTaskIdsByOwnerAgent,
@@ -30,12 +30,13 @@ import {
30
30
  getTaskDetail,
31
31
  markTaskAgentReaped,
32
32
  } from "../db";
33
- import { setTaskEntityStage, setTaskEntityStatus } from "./task-entity";
33
+ import { setTaskEntityStage } from "./task-entity";
34
34
  import { postTaskMilestoneComment, taskCloseBridge, type TaskCloseBridgeEntry } from "./task-issue-weave";
35
35
  import { requestTransientAgentShutdown } from "./transient-agent-reaper";
36
36
  import { emitRelayEvent } from "../events";
37
37
  import { routeNotification } from "../notification-router";
38
38
  import { resolveCoordinatorAgentId } from "../steward-identity";
39
+ import { stageSatisfiesDoneWhen } from "./task-done-when";
39
40
  import { emitTaskSemanticNotification, type TaskSemanticTransition } from "./task-semantic-events";
40
41
  import type { IssueRef, TaskDeliverableStage, TaskGateState, WorkspaceRecord } from "../types";
41
42
  import type { IssueTrackerAdapter } from "../issue-tracker";
@@ -120,33 +121,12 @@ function landedDiffSummary(input: LandedTaskLifecycleInput, log: LogFn): string
120
121
  }
121
122
  }
122
123
 
123
- // --- doneWhen → deliverable-stage mapping -------------------------------------------------------
124
- const STAGE_INDEX = new Map<string, number>(TASK_DELIVERABLE_STAGES.map((stage, i) => [stage, i]));
125
-
126
- // Which deliverable stage satisfies a Task's `doneWhen`. `doneWhen` is free text (#834): we map
127
- // the small known vocabulary (`landed` | `deployed` | `deliverable-attached`, with synonyms) and
128
- // default an UNSET doneWhen to `landed-on-main` — the common case where a Task is done when it
129
- // lands. An unrecognized value falls back to the landed default rather than blocking forever.
130
- function doneWhenTargetStage(doneWhen: string | undefined): TaskDeliverableStage {
131
- const value = doneWhen?.trim().toLowerCase();
132
- if (!value) return "landed-on-main";
133
- if (value.includes("deploying")) return "deploying";
134
- if (value.includes("deploy")) return "deployed";
135
- return "landed-on-main";
136
- }
137
-
138
- function stageSatisfiesDoneWhen(stage: TaskDeliverableStage, doneWhen: string | undefined): boolean {
139
- const current = STAGE_INDEX.get(stage) ?? 0;
140
- const target = STAGE_INDEX.get(doneWhenTargetStage(doneWhen)) ?? STAGE_INDEX.get("landed-on-main")!;
141
- return current >= target;
142
- }
143
-
144
124
  // --- gate verdict ------------------------------------------------------------------------------
145
- // "Gates passed" is the signal that frees the agent (reap) AND closes the Task (doneWhen). The
125
+ // "Gates passed" is the signal that frees the agent (reap). The
146
126
  // merge landing green is necessary but NOT sufficient (Codex #831 review): a worker that crashed
147
127
  // before reporting gates, or reported only a subset, must NOT read as green on a vacuous `[]`.
148
128
  // So a pass REQUIRES the expected gates present-and-passing AND no recorded gate failing. An
149
- // empty / missing / partial / failed ledger is NOT passed → no reap, no close, no green report.
129
+ // empty / missing / partial / failed ledger is NOT passed → no reap, no green report.
150
130
  export const EXPECTED_GATES = ["typecheck", "test"] as const;
151
131
 
152
132
  interface GateVerdict {
@@ -310,20 +290,10 @@ async function driveTaskOnLand(
310
290
  const gatesPassed = gateVerdict.passed;
311
291
 
312
292
  // 3 (doneWhen axis) — reaching the Task's doneWhen target CLOSES the Task (execution status →
313
- // done) but ONLY with verified gates (Codex #831): a failed/incomplete gate keeps the Task
314
- // open, never `done`, even at/after the doneWhen stage. Otherwise it stays where it was ("landed
315
- // but not done", e.g. doneWhen=deployed waits on deploy). Idempotent: an already-done Task isn't
316
- // re-written.
293
+ // done). Gates are intentionally separate: they guard reaping and green reporting, not the
294
+ // execution status completion predicate. Otherwise empty gates can strand a landed Task forever.
317
295
  const doneWhenSatisfied = stageSatisfiesDoneWhen("landed-on-main", before.doneWhen);
318
- let statusAdvanced = false;
319
- if (doneWhenSatisfied && gatesPassed && before.status !== "done") {
320
- detail = setTaskEntityStatus(taskId, {
321
- status: "done",
322
- note: `doneWhen reached on land (${input.mergedSha?.slice(0, 12) ?? input.workspace.branch ?? "merged"})`,
323
- actor: "task-lifecycle",
324
- });
325
- statusAdvanced = true;
326
- }
296
+ const statusAdvanced = before.status !== "done" && detail.status === "done";
327
297
 
328
298
  // 3b (close projection) — the linked issue is closed by the canonical #482/#474 path (which ran
329
299
  // immediately before this in notifyBranchLanded). We PROJECT that ledger via #833, never re-close.
@@ -341,7 +311,7 @@ async function driveTaskOnLand(
341
311
  const summaryBits = [`Landed to \`${input.workspace.baseRef ?? "main"}\``];
342
312
  if (diffSummary) summaryBits.push(diffSummary);
343
313
  if (!gatesPassed) summaryBits.push(`gates not verified (${gateVerdict.reason ?? "incomplete"})`);
344
- summaryBits.push(statusAdvanced ? "Task done (doneWhen reached)" : `Task at \`landed-on-main\` (doneWhen \`${before.doneWhen ?? "landed"}\` not yet reached)`);
314
+ summaryBits.push(doneWhenSatisfied ? "Task done (doneWhen reached)" : `Task at \`landed-on-main\` (doneWhen \`${before.doneWhen ?? "landed"}\` not yet reached)`);
345
315
  const milestone = await postTaskMilestoneComment(
346
316
  {
347
317
  taskId,