agent-relay-orchestrator 0.19.3 → 0.21.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.
Files changed (2) hide show
  1. package/package.json +2 -2
  2. package/src/relay.ts +19 -30
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay-orchestrator",
3
- "version": "0.19.3",
3
+ "version": "0.21.0",
4
4
  "description": "Agent Relay orchestrator — manages agent lifecycle across hosts",
5
5
  "type": "module",
6
6
  "bin": {
@@ -16,7 +16,7 @@
16
16
  "test": "bun test"
17
17
  },
18
18
  "dependencies": {
19
- "agent-relay-sdk": "0.2.10"
19
+ "agent-relay-sdk": "0.2.12"
20
20
  },
21
21
  "devDependencies": {
22
22
  "@types/bun": "latest",
package/src/relay.ts CHANGED
@@ -3,7 +3,7 @@ import type { ProviderProbeCache } from "./provider-probe";
3
3
  import { detectSelfSupervision } from "./self-supervision";
4
4
  import { GIT_SHA, ORCHESTRATOR_PROTOCOL_VERSION, VERSION, runtimeMetadata } from "./version";
5
5
  import type { WorkspaceMetadata, WorkspaceMode, ManagedSessionExitDiagnostics as SdkManagedSessionExitDiagnostics } from "agent-relay-sdk";
6
- import { RELAY_TOKEN_HEADER } from "agent-relay-sdk";
6
+ import { ReconnectionManager, RelayHttpClient } from "agent-relay-sdk";
7
7
 
8
8
  export interface RelayClient {
9
9
  register(): Promise<void>;
@@ -58,10 +58,11 @@ export interface RelayCommand {
58
58
  status: string;
59
59
  }
60
60
 
61
- const BACKOFF_SCHEDULE_MS = [
62
- 30_000, 60_000, 120_000, 240_000, 480_000, 960_000,
63
- ];
64
- const MAX_BACKOFF_MS = 3_600_000; // 1 hour
61
+ // Reconnect backoff: 30s → 1m → 2m → … capped at 1h, with jitter. Replaces the
62
+ // former fixed BACKOFF_SCHEDULE_MS staircase — see SDK ReconnectionManager.
63
+ const RECONNECT_INITIAL_MS = 30_000;
64
+ const RECONNECT_MAX_MS = 3_600_000; // 1 hour
65
+ const RECONNECT_JITTER_MS = 1_000;
65
66
 
66
67
  export function buildRegistrationMeta(
67
68
  config: Pick<OrchestratorConfig, "tmuxPrefix">,
@@ -88,26 +89,17 @@ export function createRelayClient(config: OrchestratorConfig, probeCache: Provid
88
89
  const agentId = `orchestrator-${config.id}`;
89
90
  let heartbeatTimer: Timer | null = null;
90
91
  let connected = false;
91
- let backoffIndex = 0;
92
+ const reconnectMgr = new ReconnectionManager({ initialMs: RECONNECT_INITIAL_MS, maxMs: RECONNECT_MAX_MS, jitterMs: RECONNECT_JITTER_MS });
92
93
  let cursorFloor = 0;
93
94
  let apiUrl: string | undefined;
94
- let runtimeToken: string | undefined;
95
-
96
- function headers(): Record<string, string> {
97
- const h: Record<string, string> = { "Content-Type": "application/json" };
98
- const token = runtimeToken ?? config.token;
99
- if (token) h[RELAY_TOKEN_HEADER] = token;
100
- return h;
101
- }
95
+ // Shared transport: auth-header injection + timeout + typed errors. The
96
+ // orchestrator-control endpoints have no typed method, so route them through
97
+ // the generic request() escape hatch. setToken() swaps in the runtime token
98
+ // the relay mints at registration.
99
+ const http = new RelayHttpClient({ baseUrl: config.relayUrl, token: config.token });
102
100
 
103
101
  async function apiCall(method: string, path: string, body?: unknown): Promise<Response> {
104
- const url = `${config.relayUrl}/api${path}`;
105
- const res = await fetch(url, {
106
- method,
107
- headers: headers(),
108
- body: body ? JSON.stringify(body) : undefined,
109
- });
110
- return res;
102
+ return http.request(method, `/api${path}`, body);
111
103
  }
112
104
 
113
105
  async function register(): Promise<void> {
@@ -135,9 +127,9 @@ export function createRelayClient(config: OrchestratorConfig, probeCache: Provid
135
127
  throw new Error(`Failed to register orchestrator: ${res.status} ${err}`);
136
128
  }
137
129
  const registered = await res.json().catch(() => null) as { runtimeToken?: { token?: string } } | null;
138
- if (registered?.runtimeToken?.token) runtimeToken = registered.runtimeToken.token;
130
+ if (registered?.runtimeToken?.token) http.setToken(registered.runtimeToken.token);
139
131
  connected = true;
140
- backoffIndex = 0;
132
+ reconnectMgr.reset();
141
133
 
142
134
  // Bootstrap message cursor
143
135
  const cursor = await apiCall("GET", "/messages/cursor");
@@ -168,7 +160,7 @@ export function createRelayClient(config: OrchestratorConfig, probeCache: Provid
168
160
  if (!connected) {
169
161
  console.error("[orchestrator] Reconnected to relay");
170
162
  connected = true;
171
- backoffIndex = 0;
163
+ reconnectMgr.reset();
172
164
  }
173
165
  } catch (err) {
174
166
  if (connected) {
@@ -180,12 +172,9 @@ export function createRelayClient(config: OrchestratorConfig, probeCache: Provid
180
172
  }
181
173
 
182
174
  async function reconnect(): Promise<void> {
183
- const delay = backoffIndex < BACKOFF_SCHEDULE_MS.length
184
- ? BACKOFF_SCHEDULE_MS[backoffIndex]!
185
- : MAX_BACKOFF_MS;
186
- backoffIndex = Math.min(backoffIndex + 1, BACKOFF_SCHEDULE_MS.length);
187
- console.error(`[orchestrator] Reconnecting in ${Math.round(delay / 1000)}s...`);
188
- await new Promise((resolve) => setTimeout(resolve, delay));
175
+ const delayMs = reconnectMgr.nextDelay();
176
+ console.error(`[orchestrator] Reconnecting in ${Math.round(delayMs / 1000)}s...`);
177
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
189
178
  try {
190
179
  await register();
191
180
  } catch {