agent-relay 2.3.2 → 2.3.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/dist/index.cjs +1 -1
- package/package.json +18 -18
- package/packages/acp-bridge/package.json +2 -2
- package/packages/api-types/package.json +1 -1
- package/packages/benchmark/package.json +5 -5
- package/packages/bridge/package.json +7 -7
- package/packages/cli-tester/package.json +1 -1
- package/packages/config/dist/cloud-config.d.ts +1 -1
- package/packages/config/dist/cloud-config.d.ts.map +1 -1
- package/packages/config/dist/cloud-config.js.map +1 -1
- package/packages/config/dist/schemas.d.ts +5 -5
- package/packages/config/dist/schemas.js +1 -1
- package/packages/config/dist/schemas.js.map +1 -1
- package/packages/config/package.json +2 -2
- package/packages/config/src/cloud-config.ts +2 -2
- package/packages/config/src/schemas.test.ts +48 -0
- package/packages/config/src/schemas.ts +1 -1
- package/packages/continuity/package.json +2 -2
- package/packages/daemon/package.json +12 -12
- package/packages/hooks/package.json +4 -4
- package/packages/mcp/package.json +5 -5
- package/packages/memory/package.json +2 -2
- package/packages/policy/package.json +2 -2
- package/packages/protocol/package.json +1 -1
- package/packages/resiliency/package.json +1 -1
- package/packages/sdk/package.json +3 -3
- package/packages/sdk-ts/dist/__tests__/facade.test.d.ts +2 -0
- package/packages/sdk-ts/dist/__tests__/facade.test.d.ts.map +1 -0
- package/packages/sdk-ts/dist/__tests__/facade.test.js +257 -0
- package/packages/sdk-ts/dist/__tests__/facade.test.js.map +1 -0
- package/packages/sdk-ts/dist/__tests__/unit.test.d.ts +2 -0
- package/packages/sdk-ts/dist/__tests__/unit.test.d.ts.map +1 -0
- package/packages/sdk-ts/dist/__tests__/unit.test.js +124 -0
- package/packages/sdk-ts/dist/__tests__/unit.test.js.map +1 -0
- package/packages/sdk-ts/dist/client.d.ts +2 -0
- package/packages/sdk-ts/dist/client.d.ts.map +1 -1
- package/packages/sdk-ts/dist/client.js +2 -0
- package/packages/sdk-ts/dist/client.js.map +1 -1
- package/packages/sdk-ts/dist/protocol.d.ts +1 -0
- package/packages/sdk-ts/dist/protocol.d.ts.map +1 -1
- package/packages/sdk-ts/dist/relay.d.ts +44 -0
- package/packages/sdk-ts/dist/relay.d.ts.map +1 -1
- package/packages/sdk-ts/dist/relay.js +89 -11
- package/packages/sdk-ts/dist/relay.js.map +1 -1
- package/packages/sdk-ts/dist/relaycast.js +2 -2
- package/packages/sdk-ts/dist/relaycast.js.map +1 -1
- package/packages/sdk-ts/package.json +3 -3
- package/packages/sdk-ts/src/__tests__/facade.test.ts +296 -0
- package/packages/sdk-ts/src/__tests__/unit.test.ts +152 -0
- package/packages/sdk-ts/src/client.ts +4 -0
- package/packages/sdk-ts/src/protocol.ts +1 -1
- package/packages/sdk-ts/src/relay.ts +112 -11
- package/packages/sdk-ts/src/relaycast.ts +2 -2
- package/packages/spawner/package.json +1 -1
- package/packages/state/package.json +1 -1
- package/packages/storage/package.json +2 -2
- package/packages/telemetry/package.json +1 -1
- package/packages/trajectory/package.json +2 -2
- package/packages/user-directory/package.json +2 -2
- package/packages/utils/package.json +3 -3
- package/packages/wrapper/package.json +6 -6
- package/scripts/postinstall.js +106 -2
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests — no broker binary or RELAY_API_KEY required.
|
|
3
|
+
*
|
|
4
|
+
* Run:
|
|
5
|
+
* npm run build && node --test dist/__tests__/unit.test.js
|
|
6
|
+
*/
|
|
7
|
+
import assert from "node:assert/strict";
|
|
8
|
+
import { join, sep } from "node:path";
|
|
9
|
+
import { mkdtemp, writeFile, rm } from "node:fs/promises";
|
|
10
|
+
import { tmpdir } from "node:os";
|
|
11
|
+
import test from "node:test";
|
|
12
|
+
|
|
13
|
+
import { AgentRelay, type Agent } from "../relay.js";
|
|
14
|
+
import { getLogs, listLoggedAgents } from "../logs.js";
|
|
15
|
+
|
|
16
|
+
// ── waitForAny ──────────────────────────────────────────────────────────────
|
|
17
|
+
|
|
18
|
+
function makeFakeAgent(
|
|
19
|
+
name: string,
|
|
20
|
+
exitAfterMs?: number,
|
|
21
|
+
): Agent {
|
|
22
|
+
let resolveExit: ((reason: "exited" | "released") => void) | undefined;
|
|
23
|
+
const exitPromise = new Promise<"exited" | "released">((resolve) => {
|
|
24
|
+
resolveExit = resolve;
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
if (exitAfterMs !== undefined) {
|
|
28
|
+
setTimeout(() => resolveExit?.("exited"), exitAfterMs);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
name,
|
|
33
|
+
runtime: "pty",
|
|
34
|
+
channels: ["general"],
|
|
35
|
+
exitCode: undefined,
|
|
36
|
+
exitSignal: undefined,
|
|
37
|
+
async release() {
|
|
38
|
+
resolveExit?.("released");
|
|
39
|
+
},
|
|
40
|
+
waitForExit(timeoutMs?: number) {
|
|
41
|
+
if (timeoutMs === 0) return Promise.resolve("timeout" as const);
|
|
42
|
+
if (timeoutMs !== undefined) {
|
|
43
|
+
return Promise.race([
|
|
44
|
+
exitPromise,
|
|
45
|
+
new Promise<"timeout">((resolve) =>
|
|
46
|
+
setTimeout(() => resolve("timeout"), timeoutMs),
|
|
47
|
+
),
|
|
48
|
+
]);
|
|
49
|
+
}
|
|
50
|
+
return exitPromise;
|
|
51
|
+
},
|
|
52
|
+
async sendMessage() {
|
|
53
|
+
return { eventId: "fake", from: name, to: "", text: "" };
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
test("waitForAny: returns first agent to exit", async () => {
|
|
59
|
+
const fast = makeFakeAgent("fast", 50);
|
|
60
|
+
const slow = makeFakeAgent("slow", 5_000);
|
|
61
|
+
|
|
62
|
+
const { agent, result } = await AgentRelay.waitForAny([fast, slow], 3_000);
|
|
63
|
+
assert.equal(agent.name, "fast");
|
|
64
|
+
assert.equal(result, "exited");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("waitForAny: returns timeout when no agent exits", async () => {
|
|
68
|
+
const a = makeFakeAgent("a");
|
|
69
|
+
const b = makeFakeAgent("b");
|
|
70
|
+
|
|
71
|
+
const { result } = await AgentRelay.waitForAny([a, b], 100);
|
|
72
|
+
assert.equal(result, "timeout");
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("waitForAny: handles released agent", async () => {
|
|
76
|
+
const agent = makeFakeAgent("releasable");
|
|
77
|
+
|
|
78
|
+
// Release after 50ms
|
|
79
|
+
setTimeout(() => agent.release(), 50);
|
|
80
|
+
|
|
81
|
+
const { agent: resolved, result } = await AgentRelay.waitForAny([agent], 3_000);
|
|
82
|
+
assert.equal(resolved.name, "releasable");
|
|
83
|
+
assert.equal(result, "released");
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test("waitForAny: throws on empty agents array", async () => {
|
|
87
|
+
await assert.rejects(
|
|
88
|
+
() => AgentRelay.waitForAny([]),
|
|
89
|
+
{ message: "waitForAny requires at least one agent" },
|
|
90
|
+
);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// ── getLogs ──────────────────────────────────────────────────────────────────
|
|
94
|
+
|
|
95
|
+
test("getLogs: rejects path traversal", async () => {
|
|
96
|
+
const result = await getLogs("../../etc/passwd", {
|
|
97
|
+
logsDir: "/tmp/test-logs",
|
|
98
|
+
});
|
|
99
|
+
assert.equal(result.found, false);
|
|
100
|
+
assert.equal(result.content, "");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("getLogs: returns not found for missing agent", async () => {
|
|
104
|
+
const dir = await mkdtemp(join(tmpdir(), "relay-test-logs-"));
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const result = await getLogs("nonexistent", { logsDir: dir });
|
|
108
|
+
assert.equal(result.found, false);
|
|
109
|
+
assert.equal(result.content, "");
|
|
110
|
+
} finally {
|
|
111
|
+
await rm(dir, { recursive: true, force: true });
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("getLogs: reads content from log file", async () => {
|
|
116
|
+
const dir = await mkdtemp(join(tmpdir(), "relay-test-logs-"));
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
const logContent = "line1\nline2\nline3\n";
|
|
120
|
+
await writeFile(join(dir, "TestAgent.log"), logContent);
|
|
121
|
+
|
|
122
|
+
const result = await getLogs("TestAgent", { logsDir: dir, lines: 2 });
|
|
123
|
+
assert.equal(result.found, true);
|
|
124
|
+
assert.equal(result.content, "line2\nline3");
|
|
125
|
+
assert.equal(result.lineCount, 2);
|
|
126
|
+
} finally {
|
|
127
|
+
await rm(dir, { recursive: true, force: true });
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("listLoggedAgents: lists agent names from log files", async () => {
|
|
132
|
+
const dir = await mkdtemp(join(tmpdir(), "relay-test-logs-"));
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
await writeFile(join(dir, "Alice.log"), "hello\n");
|
|
136
|
+
await writeFile(join(dir, "Bob.log"), "world\n");
|
|
137
|
+
await writeFile(join(dir, "not-a-log.txt"), "skip\n");
|
|
138
|
+
|
|
139
|
+
const agents = await listLoggedAgents(dir);
|
|
140
|
+
assert.ok(agents.includes("Alice"));
|
|
141
|
+
assert.ok(agents.includes("Bob"));
|
|
142
|
+
assert.ok(!agents.includes("not-a-log"));
|
|
143
|
+
} finally {
|
|
144
|
+
await rm(dir, { recursive: true, force: true });
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test("listLoggedAgents: returns empty for missing directory", async () => {
|
|
149
|
+
const agents = await listLoggedAgents("/tmp/definitely-nonexistent-dir");
|
|
150
|
+
assert.deepEqual(agents, []);
|
|
151
|
+
});
|
|
152
|
+
|
|
@@ -34,12 +34,14 @@ export interface SpawnPtyInput {
|
|
|
34
34
|
cli: string;
|
|
35
35
|
args?: string[];
|
|
36
36
|
channels?: string[];
|
|
37
|
+
task?: string;
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
export interface SpawnHeadlessClaudeInput {
|
|
40
41
|
name: string;
|
|
41
42
|
args?: string[];
|
|
42
43
|
channels?: string[];
|
|
44
|
+
task?: string;
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
export interface SendMessageInput {
|
|
@@ -167,6 +169,7 @@ export class AgentRelayClient {
|
|
|
167
169
|
};
|
|
168
170
|
const result = await this.requestOk<{ name: string; runtime: AgentRuntime }>("spawn_agent", {
|
|
169
171
|
agent,
|
|
172
|
+
...(input.task != null ? { initial_task: input.task } : {}),
|
|
170
173
|
});
|
|
171
174
|
return result;
|
|
172
175
|
}
|
|
@@ -183,6 +186,7 @@ export class AgentRelayClient {
|
|
|
183
186
|
};
|
|
184
187
|
const result = await this.requestOk<{ name: string; runtime: AgentRuntime }>("spawn_agent", {
|
|
185
188
|
agent,
|
|
189
|
+
...(input.task != null ? { initial_task: input.task } : {}),
|
|
186
190
|
});
|
|
187
191
|
return result;
|
|
188
192
|
}
|
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
} from "./client.js";
|
|
35
35
|
import type { AgentRuntime, BrokerEvent, BrokerStatus } from "./protocol.js";
|
|
36
36
|
import { RelaycastApi } from "./relaycast.js";
|
|
37
|
+
import { getLogs as getLogsFromFile, listLoggedAgents as listLoggedAgentsFromFile, type LogsResult, type GetLogsOptions } from "./logs.js";
|
|
37
38
|
|
|
38
39
|
function isUnsupportedOperation(error: unknown): error is AgentRelayProtocolError {
|
|
39
40
|
return error instanceof AgentRelayProtocolError && error.code === "unsupported_operation";
|
|
@@ -66,6 +67,10 @@ export interface Agent {
|
|
|
66
67
|
readonly name: string;
|
|
67
68
|
readonly runtime: AgentRuntime;
|
|
68
69
|
readonly channels: string[];
|
|
70
|
+
/** Set when the agent exits. Available after `onAgentExited` fires. */
|
|
71
|
+
exitCode?: number;
|
|
72
|
+
/** Set when the agent exits via signal. Available after `onAgentExited` fires. */
|
|
73
|
+
exitSignal?: string;
|
|
69
74
|
release(): Promise<void>;
|
|
70
75
|
/** Wait for the agent process to exit on its own.
|
|
71
76
|
* @param timeoutMs — optional timeout in ms. Resolves with `"timeout"` if exceeded,
|
|
@@ -94,6 +99,7 @@ export interface AgentSpawner {
|
|
|
94
99
|
name?: string;
|
|
95
100
|
args?: string[];
|
|
96
101
|
channels?: string[];
|
|
102
|
+
task?: string;
|
|
97
103
|
}): Promise<Agent>;
|
|
98
104
|
}
|
|
99
105
|
|
|
@@ -119,6 +125,7 @@ export class AgentRelay {
|
|
|
119
125
|
onAgentSpawned: EventHook<Agent> = null;
|
|
120
126
|
onAgentReleased: EventHook<Agent> = null;
|
|
121
127
|
onAgentExited: EventHook<Agent> = null;
|
|
128
|
+
onAgentReady: EventHook<Agent> = null;
|
|
122
129
|
onWorkerOutput: EventHook<{ name: string; stream: string; chunk: string }> = null;
|
|
123
130
|
onDeliveryUpdate: EventHook<BrokerEvent> = null;
|
|
124
131
|
|
|
@@ -133,7 +140,11 @@ export class AgentRelay {
|
|
|
133
140
|
private startPromise?: Promise<AgentRelayClient>;
|
|
134
141
|
private unsubEvent?: () => void;
|
|
135
142
|
private readonly knownAgents = new Map<string, Agent>();
|
|
136
|
-
private readonly exitResolvers = new Map<
|
|
143
|
+
private readonly exitResolvers = new Map<
|
|
144
|
+
string,
|
|
145
|
+
{ resolve: (reason: "exited" | "released") => void; token: number }
|
|
146
|
+
>();
|
|
147
|
+
private exitResolverSeq = 0;
|
|
137
148
|
private readonly relaycastByName = new Map<string, RelaycastApi>();
|
|
138
149
|
|
|
139
150
|
constructor(options: AgentRelayOptions = {}) {
|
|
@@ -164,6 +175,7 @@ export class AgentRelay {
|
|
|
164
175
|
cli: input.cli,
|
|
165
176
|
args: input.args,
|
|
166
177
|
channels,
|
|
178
|
+
task: input.task,
|
|
167
179
|
});
|
|
168
180
|
const agent = this.makeAgent(result.name, result.runtime, channels);
|
|
169
181
|
this.knownAgents.set(agent.name, agent);
|
|
@@ -211,6 +223,21 @@ export class AgentRelay {
|
|
|
211
223
|
};
|
|
212
224
|
}
|
|
213
225
|
|
|
226
|
+
// ── Messaging ─────────────────────────────────────────────────────────
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Broadcast a message to all connected agents.
|
|
230
|
+
* @param text — the message body
|
|
231
|
+
* @param options — optional sender name (defaults to "human:orchestrator")
|
|
232
|
+
*/
|
|
233
|
+
async broadcast(
|
|
234
|
+
text: string,
|
|
235
|
+
options?: { from?: string },
|
|
236
|
+
): Promise<Message> {
|
|
237
|
+
const from = options?.from ?? "human:orchestrator";
|
|
238
|
+
return this.human({ name: from }).sendMessage({ to: "*", text });
|
|
239
|
+
}
|
|
240
|
+
|
|
214
241
|
// ── Listing ─────────────────────────────────────────────────────────────
|
|
215
242
|
|
|
216
243
|
async listAgents(): Promise<Agent[]> {
|
|
@@ -232,6 +259,57 @@ export class AgentRelay {
|
|
|
232
259
|
return client.getStatus();
|
|
233
260
|
}
|
|
234
261
|
|
|
262
|
+
// ── Logs ──────────────────────────────────────────────────────────────
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Read the last N lines of an agent's log file.
|
|
266
|
+
*
|
|
267
|
+
* @example
|
|
268
|
+
* ```ts
|
|
269
|
+
* const logs = await relay.getLogs("Worker1", { lines: 100 });
|
|
270
|
+
* if (logs.found) console.log(logs.content);
|
|
271
|
+
* ```
|
|
272
|
+
*/
|
|
273
|
+
async getLogs(agentName: string, options?: { lines?: number }): Promise<LogsResult> {
|
|
274
|
+
const cwd = this.clientOptions.cwd ?? process.cwd();
|
|
275
|
+
const logsDir = path.join(cwd, ".agent-relay", "worker-logs");
|
|
276
|
+
return getLogsFromFile(agentName, { logsDir, lines: options?.lines });
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/** List all agents that have log files. */
|
|
280
|
+
async listLoggedAgents(): Promise<string[]> {
|
|
281
|
+
const cwd = this.clientOptions.cwd ?? process.cwd();
|
|
282
|
+
const logsDir = path.join(cwd, ".agent-relay", "worker-logs");
|
|
283
|
+
return listLoggedAgentsFromFile(logsDir);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// ── Wait helpers ──────────────────────────────────────────────────────
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Wait for any one of the given agents to exit. Returns the first agent
|
|
290
|
+
* that exits along with its exit reason.
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* ```ts
|
|
294
|
+
* const { agent, result } = await AgentRelay.waitForAny([worker1, worker2], 60_000);
|
|
295
|
+
* console.log(`${agent.name} finished: ${result}`);
|
|
296
|
+
* ```
|
|
297
|
+
*/
|
|
298
|
+
static async waitForAny(
|
|
299
|
+
agents: Agent[],
|
|
300
|
+
timeoutMs?: number,
|
|
301
|
+
): Promise<{ agent: Agent; result: "exited" | "timeout" | "released" }> {
|
|
302
|
+
if (agents.length === 0) {
|
|
303
|
+
throw new Error("waitForAny requires at least one agent");
|
|
304
|
+
}
|
|
305
|
+
return Promise.race(
|
|
306
|
+
agents.map(async (agent) => {
|
|
307
|
+
const result = await agent.waitForExit(timeoutMs);
|
|
308
|
+
return { agent, result };
|
|
309
|
+
}),
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
|
|
235
313
|
// ── Lifecycle ───────────────────────────────────────────────────────────
|
|
236
314
|
|
|
237
315
|
async shutdown(): Promise<void> {
|
|
@@ -244,8 +322,8 @@ export class AgentRelay {
|
|
|
244
322
|
this.client = undefined;
|
|
245
323
|
}
|
|
246
324
|
this.knownAgents.clear();
|
|
247
|
-
for (const
|
|
248
|
-
resolve("released");
|
|
325
|
+
for (const entry of this.exitResolvers.values()) {
|
|
326
|
+
entry.resolve("released");
|
|
249
327
|
}
|
|
250
328
|
this.exitResolvers.clear();
|
|
251
329
|
}
|
|
@@ -323,7 +401,7 @@ export class AgentRelay {
|
|
|
323
401
|
this.makeAgent(event.name, "pty", []);
|
|
324
402
|
this.onAgentReleased?.(agent);
|
|
325
403
|
this.knownAgents.delete(event.name);
|
|
326
|
-
this.exitResolvers.get(event.name)?.("released");
|
|
404
|
+
this.exitResolvers.get(event.name)?.resolve("released");
|
|
327
405
|
this.exitResolvers.delete(event.name);
|
|
328
406
|
break;
|
|
329
407
|
}
|
|
@@ -331,12 +409,24 @@ export class AgentRelay {
|
|
|
331
409
|
const agent =
|
|
332
410
|
this.knownAgents.get(event.name) ??
|
|
333
411
|
this.makeAgent(event.name, "pty", []);
|
|
412
|
+
// Populate exit info before firing the hook
|
|
413
|
+
(agent as { exitCode?: number }).exitCode = event.code;
|
|
414
|
+
(agent as { exitSignal?: string }).exitSignal = event.signal;
|
|
334
415
|
this.onAgentExited?.(agent);
|
|
335
416
|
this.knownAgents.delete(event.name);
|
|
336
|
-
this.exitResolvers.get(event.name)?.("exited");
|
|
417
|
+
this.exitResolvers.get(event.name)?.resolve("exited");
|
|
337
418
|
this.exitResolvers.delete(event.name);
|
|
338
419
|
break;
|
|
339
420
|
}
|
|
421
|
+
case "worker_ready": {
|
|
422
|
+
let agent = this.knownAgents.get(event.name);
|
|
423
|
+
if (!agent) {
|
|
424
|
+
agent = this.makeAgent(event.name, event.runtime, []);
|
|
425
|
+
this.knownAgents.set(event.name, agent);
|
|
426
|
+
}
|
|
427
|
+
this.onAgentReady?.(agent);
|
|
428
|
+
break;
|
|
429
|
+
}
|
|
340
430
|
case "worker_stream": {
|
|
341
431
|
this.onWorkerOutput?.({
|
|
342
432
|
name: event.name,
|
|
@@ -362,6 +452,8 @@ export class AgentRelay {
|
|
|
362
452
|
name,
|
|
363
453
|
runtime,
|
|
364
454
|
channels,
|
|
455
|
+
exitCode: undefined,
|
|
456
|
+
exitSignal: undefined,
|
|
365
457
|
async release() {
|
|
366
458
|
const client = await relay.ensureStarted();
|
|
367
459
|
await client.release(name);
|
|
@@ -379,13 +471,21 @@ export class AgentRelay {
|
|
|
379
471
|
return;
|
|
380
472
|
}
|
|
381
473
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
resolve(reason)
|
|
474
|
+
const token = ++relay.exitResolverSeq;
|
|
475
|
+
relay.exitResolvers.set(name, {
|
|
476
|
+
resolve(reason) {
|
|
477
|
+
if (timer) clearTimeout(timer);
|
|
478
|
+
resolve(reason);
|
|
479
|
+
},
|
|
480
|
+
token,
|
|
385
481
|
});
|
|
386
482
|
if (timeoutMs !== undefined) {
|
|
387
483
|
timer = setTimeout(() => {
|
|
388
|
-
|
|
484
|
+
// Only delete if this is still our resolver (not one from a later call)
|
|
485
|
+
const current = relay.exitResolvers.get(name);
|
|
486
|
+
if (current?.token === token) {
|
|
487
|
+
relay.exitResolvers.delete(name);
|
|
488
|
+
}
|
|
389
489
|
resolve("timeout");
|
|
390
490
|
}, timeoutMs);
|
|
391
491
|
}
|
|
@@ -438,11 +538,12 @@ export class AgentRelay {
|
|
|
438
538
|
const channels = options?.channels ?? ["general"];
|
|
439
539
|
const args = options?.args ?? [];
|
|
440
540
|
|
|
541
|
+
const task = options?.task;
|
|
441
542
|
let result: { name: string; runtime: AgentRuntime };
|
|
442
543
|
if (runtime === "headless_claude") {
|
|
443
|
-
result = await client.spawnHeadlessClaude({ name, args, channels });
|
|
544
|
+
result = await client.spawnHeadlessClaude({ name, args, channels, task });
|
|
444
545
|
} else {
|
|
445
|
-
result = await client.spawnPty({ name, cli, args, channels });
|
|
546
|
+
result = await client.spawnPty({ name, cli, args, channels, task });
|
|
446
547
|
}
|
|
447
548
|
|
|
448
549
|
const agent = relay.makeAgent(result.name, result.runtime, channels);
|
|
@@ -2,7 +2,7 @@ import { randomBytes } from "node:crypto";
|
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
-
import {
|
|
5
|
+
import { RelayCast, RelayError, type AgentClient } from "@relaycast/sdk";
|
|
6
6
|
|
|
7
7
|
export interface RelaycastCredentials {
|
|
8
8
|
workspace_id: string;
|
|
@@ -85,7 +85,7 @@ export class RelaycastApi {
|
|
|
85
85
|
if (this.agentClient) return this.agentClient;
|
|
86
86
|
|
|
87
87
|
const apiKey = await this.resolveApiKey();
|
|
88
|
-
const relay = new
|
|
88
|
+
const relay = new RelayCast({ apiKey, baseUrl: this.baseUrl });
|
|
89
89
|
|
|
90
90
|
// Register — retry with a suffixed name on 409 conflict.
|
|
91
91
|
let name = this.agentName;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/storage",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.4",
|
|
4
4
|
"description": "Storage adapters and interfaces for Relay message/session persistence",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
}
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@agent-relay/protocol": "2.3.
|
|
59
|
+
"@agent-relay/protocol": "2.3.4"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@types/node": "^22.19.3",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/trajectory",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.4",
|
|
4
4
|
"description": "Trajectory integration utilities (trail/PDERO) for Relay",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"test:watch": "vitest"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@agent-relay/config": "2.3.
|
|
25
|
+
"@agent-relay/config": "2.3.4"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/node": "^22.19.3",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/user-directory",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.4",
|
|
4
4
|
"description": "User directory service for agent-relay (per-user credential storage)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"test:watch": "vitest"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@agent-relay/resiliency": "2.3.
|
|
25
|
+
"@agent-relay/resiliency": "2.3.4"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/node": "^22.19.3",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/utils",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.4",
|
|
4
4
|
"description": "Shared utilities for agent-relay: logging, name generation, command resolution, update checking",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/cjs/index.js",
|
|
@@ -112,8 +112,8 @@
|
|
|
112
112
|
"vitest": "^3.2.4"
|
|
113
113
|
},
|
|
114
114
|
"dependencies": {
|
|
115
|
-
"@agent-relay/config": "2.3.
|
|
116
|
-
"@agent-relay/protocol": "2.3.
|
|
115
|
+
"@agent-relay/config": "2.3.4",
|
|
116
|
+
"@agent-relay/protocol": "2.3.4",
|
|
117
117
|
"compare-versions": "^6.1.1"
|
|
118
118
|
},
|
|
119
119
|
"publishConfig": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/wrapper",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.4",
|
|
4
4
|
"description": "CLI agent wrappers for Agent Relay - tmux, pty integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -30,11 +30,11 @@
|
|
|
30
30
|
"clean": "rm -rf dist"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@agent-relay/api-types": "2.3.
|
|
34
|
-
"@agent-relay/protocol": "2.3.
|
|
35
|
-
"@agent-relay/config": "2.3.
|
|
36
|
-
"@agent-relay/continuity": "2.3.
|
|
37
|
-
"@agent-relay/resiliency": "2.3.
|
|
33
|
+
"@agent-relay/api-types": "2.3.4",
|
|
34
|
+
"@agent-relay/protocol": "2.3.4",
|
|
35
|
+
"@agent-relay/config": "2.3.4",
|
|
36
|
+
"@agent-relay/continuity": "2.3.4",
|
|
37
|
+
"@agent-relay/resiliency": "2.3.4",
|
|
38
38
|
"zod": "^3.23.8"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|