agent-relay-server 0.118.1 → 0.118.3

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.1",
5
+ "version": "0.118.3",
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.1",
3
+ "version": "0.118.3",
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.1",
39
+ "agent-relay-channels-host": "0.118.3",
40
40
  "agent-relay-providers": "0.104.1",
41
41
  "agent-relay-sdk": "0.2.101",
42
42
  "ajv": "^8.20.0"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agent-relay-runner",
3
3
  "description": "Thin Agent Relay runner bridge for Claude Code",
4
- "version": "0.118.1",
4
+ "version": "0.118.3",
5
5
  "agentRelayContracts": {
6
6
  "providerPluginProtocol": 1
7
7
  }
@@ -21,6 +21,7 @@
21
21
  import {
22
22
  createActivityEvent,
23
23
  getActiveMergePause,
24
+ getAgent,
24
25
  listOrchestrators,
25
26
  listWorkspaces,
26
27
  patchWorkspaceMetadata,
@@ -252,13 +253,43 @@ interface LandSweepResult {
252
253
  heldByClaim: string[];
253
254
  heldByPause: string[];
254
255
  heldAwaitingApproval: string[];
256
+ previewUnavailable: string[];
255
257
  backedOff: string[];
256
258
  leftForSteward: string[];
257
259
  wokeStewards: string[];
258
260
  }
259
261
 
260
262
  function emptyLandSweep(): LandSweepResult {
261
- return { scanned: 0, merged: [], heldByLease: [], heldByClaim: [], heldByPause: [], heldAwaitingApproval: [], backedOff: [], leftForSteward: [], wokeStewards: [] };
263
+ return { scanned: 0, merged: [], heldByLease: [], heldByClaim: [], heldByPause: [], heldAwaitingApproval: [], previewUnavailable: [], backedOff: [], leftForSteward: [], wokeStewards: [] };
264
+ }
265
+
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
+ async function fetchFirstUsableMergePreview(
281
+ ws: WorkspaceRecord,
282
+ orchestrators: ReturnType<typeof listOrchestrators>,
283
+ ): Promise<WorkspaceMergePreview | { available: false } | null> {
284
+ for (const orch of workspaceOrchestrators(ws, orchestrators)) {
285
+ if (!orch.apiUrl) continue;
286
+ const preview = await fetchHostMergePreview(orch.apiUrl, ws);
287
+ if (!preview || (preview as { available?: false }).available === false) continue;
288
+ const p = preview as WorkspaceMergePreview;
289
+ if (p.error || p.missing) continue;
290
+ return p;
291
+ }
292
+ return null;
262
293
  }
263
294
 
264
295
  // Deterministic auto-land (Layer 0, #167/#207/#242): land conflict-free ready work
@@ -269,7 +300,7 @@ async function landCandidates(
269
300
  orchestrators: ReturnType<typeof listOrchestrators>,
270
301
  ): Promise<LandSweepResult> {
271
302
  const stewardEnabled = getStewardConfig().enabled;
272
- const merged: string[] = [];
303
+ const merged: string[] = [], previewUnavailable: string[] = [];
273
304
  const heldByLease: string[] = [], heldByClaim: string[] = [], heldByPause: string[] = [], heldAwaitingApproval: string[] = [], backedOff: string[] = [], leftForSteward: string[] = [], wokeStewards: string[] = [];
274
305
 
275
306
  for (const ws of candidates) {
@@ -286,12 +317,12 @@ async function landCandidates(
286
317
  escalateNoProgressMerge(ws);
287
318
  continue;
288
319
  }
289
- const orch = orchestrators.find((candidate) => workspacePathWithinBase(ws.sourceCwd, candidate.baseDir));
290
- if (!orch?.apiUrl) continue;
291
- const preview = await fetchHostMergePreview(orch.apiUrl, ws);
292
- if (!preview || (preview as { available?: false }).available === false) continue;
320
+ const preview = await fetchFirstUsableMergePreview(ws, orchestrators);
321
+ if (!preview) {
322
+ previewUnavailable.push(ws.id);
323
+ continue;
324
+ }
293
325
  const p = preview as WorkspaceMergePreview;
294
- if (p.error || p.missing) continue;
295
326
  if (p.reason === DIRTY_WORKTREE_LAND_SKIP_REASON) {
296
327
  patchWorkspaceMetadata(ws.id, { lastLandSkipReason: p.reason, lastLandSkipAt: Date.now() });
297
328
  } else if (ws.metadata.lastLandSkipReason === DIRTY_WORKTREE_LAND_SKIP_REASON) {
@@ -354,7 +385,7 @@ async function landCandidates(
354
385
  });
355
386
  }
356
387
 
357
- return { scanned: candidates.length, merged, heldByLease, heldByClaim, heldByPause, heldAwaitingApproval, backedOff, leftForSteward, wokeStewards };
388
+ return { scanned: candidates.length, merged, heldByLease, heldByClaim, heldByPause, heldAwaitingApproval, previewUnavailable, backedOff, leftForSteward, wokeStewards };
358
389
  }
359
390
 
360
391
  // Coalesced, fire-and-forget per-repo land trigger (see module header). A run already