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.
- package/docs/openapi.json +1 -1
- package/package.json +2 -2
- package/public/assets/{MessageBubble-WYqSyKPM.js → MessageBubble-DtLlfBAK.js} +2 -2
- package/public/assets/{MessageBubble-WYqSyKPM.js.map → MessageBubble-DtLlfBAK.js.map} +1 -1
- package/public/assets/{PlanGraphPanel-Dvu17jGe.js → PlanGraphPanel-pcKJ4q_4.js} +2 -2
- package/public/assets/{PlanGraphPanel-Dvu17jGe.js.map → PlanGraphPanel-pcKJ4q_4.js.map} +1 -1
- package/public/assets/{activity-wAN_upn7.js → activity-DRAeL0Jq.js} +2 -2
- package/public/assets/{activity-wAN_upn7.js.map → activity-DRAeL0Jq.js.map} +1 -1
- package/public/assets/{agent-profiles-D8WLdTCs.js → agent-profiles-cnWPANzx.js} +2 -2
- package/public/assets/{agent-profiles-D8WLdTCs.js.map → agent-profiles-cnWPANzx.js.map} +1 -1
- package/public/assets/{agents-C5FajNPw.js → agents-DgUjmk_D.js} +2 -2
- package/public/assets/{agents-C5FajNPw.js.map → agents-DgUjmk_D.js.map} +1 -1
- package/public/assets/{automation-ShdXg0pX.js → automation-CuTlgY1E.js} +2 -2
- package/public/assets/{automation-ShdXg0pX.js.map → automation-CuTlgY1E.js.map} +1 -1
- package/public/assets/{chat-aRCk0pLJ.js → chat-C7XCmaZz.js} +2 -2
- package/public/assets/{chat-aRCk0pLJ.js.map → chat-C7XCmaZz.js.map} +1 -1
- package/public/assets/{formatted-body-impl-DZc7C4-2.js → formatted-body-impl-rLDPgdmJ.js} +2 -2
- package/public/assets/{formatted-body-impl-DZc7C4-2.js.map → formatted-body-impl-rLDPgdmJ.js.map} +1 -1
- package/public/assets/{index-DXEYz_JV.js → index-DGyeTLPg.js} +5 -5
- package/public/assets/index-DGyeTLPg.js.map +1 -0
- package/public/assets/{insights-FzTX2wNh.js → insights-C0dDL4El.js} +2 -2
- package/public/assets/{insights-FzTX2wNh.js.map → insights-C0dDL4El.js.map} +1 -1
- package/public/assets/{maintenance-C8Uw8XiI.js → maintenance-DkOkcEkb.js} +2 -2
- package/public/assets/{maintenance-C8Uw8XiI.js.map → maintenance-DkOkcEkb.js.map} +1 -1
- package/public/assets/{managed-agents-DVbJ2hqh.js → managed-agents-C7Czinaz.js} +2 -2
- package/public/assets/{managed-agents-DVbJ2hqh.js.map → managed-agents-C7Czinaz.js.map} +1 -1
- package/public/assets/{markdown-preview-impl-C5D-B-9S.js → markdown-preview-impl-BZ7sigEc.js} +2 -2
- package/public/assets/{markdown-preview-impl-C5D-B-9S.js.map → markdown-preview-impl-BZ7sigEc.js.map} +1 -1
- package/public/assets/{memory-Bp-D_V80.js → memory-Bys-M7GK.js} +2 -2
- package/public/assets/{memory-Bp-D_V80.js.map → memory-Bys-M7GK.js.map} +1 -1
- package/public/assets/{messages-DE1QSeHI.js → messages-COSJ6trN.js} +2 -2
- package/public/assets/{messages-DE1QSeHI.js.map → messages-COSJ6trN.js.map} +1 -1
- package/public/assets/{orchestrators-B0u2lQfw.js → orchestrators-BjLl1Z0L.js} +2 -2
- package/public/assets/{orchestrators-B0u2lQfw.js.map → orchestrators-BjLl1Z0L.js.map} +1 -1
- package/public/assets/{overview-DpaEvvLT.js → overview-BmU7twic.js} +2 -2
- package/public/assets/{overview-DpaEvvLT.js.map → overview-BmU7twic.js.map} +1 -1
- package/public/assets/{pairs-DRp_qAu8.js → pairs-xclXjw-c.js} +2 -2
- package/public/assets/{pairs-DRp_qAu8.js.map → pairs-xclXjw-c.js.map} +1 -1
- package/public/assets/{prompt-settings-CNoBiymk.js → prompt-settings-CtQd0nz0.js} +2 -2
- package/public/assets/{prompt-settings-CNoBiymk.js.map → prompt-settings-CtQd0nz0.js.map} +1 -1
- package/public/assets/{registry-0A4QFMWC.js → registry-CDe6vjKI.js} +2 -2
- package/public/assets/{registry-0A4QFMWC.js.map → registry-CDe6vjKI.js.map} +1 -1
- package/public/assets/{security-C9P6yiQ3.js → security-C9mBNQMZ.js} +2 -2
- package/public/assets/{security-C9P6yiQ3.js.map → security-C9mBNQMZ.js.map} +1 -1
- package/public/assets/{select-B1PVUj6D.js → select-BJDKQk4q.js} +2 -2
- package/public/assets/{select-B1PVUj6D.js.map → select-BJDKQk4q.js.map} +1 -1
- package/public/assets/{settings-TnHlB9-t.js → settings-BmZjWJkI.js} +2 -2
- package/public/assets/{settings-TnHlB9-t.js.map → settings-BmZjWJkI.js.map} +1 -1
- package/public/assets/{tasks-1oK_ggFC.js → tasks-DNjZKSM2.js} +2 -2
- package/public/assets/tasks-DNjZKSM2.js.map +1 -0
- package/public/assets/{teams-BBb0ZYiF.js → teams-KNzmdolE.js} +2 -2
- package/public/assets/{teams-BBb0ZYiF.js.map → teams-KNzmdolE.js.map} +1 -1
- package/public/assets/{terminal-viewer-impl-DzvaRjkp.js → terminal-viewer-impl-DnFJDl9N.js} +2 -2
- package/public/assets/{terminal-viewer-impl-DzvaRjkp.js.map → terminal-viewer-impl-DnFJDl9N.js.map} +1 -1
- package/public/assets/{user-avatar-X1pfizLv.js → user-avatar-C8sbjQke.js} +2 -2
- package/public/assets/{user-avatar-X1pfizLv.js.map → user-avatar-C8sbjQke.js.map} +1 -1
- package/public/assets/{work-queue-DQMbkSD8.js → work-queue-DINvPVPn.js} +2 -2
- package/public/assets/{work-queue-DQMbkSD8.js.map → work-queue-DINvPVPn.js.map} +1 -1
- package/public/assets/{workspaces-BNLP4IlV.js → workspaces-CSNdUg15.js} +2 -2
- package/public/assets/{workspaces-BNLP4IlV.js.map → workspaces-CSNdUg15.js.map} +1 -1
- package/public/index.html +1 -1
- package/runner/plugins/claude/.claude-plugin/plugin.json +1 -1
- package/src/mcp-task-tools.ts +2 -1
- package/src/services/spawn-registration-watchdog.ts +5 -5
- package/src/services/task-done-when.ts +21 -0
- package/src/services/task-entity.ts +10 -1
- package/src/services/task-lifecycle.ts +9 -39
- package/public/assets/index-DXEYz_JV.js.map +0 -1
- 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
|
-
|
|
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
|
|
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
|
|
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)
|
|
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
|
|
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)
|
|
314
|
-
//
|
|
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
|
-
|
|
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(
|
|
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,
|