agent-relay-server 0.23.0 → 0.25.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/package.json +2 -2
- package/public/index.html +84 -63
- package/runner/src/adapter.ts +10 -0
- package/src/branch-landed.ts +77 -0
- package/src/config-store.ts +31 -0
- package/src/connectors.ts +37 -1
- package/src/lifecycle-manager.ts +1 -0
- package/src/maintenance.ts +16 -20
- package/src/managed-policy.ts +1 -0
- package/src/mcp.ts +9 -5
- package/src/memory-broker-base.ts +161 -0
- package/src/memory-command-broker.ts +10 -119
- package/src/memory-http-broker.ts +11 -141
- package/src/notify.ts +31 -0
- package/src/routes.ts +26 -9
- package/src/workspace-phase.ts +51 -3
package/src/workspace-phase.ts
CHANGED
|
@@ -21,6 +21,26 @@ import type { WorkspaceRecord, WorkspaceStatus } from "./types";
|
|
|
21
21
|
// initialize primer (don't brief an agent on a dead workspace). Was duplicated.
|
|
22
22
|
export const TERMINAL_WORKSPACE_STATUSES = new Set<WorkspaceStatus>(["cleaned", "merged", "abandoned"]);
|
|
23
23
|
|
|
24
|
+
// The "handed off, waiting to land" statuses — an agent has finished and the
|
|
25
|
+
// auto-merge-back is responsible for getting the branch onto base. SINGLE HOME:
|
|
26
|
+
// the auto-land consumer (maintenance `autoMergeCleanFastForwards`) and the
|
|
27
|
+
// strand-escalation set MUST both derive from this. They drifted before (#242):
|
|
28
|
+
// `relay_workspace_ready` sets `ready`, but the consumer only scanned
|
|
29
|
+
// `review_requested`, so a clean `ready` worktree was never a merge candidate and
|
|
30
|
+
// parked forever while this phase view kept reporting "healthy, wait." Producer
|
|
31
|
+
// and consumer now read the same set so a `ready` can never silently fall out of
|
|
32
|
+
// the land queue again. (`review_requested` is the same healthy hand-off state —
|
|
33
|
+
// it's also where a failed auto-merge lands for a retry, see routes.ts.)
|
|
34
|
+
export const READY_TO_LAND_STATUSES = new Set<WorkspaceStatus>(["ready", "review_requested"]);
|
|
35
|
+
|
|
36
|
+
// How long a workspace may sit in a ready-to-land status before the directive
|
|
37
|
+
// projection stops saying "healthy, just wait" and surfaces it as needs-attention
|
|
38
|
+
// (#242 watchdog). A clean auto-merge runs ~every 2 min, so a handful of missed
|
|
39
|
+
// sweeps means something is wrong (wrong status filter, no online orchestrator,
|
|
40
|
+
// an unpushed branch, a wedged steward) and the agent/human should be told —
|
|
41
|
+
// instead of the old behavior where it looked healthy for 90 minutes.
|
|
42
|
+
export const LAND_PENDING_STALL_MS = 15 * 60 * 1000;
|
|
43
|
+
|
|
24
44
|
export type WorkspacePhase =
|
|
25
45
|
| "working" // active — your turn: commit, then mark ready
|
|
26
46
|
| "land-pending" // ready | review_requested — handed off; auto-merge will land it
|
|
@@ -66,7 +86,17 @@ const READY_ACTION: WorkspaceNextAction = {
|
|
|
66
86
|
// Map every WorkspaceStatus to the branch agent's mental model. Statuses that
|
|
67
87
|
// look scary but are healthy (review_requested, conflict) carry actionNeeded:false
|
|
68
88
|
// and an explicit "not your job" hint.
|
|
69
|
-
|
|
89
|
+
//
|
|
90
|
+
// `opts.now` (defaults to wall-clock) drives the #242 stall watchdog: a workspace
|
|
91
|
+
// pending-to-land past LAND_PENDING_STALL_MS flips from the "healthy, wait" view
|
|
92
|
+
// to needs-attention with a real blocker, so the status surface the agent polls
|
|
93
|
+
// can't keep masking a stuck land. The clock is `readyAt` (set once when the agent
|
|
94
|
+
// marks ready, immune to the heartbeat `updated_at` bump) — not `updatedAt`, which
|
|
95
|
+
// keeps ticking on every heartbeat and made the stall look fresh forever.
|
|
96
|
+
export function describeWorkspacePhase(
|
|
97
|
+
workspace: Pick<WorkspaceRecord, "status" | "branch" | "stewardAgentId" | "readyAt">,
|
|
98
|
+
opts: { now?: number; stallMs?: number } = {},
|
|
99
|
+
): WorkspacePhaseView {
|
|
70
100
|
switch (workspace.status) {
|
|
71
101
|
case "active":
|
|
72
102
|
return {
|
|
@@ -78,10 +108,27 @@ export function describeWorkspacePhase(workspace: Pick<WorkspaceRecord, "status"
|
|
|
78
108
|
blockers: [],
|
|
79
109
|
};
|
|
80
110
|
case "ready":
|
|
81
|
-
case "review_requested":
|
|
111
|
+
case "review_requested": {
|
|
82
112
|
// The #235 crux: these are the SAME healthy "handed off, waiting" state.
|
|
83
113
|
// `review_requested` reads like an escalation but is the normal post-ready
|
|
84
114
|
// node; an absent steward is the healthy case, not a stall.
|
|
115
|
+
const now = opts.now ?? Date.now();
|
|
116
|
+
const stallMs = opts.stallMs ?? LAND_PENDING_STALL_MS;
|
|
117
|
+
const pendingMs = typeof workspace.readyAt === "number" ? now - workspace.readyAt : undefined;
|
|
118
|
+
// #242 watchdog: past the bound this is no longer "healthy, wait." Surface it
|
|
119
|
+
// as needs-attention with a real blocker instead of the anti-panic view, so
|
|
120
|
+
// the agent (and the dashboard) stop reporting a wedged land as healthy.
|
|
121
|
+
if (pendingMs !== undefined && pendingMs > stallMs) {
|
|
122
|
+
const mins = Math.round(pendingMs / 60_000);
|
|
123
|
+
return {
|
|
124
|
+
phase: "land-pending",
|
|
125
|
+
headline: `Stalled — handed off ${mins} min ago but still hasn't landed. A clean auto-merge runs every ~2 min, so this is past the healthy window and likely stuck (no online orchestrator, an unpushed branch, or a wedged merge/steward).`,
|
|
126
|
+
hint: "Do NOT merge, push, rebase, or touch the main checkout yourself. Flag this to a human or the repo steward — the auto-merge/steward path isn't progressing and needs attention.",
|
|
127
|
+
actionNeeded: true,
|
|
128
|
+
nextActions: [WAIT_ACTION],
|
|
129
|
+
blockers: [`pending land for ~${mins} min with no progress — auto-merge/steward isn't landing it`],
|
|
130
|
+
};
|
|
131
|
+
}
|
|
85
132
|
return {
|
|
86
133
|
phase: "land-pending",
|
|
87
134
|
headline: "Handed off — waiting for the auto-merge to land your branch. This is the normal, healthy post-ready state (not an escalation).",
|
|
@@ -90,6 +137,7 @@ export function describeWorkspacePhase(workspace: Pick<WorkspaceRecord, "status"
|
|
|
90
137
|
nextActions: [WAIT_ACTION],
|
|
91
138
|
blockers: [],
|
|
92
139
|
};
|
|
140
|
+
}
|
|
93
141
|
case "merge_planned":
|
|
94
142
|
return {
|
|
95
143
|
phase: "landing",
|
|
@@ -157,7 +205,7 @@ export function worktreeMcpInstructions(workspace: Pick<WorkspaceRecord, "branch
|
|
|
157
205
|
`You are in an isolated git worktree on branch ${branch}, based on ${base} — NOT the main checkout. ${base} moves under you as other agents land in parallel; that's expected.`,
|
|
158
206
|
"Changes reach the base via: commit your work, then call `relay_workspace_ready`. Relay rebases onto the latest base, lands, and pushes for you.",
|
|
159
207
|
"Do NOT push, rebase, merge, resolve conflicts, or `cd` into the main checkout — Relay (and a steward, spawned only if a clean auto-merge isn't possible) own all of that.",
|
|
160
|
-
"After `ready` the status is `
|
|
208
|
+
"After `ready` the status is `ready` (a normal, healthy hand-off state, not a stall). Call `relay_workspace_status` with `wait:true` to block until your branch lands; you'll then continue on a fresh rebased branch (name gains a `--N` suffix).",
|
|
161
209
|
"Call `relay_workspace_status` anytime to see where you are and the exact next step.",
|
|
162
210
|
].join("\n");
|
|
163
211
|
}
|