agent-relay-server 0.29.0 → 0.30.1
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/package.json +2 -2
- package/public/index.html +257 -86
- package/runner/src/adapter.ts +1 -1
- package/runner/src/config.ts +1 -3
- package/src/agent-branch-state.ts +36 -0
- package/src/agent-ref.ts +18 -1
- package/src/branch-landed.ts +16 -2
- package/src/bus.ts +3 -21
- package/src/cli.ts +5 -1
- package/src/db.ts +46 -9
- package/src/maintenance.ts +16 -1
- package/src/mcp.ts +3 -2
- package/src/routes.ts +9 -3
- package/src/sse.ts +15 -27
- package/src/upgrade.ts +15 -4
- package/src/workspace-phase.ts +69 -1
package/src/workspace-phase.ts
CHANGED
|
@@ -15,13 +15,23 @@
|
|
|
15
15
|
// anti-panic signal.
|
|
16
16
|
|
|
17
17
|
import { TERMINAL_WORKSPACE_STATUS_VALUES } from "agent-relay-sdk";
|
|
18
|
-
import type { WorkspaceRecord, WorkspaceStatus } from "./types";
|
|
18
|
+
import type { BranchState, WorkspaceRecord, WorkspaceStatus } from "./types";
|
|
19
|
+
import { workspaceActiveClaim } from "./workspace-claim";
|
|
19
20
|
|
|
20
21
|
// Statuses where the worktree's lifecycle is over — landed or torn down. Single
|
|
21
22
|
// home; imported by maintenance (stale reap), routes (orphan scan), and the MCP
|
|
22
23
|
// initialize primer (don't brief an agent on a dead workspace). Was duplicated.
|
|
23
24
|
export const TERMINAL_WORKSPACE_STATUSES = new Set<WorkspaceStatus>(TERMINAL_WORKSPACE_STATUS_VALUES);
|
|
24
25
|
|
|
26
|
+
// The "this is the worktree the agent is actively branch-working in" predicate:
|
|
27
|
+
// isolated mode and not yet landed/torn down. SINGLE HOME — the MCP owner-workspace
|
|
28
|
+
// resolver, the db owner lookup, and the #236 badge enrichment all mean the same
|
|
29
|
+
// thing; they drifted into private copies before. listWorkspaces is ORDER BY
|
|
30
|
+
// updated_at DESC, so the first match per owner is the most recent live worktree.
|
|
31
|
+
export function isLiveIsolatedWorkspace(ws: Pick<WorkspaceRecord, "mode" | "status">): boolean {
|
|
32
|
+
return ws.mode === "isolated" && !TERMINAL_WORKSPACE_STATUSES.has(ws.status);
|
|
33
|
+
}
|
|
34
|
+
|
|
25
35
|
// The "handed off, waiting to land" statuses — an agent has finished and the
|
|
26
36
|
// auto-merge-back is responsible for getting the branch onto base. SINGLE HOME:
|
|
27
37
|
// the auto-land consumer (maintenance `autoMergeCleanFastForwards`) and the
|
|
@@ -209,6 +219,64 @@ export function describeWorkspacePhase(
|
|
|
209
219
|
}
|
|
210
220
|
}
|
|
211
221
|
|
|
222
|
+
function metaNumber(meta: Record<string, unknown> | undefined, key: string): number | undefined {
|
|
223
|
+
const v = meta?.[key];
|
|
224
|
+
return typeof v === "number" ? v : undefined;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// THE branch-state projection for the human (#236) — the sibling of
|
|
228
|
+
// describeWorkspacePhase, which targets the agent. Maps a workspace to one compact
|
|
229
|
+
// state for the agent-card/chat badge, so the recurring "does this agent have
|
|
230
|
+
// unlanded work, and where is it in the merge-back" question is answered at a
|
|
231
|
+
// glance. SINGLE HOME (server-side); the dashboard never recomputes it.
|
|
232
|
+
//
|
|
233
|
+
// idle/changes need the worktree's ahead/dirty counts, which the relay isn't in the
|
|
234
|
+
// git path to know live — the ~2 min conflict scan stashes them in metadata
|
|
235
|
+
// (`gitAhead`/`gitDirtyCount`). Until the first scan they're absent, so an active
|
|
236
|
+
// worktree shows the optimistic `changes` (the high-value "mark ready" affordance);
|
|
237
|
+
// the scan then settles it to `idle` when genuinely empty (#236 v1 option a).
|
|
238
|
+
//
|
|
239
|
+
// Returns undefined for non-branch / torn-down workspaces (no badge).
|
|
240
|
+
export function deriveBranchState(
|
|
241
|
+
workspace: Pick<WorkspaceRecord, "status" | "metadata" | "readyAt"> | null | undefined,
|
|
242
|
+
opts: { now?: number; stallMs?: number } = {},
|
|
243
|
+
): BranchState | undefined {
|
|
244
|
+
if (!workspace) return undefined;
|
|
245
|
+
const now = opts.now ?? Date.now();
|
|
246
|
+
switch (workspace.status) {
|
|
247
|
+
case "active": {
|
|
248
|
+
const meta = workspace.metadata as Record<string, unknown> | undefined;
|
|
249
|
+
const ahead = metaNumber(meta, "gitAhead");
|
|
250
|
+
const dirty = metaNumber(meta, "gitDirtyCount");
|
|
251
|
+
if (ahead === undefined && dirty === undefined) return "changes";
|
|
252
|
+
return (ahead ?? 0) > 0 || (dirty ?? 0) > 0 ? "changes" : "idle";
|
|
253
|
+
}
|
|
254
|
+
case "ready":
|
|
255
|
+
case "review_requested":
|
|
256
|
+
// Handed off, waiting for the auto-merge. A steward holding the claim means a
|
|
257
|
+
// human-out-of-loop reconciliation is underway → 🟠, otherwise the robot has it → 🔵.
|
|
258
|
+
return workspaceActiveClaim(workspace, now)?.purpose === "steward" ? "steward" : "ready";
|
|
259
|
+
case "merge_planned":
|
|
260
|
+
// Merge dispatched / under reconciliation — robot-or-steward, either way not the human's move.
|
|
261
|
+
return "steward";
|
|
262
|
+
case "conflict": {
|
|
263
|
+
// Held by a steward → reconciling (🟠). Past the stall window with nobody
|
|
264
|
+
// holding it → the steward path isn't progressing → escalate to the human (🔴).
|
|
265
|
+
if (workspaceActiveClaim(workspace, now)?.purpose === "steward") return "steward";
|
|
266
|
+
const stallMs = opts.stallMs ?? LAND_PENDING_STALL_MS;
|
|
267
|
+
const since = typeof workspace.readyAt === "number" ? now - workspace.readyAt : undefined;
|
|
268
|
+
if (since !== undefined && since > stallMs) return "blocked";
|
|
269
|
+
return "steward";
|
|
270
|
+
}
|
|
271
|
+
case "merged":
|
|
272
|
+
case "abandoned":
|
|
273
|
+
case "cleanup_requested":
|
|
274
|
+
case "cleaned":
|
|
275
|
+
// Terminal/torn down: the owner lookup filters these out, so the badge clears.
|
|
276
|
+
return undefined;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
212
280
|
// Plain-language contract printed/returned right when an agent marks a workspace
|
|
213
281
|
// ready, so the whole "what happens next" is stated up front instead of being
|
|
214
282
|
// decoded from status enums over the following minutes (#235).
|