agent-relay-server 0.118.3 → 0.118.4
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
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"openapi": "3.1.0",
|
|
3
3
|
"info": {
|
|
4
4
|
"title": "Agent Relay API",
|
|
5
|
-
"version": "0.118.
|
|
5
|
+
"version": "0.118.4",
|
|
6
6
|
"description": "Real-time message bus for inter-agent communication. Agent-first: this spec is designed for machine consumption — agents can self-discover the full API surface via GET /api/spec.",
|
|
7
7
|
"license": {
|
|
8
8
|
"name": "MIT",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-relay-server",
|
|
3
|
-
"version": "0.118.
|
|
3
|
+
"version": "0.118.4",
|
|
4
4
|
"description": "Lightweight HTTP message relay for inter-agent communication across machines",
|
|
5
5
|
"module": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"CONTRIBUTING.md"
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"agent-relay-channels-host": "0.118.
|
|
39
|
+
"agent-relay-channels-host": "0.118.4",
|
|
40
40
|
"agent-relay-providers": "0.104.1",
|
|
41
41
|
"agent-relay-sdk": "0.2.101",
|
|
42
42
|
"ajv": "^8.20.0"
|
|
@@ -21,7 +21,6 @@
|
|
|
21
21
|
import {
|
|
22
22
|
createActivityEvent,
|
|
23
23
|
getActiveMergePause,
|
|
24
|
-
getAgent,
|
|
25
24
|
listOrchestrators,
|
|
26
25
|
listWorkspaces,
|
|
27
26
|
patchWorkspaceMetadata,
|
|
@@ -36,10 +35,10 @@ import { renderTemplate } from "./prompt-resolver";
|
|
|
36
35
|
import { isAwaitingReviewerApproval } from "./reviewer-pipeline";
|
|
37
36
|
import { emitWorkspaceTaskSemanticNotifications } from "./services/task-lifecycle";
|
|
38
37
|
import { ensureRepoSteward } from "./steward";
|
|
39
|
-
import { requestWorkspaceMerge } from "./workspace-merge";
|
|
38
|
+
import { requestWorkspaceMerge, workspaceMergeOrchestrators } from "./workspace-merge";
|
|
40
39
|
import { workspaceActiveClaim } from "./workspace-claim";
|
|
41
40
|
import { DIRTY_WORKTREE_LAND_SKIP_REASON, READY_TO_LAND_STATUSES } from "./workspace-phase";
|
|
42
|
-
import { fetchHostMergePreview
|
|
41
|
+
import { fetchHostMergePreview } from "./workspace-probe";
|
|
43
42
|
import type { WorkspaceAutoMergePolicy, WorkspaceLandingPolicy, WorkspaceMergePreview, WorkspaceMergeStrategy, WorkspaceRecord } from "./types";
|
|
44
43
|
import { STEWARD_WAKE_COOLDOWN_MS, stewardFallbackTarget, workspaceAutoMergeEnabled } from "./config";
|
|
45
44
|
|
|
@@ -263,25 +262,11 @@ function emptyLandSweep(): LandSweepResult {
|
|
|
263
262
|
return { scanned: 0, merged: [], heldByLease: [], heldByClaim: [], heldByPause: [], heldAwaitingApproval: [], previewUnavailable: [], backedOff: [], leftForSteward: [], wokeStewards: [] };
|
|
264
263
|
}
|
|
265
264
|
|
|
266
|
-
function workspaceOrchestrators(
|
|
267
|
-
ws: WorkspaceRecord,
|
|
268
|
-
orchestrators: ReturnType<typeof listOrchestrators>,
|
|
269
|
-
): ReturnType<typeof listOrchestrators> {
|
|
270
|
-
const candidates = orchestrators.filter((candidate) => workspacePathWithinBase(ws.sourceCwd, candidate.baseDir));
|
|
271
|
-
const ownerMachine = ws.ownerAgentId ? getAgent(ws.ownerAgentId)?.machine : undefined;
|
|
272
|
-
if (!ownerMachine) return candidates;
|
|
273
|
-
return [...candidates].sort((a, b) => {
|
|
274
|
-
const aOwner = a.hostname === ownerMachine || a.id === ownerMachine ? 0 : 1;
|
|
275
|
-
const bOwner = b.hostname === ownerMachine || b.id === ownerMachine ? 0 : 1;
|
|
276
|
-
return aOwner - bOwner;
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
|
|
280
265
|
async function fetchFirstUsableMergePreview(
|
|
281
266
|
ws: WorkspaceRecord,
|
|
282
267
|
orchestrators: ReturnType<typeof listOrchestrators>,
|
|
283
268
|
): Promise<WorkspaceMergePreview | { available: false } | null> {
|
|
284
|
-
for (const orch of
|
|
269
|
+
for (const orch of workspaceMergeOrchestrators(ws, orchestrators)) {
|
|
285
270
|
if (!orch.apiUrl) continue;
|
|
286
271
|
const preview = await fetchHostMergePreview(orch.apiUrl, ws);
|
|
287
272
|
if (!preview || (preview as { available?: false }).available === false) continue;
|
package/src/workspace-merge.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
import { getLandingConfig } from "./config-store";
|
|
15
15
|
import type {
|
|
16
16
|
Command,
|
|
17
|
+
Orchestrator,
|
|
17
18
|
WorkspaceAutoMergePolicy,
|
|
18
19
|
WorkspaceLandingPolicy,
|
|
19
20
|
WorkspaceMergeStrategy,
|
|
@@ -142,6 +143,39 @@ export function withOwnerOnline<T extends { ownerAgentId?: string }>(workspace:
|
|
|
142
143
|
return { ...workspace, ownerOnline: isOwnerAlive(workspace.ownerAgentId) };
|
|
143
144
|
}
|
|
144
145
|
|
|
146
|
+
function workspaceOwnerHostKeys(workspace: WorkspaceRecord): Set<string> {
|
|
147
|
+
const keys = new Set<string>();
|
|
148
|
+
const owner = workspace.ownerAgentId ? getAgent(workspace.ownerAgentId) : undefined;
|
|
149
|
+
if (!owner) return keys;
|
|
150
|
+
if (owner.machine) keys.add(owner.machine);
|
|
151
|
+
const meta = isRecord(owner.meta) ? owner.meta : undefined;
|
|
152
|
+
const orchestratorId = meta ? stringValue(meta.orchestratorId) : undefined;
|
|
153
|
+
if (orchestratorId) keys.add(orchestratorId);
|
|
154
|
+
if (orchestratorId) keys.add(`orchestrator-${orchestratorId}`);
|
|
155
|
+
return keys;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Online hosts that can operate on a workspace, ordered by ownership first.
|
|
160
|
+
* The fallback to any matching host is intentional for manual recovery, but the
|
|
161
|
+
* owner host must win when known: isolated worktree paths are host-local.
|
|
162
|
+
*/
|
|
163
|
+
export function workspaceMergeOrchestrators(
|
|
164
|
+
workspace: WorkspaceRecord,
|
|
165
|
+
orchestrators: Orchestrator[] = listOrchestrators(),
|
|
166
|
+
): Orchestrator[] {
|
|
167
|
+
const candidates = orchestrators.filter((candidate) =>
|
|
168
|
+
candidate.status === "online" && isPathWithinBase(workspace.sourceCwd, candidate.baseDir),
|
|
169
|
+
);
|
|
170
|
+
const ownerKeys = workspaceOwnerHostKeys(workspace);
|
|
171
|
+
if (ownerKeys.size === 0) return candidates;
|
|
172
|
+
return [...candidates].sort((a, b) => {
|
|
173
|
+
const aOwner = ownerKeys.has(a.id) || ownerKeys.has(a.hostname) || ownerKeys.has(a.agentId) ? 0 : 1;
|
|
174
|
+
const bOwner = ownerKeys.has(b.id) || ownerKeys.has(b.hostname) || ownerKeys.has(b.agentId) ? 0 : 1;
|
|
175
|
+
return aOwner - bOwner;
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
|
|
145
179
|
/**
|
|
146
180
|
* Resolved cwds of every live (non-offline) agent, including the workspace sourceCwd
|
|
147
181
|
* they were spawned in. The third "nobody is using it" signal the reaper needs: a
|
|
@@ -206,9 +240,7 @@ export function requestWorkspaceMerge(workspace: WorkspaceRecord, opts: RequestW
|
|
|
206
240
|
}
|
|
207
241
|
try {
|
|
208
242
|
// Merge needs a live host: rebasing against a stale base later is unsafe.
|
|
209
|
-
const onlineOwner =
|
|
210
|
-
(candidate) => candidate.status === "online" && isPathWithinBase(workspace.sourceCwd, candidate.baseDir),
|
|
211
|
-
);
|
|
243
|
+
const onlineOwner = workspaceMergeOrchestrators(workspace)[0];
|
|
212
244
|
if (!onlineOwner) {
|
|
213
245
|
releaseMergeLease({ repoRoot: workspace.repoRoot, workspaceId: workspace.id });
|
|
214
246
|
return { ok: false, status: 409, error: "no online orchestrator available for workspace merge" };
|