agent-relay 2.0.32 → 2.0.34
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/README.md +44 -0
- package/dist/index.cjs +7231 -6234
- package/package.json +19 -18
- package/packages/api-types/.trajectories/active/traj_xbsvuzogscey.json +15 -0
- package/packages/api-types/.trajectories/index.json +12 -0
- package/packages/api-types/package.json +1 -1
- package/packages/benchmark/package.json +4 -4
- package/packages/bridge/dist/spawner.d.ts.map +1 -1
- package/packages/bridge/dist/spawner.js +127 -0
- package/packages/bridge/dist/spawner.js.map +1 -1
- package/packages/bridge/package.json +8 -8
- package/packages/bridge/src/spawner.ts +137 -0
- package/packages/cli-tester/package.json +1 -1
- package/packages/config/package.json +2 -2
- package/packages/continuity/package.json +1 -1
- package/packages/daemon/package.json +12 -12
- package/packages/hooks/package.json +4 -4
- package/packages/mcp/package.json +3 -3
- 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 +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 +2 -2
- package/packages/wrapper/dist/base-wrapper.d.ts.map +1 -1
- package/packages/wrapper/dist/base-wrapper.js +27 -7
- package/packages/wrapper/dist/base-wrapper.js.map +1 -1
- package/packages/wrapper/dist/client.d.ts +27 -0
- package/packages/wrapper/dist/client.d.ts.map +1 -1
- package/packages/wrapper/dist/client.js +116 -0
- package/packages/wrapper/dist/client.js.map +1 -1
- package/packages/wrapper/dist/index.d.ts +3 -0
- package/packages/wrapper/dist/index.d.ts.map +1 -1
- package/packages/wrapper/dist/index.js +6 -0
- package/packages/wrapper/dist/index.js.map +1 -1
- package/packages/wrapper/dist/opencode-api.d.ts +106 -0
- package/packages/wrapper/dist/opencode-api.d.ts.map +1 -0
- package/packages/wrapper/dist/opencode-api.js +219 -0
- package/packages/wrapper/dist/opencode-api.js.map +1 -0
- package/packages/wrapper/dist/opencode-wrapper.d.ts +157 -0
- package/packages/wrapper/dist/opencode-wrapper.d.ts.map +1 -0
- package/packages/wrapper/dist/opencode-wrapper.js +414 -0
- package/packages/wrapper/dist/opencode-wrapper.js.map +1 -0
- package/packages/wrapper/dist/relay-pty-orchestrator.d.ts.map +1 -1
- package/packages/wrapper/dist/relay-pty-orchestrator.js +18 -0
- package/packages/wrapper/dist/relay-pty-orchestrator.js.map +1 -1
- package/packages/wrapper/dist/wrapper-events.d.ts +489 -0
- package/packages/wrapper/dist/wrapper-events.d.ts.map +1 -0
- package/packages/wrapper/dist/wrapper-events.js +252 -0
- package/packages/wrapper/dist/wrapper-events.js.map +1 -0
- package/packages/wrapper/package.json +7 -6
- package/packages/wrapper/src/base-wrapper.ts +23 -7
- package/packages/wrapper/src/client.test.ts +92 -3
- package/packages/wrapper/src/client.ts +163 -0
- package/packages/wrapper/src/index.ts +29 -0
- package/packages/wrapper/src/opencode-api.test.ts +292 -0
- package/packages/wrapper/src/opencode-api.ts +285 -0
- package/packages/wrapper/src/opencode-wrapper.ts +513 -0
- package/packages/wrapper/src/relay-pty-orchestrator.test.ts +176 -0
- package/packages/wrapper/src/relay-pty-orchestrator.ts +20 -0
- package/packages/wrapper/src/wrapper-events.ts +395 -0
- package/scripts/postinstall.js +147 -2
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed Event Definitions for Agent Relay Wrapper
|
|
3
|
+
*
|
|
4
|
+
* Inspired by opencode's BusEvent pattern, this provides type-safe event
|
|
5
|
+
* definitions with Zod schema validation. Events can be used for:
|
|
6
|
+
* - Wrapper internal notifications
|
|
7
|
+
* - SDK client subscriptions
|
|
8
|
+
* - OpenAPI spec generation
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { RelayEvent, emitEvent, onEvent } from './wrapper-events.js';
|
|
13
|
+
*
|
|
14
|
+
* // Subscribe to events
|
|
15
|
+
* onEvent(RelayEvent.AgentConnected, (event) => {
|
|
16
|
+
* console.log(`Agent ${event.properties.agentName} connected`);
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* // Emit events
|
|
20
|
+
* emitEvent(RelayEvent.AgentConnected, {
|
|
21
|
+
* agentName: 'MyAgent',
|
|
22
|
+
* connectionId: 'abc123',
|
|
23
|
+
* });
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
import { z } from 'zod';
|
|
27
|
+
import { EventEmitter } from 'node:events';
|
|
28
|
+
/**
|
|
29
|
+
* Define a typed event with Zod schema validation
|
|
30
|
+
*/
|
|
31
|
+
export function defineEvent(type, schema) {
|
|
32
|
+
return { type, schema };
|
|
33
|
+
}
|
|
34
|
+
// =========================================================================
|
|
35
|
+
// Daemon Event Definitions
|
|
36
|
+
// =========================================================================
|
|
37
|
+
export var RelayEvent;
|
|
38
|
+
(function (RelayEvent) {
|
|
39
|
+
// --- Agent Lifecycle Events ---
|
|
40
|
+
RelayEvent.AgentConnected = defineEvent('daemon.agent.connected', z.object({
|
|
41
|
+
agentName: z.string(),
|
|
42
|
+
connectionId: z.string(),
|
|
43
|
+
cli: z.string().optional(),
|
|
44
|
+
task: z.string().optional(),
|
|
45
|
+
workingDirectory: z.string().optional(),
|
|
46
|
+
}));
|
|
47
|
+
RelayEvent.AgentDisconnected = defineEvent('daemon.agent.disconnected', z.object({
|
|
48
|
+
agentName: z.string(),
|
|
49
|
+
connectionId: z.string(),
|
|
50
|
+
reason: z.enum(['clean', 'error', 'timeout', 'replaced']).optional(),
|
|
51
|
+
}));
|
|
52
|
+
RelayEvent.AgentSpawned = defineEvent('daemon.agent.spawned', z.object({
|
|
53
|
+
agentName: z.string(),
|
|
54
|
+
parentAgent: z.string(),
|
|
55
|
+
cli: z.string(),
|
|
56
|
+
task: z.string(),
|
|
57
|
+
}));
|
|
58
|
+
RelayEvent.AgentReleased = defineEvent('daemon.agent.released', z.object({
|
|
59
|
+
agentName: z.string(),
|
|
60
|
+
releasedBy: z.string(),
|
|
61
|
+
}));
|
|
62
|
+
// --- Message Events ---
|
|
63
|
+
RelayEvent.MessageRouted = defineEvent('daemon.message.routed', z.object({
|
|
64
|
+
messageId: z.string(),
|
|
65
|
+
from: z.string(),
|
|
66
|
+
to: z.string(),
|
|
67
|
+
kind: z.string().optional(),
|
|
68
|
+
bodyPreview: z.string().optional(),
|
|
69
|
+
}));
|
|
70
|
+
RelayEvent.MessageDelivered = defineEvent('daemon.message.delivered', z.object({
|
|
71
|
+
messageId: z.string(),
|
|
72
|
+
to: z.string(),
|
|
73
|
+
deliverySeq: z.number(),
|
|
74
|
+
}));
|
|
75
|
+
RelayEvent.MessageFailed = defineEvent('daemon.message.failed', z.object({
|
|
76
|
+
messageId: z.string(),
|
|
77
|
+
to: z.string(),
|
|
78
|
+
error: z.string(),
|
|
79
|
+
}));
|
|
80
|
+
// --- Channel Events ---
|
|
81
|
+
RelayEvent.ChannelJoined = defineEvent('daemon.channel.joined', z.object({
|
|
82
|
+
channel: z.string(),
|
|
83
|
+
member: z.string(),
|
|
84
|
+
}));
|
|
85
|
+
RelayEvent.ChannelLeft = defineEvent('daemon.channel.left', z.object({
|
|
86
|
+
channel: z.string(),
|
|
87
|
+
member: z.string(),
|
|
88
|
+
}));
|
|
89
|
+
RelayEvent.ChannelMessage = defineEvent('daemon.channel.message', z.object({
|
|
90
|
+
channel: z.string(),
|
|
91
|
+
from: z.string(),
|
|
92
|
+
bodyPreview: z.string().optional(),
|
|
93
|
+
}));
|
|
94
|
+
// --- Processing State Events ---
|
|
95
|
+
RelayEvent.AgentProcessingStarted = defineEvent('daemon.agent.processing.started', z.object({
|
|
96
|
+
agentName: z.string(),
|
|
97
|
+
messageId: z.string(),
|
|
98
|
+
}));
|
|
99
|
+
RelayEvent.AgentProcessingEnded = defineEvent('daemon.agent.processing.ended', z.object({
|
|
100
|
+
agentName: z.string(),
|
|
101
|
+
durationMs: z.number().optional(),
|
|
102
|
+
}));
|
|
103
|
+
// --- Shadow Events ---
|
|
104
|
+
RelayEvent.ShadowBound = defineEvent('daemon.shadow.bound', z.object({
|
|
105
|
+
shadowAgent: z.string(),
|
|
106
|
+
primaryAgent: z.string(),
|
|
107
|
+
speakOn: z.array(z.string()),
|
|
108
|
+
}));
|
|
109
|
+
RelayEvent.ShadowUnbound = defineEvent('daemon.shadow.unbound', z.object({
|
|
110
|
+
shadowAgent: z.string(),
|
|
111
|
+
primaryAgent: z.string(),
|
|
112
|
+
}));
|
|
113
|
+
// --- System Events ---
|
|
114
|
+
RelayEvent.DaemonStarted = defineEvent('daemon.system.started', z.object({
|
|
115
|
+
socketPath: z.string(),
|
|
116
|
+
version: z.string().optional(),
|
|
117
|
+
}));
|
|
118
|
+
RelayEvent.DaemonStopped = defineEvent('daemon.system.stopped', z.object({
|
|
119
|
+
reason: z.string().optional(),
|
|
120
|
+
}));
|
|
121
|
+
RelayEvent.RateLimitExceeded = defineEvent('daemon.system.rate_limit_exceeded', z.object({
|
|
122
|
+
agentName: z.string(),
|
|
123
|
+
}));
|
|
124
|
+
// --- All event definitions for iteration ---
|
|
125
|
+
RelayEvent.all = [
|
|
126
|
+
RelayEvent.AgentConnected,
|
|
127
|
+
RelayEvent.AgentDisconnected,
|
|
128
|
+
RelayEvent.AgentSpawned,
|
|
129
|
+
RelayEvent.AgentReleased,
|
|
130
|
+
RelayEvent.MessageRouted,
|
|
131
|
+
RelayEvent.MessageDelivered,
|
|
132
|
+
RelayEvent.MessageFailed,
|
|
133
|
+
RelayEvent.ChannelJoined,
|
|
134
|
+
RelayEvent.ChannelLeft,
|
|
135
|
+
RelayEvent.ChannelMessage,
|
|
136
|
+
RelayEvent.AgentProcessingStarted,
|
|
137
|
+
RelayEvent.AgentProcessingEnded,
|
|
138
|
+
RelayEvent.ShadowBound,
|
|
139
|
+
RelayEvent.ShadowUnbound,
|
|
140
|
+
RelayEvent.DaemonStarted,
|
|
141
|
+
RelayEvent.DaemonStopped,
|
|
142
|
+
RelayEvent.RateLimitExceeded,
|
|
143
|
+
];
|
|
144
|
+
})(RelayEvent || (RelayEvent = {}));
|
|
145
|
+
// =========================================================================
|
|
146
|
+
// Event Bus
|
|
147
|
+
// =========================================================================
|
|
148
|
+
/**
|
|
149
|
+
* Type-safe event bus for daemon events
|
|
150
|
+
*/
|
|
151
|
+
class RelayEventBus extends EventEmitter {
|
|
152
|
+
static instance;
|
|
153
|
+
constructor() {
|
|
154
|
+
super();
|
|
155
|
+
this.setMaxListeners(100); // Allow many subscribers
|
|
156
|
+
}
|
|
157
|
+
static getInstance() {
|
|
158
|
+
if (!RelayEventBus.instance) {
|
|
159
|
+
RelayEventBus.instance = new RelayEventBus();
|
|
160
|
+
}
|
|
161
|
+
return RelayEventBus.instance;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Emit a typed event
|
|
165
|
+
*/
|
|
166
|
+
emitEvent(definition, properties) {
|
|
167
|
+
const payload = {
|
|
168
|
+
type: definition.type,
|
|
169
|
+
properties,
|
|
170
|
+
timestamp: Date.now(),
|
|
171
|
+
};
|
|
172
|
+
// Validate properties against schema
|
|
173
|
+
const result = definition.schema.safeParse(properties);
|
|
174
|
+
if (!result.success) {
|
|
175
|
+
console.error(`[RelayEventBus] Invalid event properties for ${definition.type}:`, result.error);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
// Emit to specific subscribers and wildcard subscribers
|
|
179
|
+
this.emit(definition.type, payload);
|
|
180
|
+
this.emit('*', payload);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Subscribe to a typed event
|
|
184
|
+
*/
|
|
185
|
+
onEvent(definition, callback) {
|
|
186
|
+
this.on(definition.type, callback);
|
|
187
|
+
return () => this.off(definition.type, callback);
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Subscribe to all events (wildcard)
|
|
191
|
+
*/
|
|
192
|
+
onAnyEvent(callback) {
|
|
193
|
+
this.on('*', callback);
|
|
194
|
+
return () => this.off('*', callback);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Subscribe to an event once
|
|
198
|
+
*/
|
|
199
|
+
onceEvent(definition, callback) {
|
|
200
|
+
this.once(definition.type, callback);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// =========================================================================
|
|
204
|
+
// Exports
|
|
205
|
+
// =========================================================================
|
|
206
|
+
/**
|
|
207
|
+
* Global daemon event bus instance
|
|
208
|
+
*/
|
|
209
|
+
export const relayEventBus = RelayEventBus.getInstance();
|
|
210
|
+
/**
|
|
211
|
+
* Emit a daemon event
|
|
212
|
+
*/
|
|
213
|
+
export function emitEvent(definition, properties) {
|
|
214
|
+
relayEventBus.emitEvent(definition, properties);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Subscribe to a daemon event
|
|
218
|
+
*/
|
|
219
|
+
export function onEvent(definition, callback) {
|
|
220
|
+
return relayEventBus.onEvent(definition, callback);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Subscribe to all daemon events
|
|
224
|
+
*/
|
|
225
|
+
export function onAnyEvent(callback) {
|
|
226
|
+
return relayEventBus.onAnyEvent(callback);
|
|
227
|
+
}
|
|
228
|
+
// =========================================================================
|
|
229
|
+
// OpenAPI Schema Generation
|
|
230
|
+
// =========================================================================
|
|
231
|
+
/**
|
|
232
|
+
* Generate OpenAPI-compatible schema for all daemon events
|
|
233
|
+
* This can be used to auto-generate SDK types
|
|
234
|
+
*/
|
|
235
|
+
export function generateEventSchemas() {
|
|
236
|
+
const schemas = {};
|
|
237
|
+
for (const definition of RelayEvent.all) {
|
|
238
|
+
// Extract JSON Schema from Zod schema
|
|
239
|
+
// Note: This is a simplified version - production use should use zod-to-json-schema
|
|
240
|
+
schemas[definition.type] = {
|
|
241
|
+
type: 'object',
|
|
242
|
+
properties: {
|
|
243
|
+
type: { type: 'string', const: definition.type },
|
|
244
|
+
properties: definition.schema._def, // Simplified - use zod-to-json-schema for production
|
|
245
|
+
timestamp: { type: 'number' },
|
|
246
|
+
},
|
|
247
|
+
required: ['type', 'properties', 'timestamp'],
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
return schemas;
|
|
251
|
+
}
|
|
252
|
+
//# sourceMappingURL=wrapper-events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wrapper-events.js","sourceRoot":"","sources":["../src/wrapper-events.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-relay/wrapper",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.34",
|
|
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,12 @@
|
|
|
30
30
|
"clean": "rm -rf dist"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@agent-relay/api-types": "2.0.
|
|
34
|
-
"@agent-relay/protocol": "2.0.
|
|
35
|
-
"@agent-relay/config": "2.0.
|
|
36
|
-
"@agent-relay/continuity": "2.0.
|
|
37
|
-
"@agent-relay/resiliency": "2.0.
|
|
33
|
+
"@agent-relay/api-types": "2.0.34",
|
|
34
|
+
"@agent-relay/protocol": "2.0.34",
|
|
35
|
+
"@agent-relay/config": "2.0.34",
|
|
36
|
+
"@agent-relay/continuity": "2.0.34",
|
|
37
|
+
"@agent-relay/resiliency": "2.0.34",
|
|
38
|
+
"zod": "^3.23.8"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|
|
40
41
|
"typescript": "^5.9.3",
|
|
@@ -576,11 +576,19 @@ export abstract class BaseWrapper extends EventEmitter {
|
|
|
576
576
|
* Execute a spawn command
|
|
577
577
|
*/
|
|
578
578
|
protected async executeSpawn(name: string, cli: string, task: string): Promise<void> {
|
|
579
|
-
//
|
|
580
|
-
|
|
581
|
-
|
|
579
|
+
// Try daemon socket spawn first (most reliable)
|
|
580
|
+
if (this.client.state === 'READY') {
|
|
581
|
+
try {
|
|
582
|
+
const result = await this.client.spawn({ name, cli, task });
|
|
583
|
+
if (result.success) return;
|
|
584
|
+
// If spawn failed, log and fall through to other methods
|
|
585
|
+
console.warn(`[base-wrapper] Daemon spawn failed: ${result.error}`);
|
|
586
|
+
} catch (e) {
|
|
587
|
+
console.warn(`[base-wrapper] Daemon spawn error: ${e instanceof Error ? e.message : String(e)}`);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
582
590
|
|
|
583
|
-
// Try dashboard API
|
|
591
|
+
// Try dashboard API as fallback
|
|
584
592
|
if (this.config.dashboardPort) {
|
|
585
593
|
try {
|
|
586
594
|
const response = await fetch(
|
|
@@ -607,9 +615,17 @@ export abstract class BaseWrapper extends EventEmitter {
|
|
|
607
615
|
* Execute a release command
|
|
608
616
|
*/
|
|
609
617
|
protected async executeRelease(name: string): Promise<void> {
|
|
610
|
-
//
|
|
611
|
-
|
|
612
|
-
|
|
618
|
+
// Try daemon socket release first (most reliable)
|
|
619
|
+
if (this.client.state === 'READY') {
|
|
620
|
+
try {
|
|
621
|
+
const result = await this.client.release(name);
|
|
622
|
+
if (result.success) return;
|
|
623
|
+
// If release failed, log and fall through to other methods
|
|
624
|
+
console.warn(`[base-wrapper] Daemon release failed: ${result.error}`);
|
|
625
|
+
} catch (e) {
|
|
626
|
+
console.warn(`[base-wrapper] Daemon release error: ${e instanceof Error ? e.message : String(e)}`);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
613
629
|
|
|
614
630
|
// Try dashboard API as fallback (backwards compatibility)
|
|
615
631
|
if (this.config.dashboardPort) {
|
|
@@ -256,7 +256,96 @@ describe('RelayClient', () => {
|
|
|
256
256
|
});
|
|
257
257
|
});
|
|
258
258
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
259
|
+
describe('spawn', () => {
|
|
260
|
+
it('should return error when client not ready', async () => {
|
|
261
|
+
const client = new RelayClient({ reconnect: false });
|
|
262
|
+
const result = await client.spawn({ name: 'Worker1', cli: 'claude', task: 'Test task' });
|
|
263
|
+
expect(result.success).toBe(false);
|
|
264
|
+
expect(result.error).toBe('Client not ready');
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('should send SPAWN envelope when ready', async () => {
|
|
268
|
+
const client = new RelayClient({ reconnect: false });
|
|
269
|
+
(client as any)._state = 'READY';
|
|
270
|
+
(client as any).socket = { write: vi.fn() };
|
|
271
|
+
|
|
272
|
+
// Mock requestResponse to return a successful result
|
|
273
|
+
const mockResult = { replyTo: 'test-id', success: true, name: 'Worker1' };
|
|
274
|
+
vi.spyOn(client as any, 'requestResponse').mockResolvedValue(mockResult);
|
|
275
|
+
|
|
276
|
+
const result = await client.spawn({
|
|
277
|
+
name: 'Worker1',
|
|
278
|
+
cli: 'claude',
|
|
279
|
+
task: 'Test task',
|
|
280
|
+
model: 'opus',
|
|
281
|
+
cwd: '/test/path',
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
expect((client as any).requestResponse).toHaveBeenCalledWith(
|
|
285
|
+
'SPAWN',
|
|
286
|
+
expect.objectContaining({
|
|
287
|
+
name: 'Worker1',
|
|
288
|
+
cli: 'claude',
|
|
289
|
+
task: 'Test task',
|
|
290
|
+
model: 'opus',
|
|
291
|
+
cwd: '/test/path',
|
|
292
|
+
}),
|
|
293
|
+
30000
|
|
294
|
+
);
|
|
295
|
+
expect(result.success).toBe(true);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it('should handle spawn failure', async () => {
|
|
299
|
+
const client = new RelayClient({ reconnect: false });
|
|
300
|
+
(client as any)._state = 'READY';
|
|
301
|
+
(client as any).socket = { write: vi.fn() };
|
|
302
|
+
|
|
303
|
+
vi.spyOn(client as any, 'requestResponse').mockRejectedValue(new Error('Spawn failed'));
|
|
304
|
+
|
|
305
|
+
const result = await client.spawn({ name: 'Worker1', cli: 'claude', task: 'Test task' });
|
|
306
|
+
|
|
307
|
+
expect(result.success).toBe(false);
|
|
308
|
+
expect(result.error).toBe('Spawn failed');
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
describe('release', () => {
|
|
313
|
+
it('should return error when client not ready', async () => {
|
|
314
|
+
const client = new RelayClient({ reconnect: false });
|
|
315
|
+
const result = await client.release('Worker1');
|
|
316
|
+
expect(result.success).toBe(false);
|
|
317
|
+
expect(result.error).toBe('Client not ready');
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it('should send RELEASE envelope when ready', async () => {
|
|
321
|
+
const client = new RelayClient({ reconnect: false });
|
|
322
|
+
(client as any)._state = 'READY';
|
|
323
|
+
(client as any).socket = { write: vi.fn() };
|
|
324
|
+
|
|
325
|
+
const mockResult = { replyTo: 'test-id', success: true, name: 'Worker1' };
|
|
326
|
+
vi.spyOn(client as any, 'requestResponse').mockResolvedValue(mockResult);
|
|
327
|
+
|
|
328
|
+
const result = await client.release('Worker1', 'Task completed');
|
|
329
|
+
|
|
330
|
+
expect((client as any).requestResponse).toHaveBeenCalledWith(
|
|
331
|
+
'RELEASE',
|
|
332
|
+
{ name: 'Worker1', reason: 'Task completed' },
|
|
333
|
+
10000
|
|
334
|
+
);
|
|
335
|
+
expect(result.success).toBe(true);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it('should handle release failure', async () => {
|
|
339
|
+
const client = new RelayClient({ reconnect: false });
|
|
340
|
+
(client as any)._state = 'READY';
|
|
341
|
+
(client as any).socket = { write: vi.fn() };
|
|
342
|
+
|
|
343
|
+
vi.spyOn(client as any, 'requestResponse').mockRejectedValue(new Error('Release failed'));
|
|
344
|
+
|
|
345
|
+
const result = await client.release('Worker1');
|
|
346
|
+
|
|
347
|
+
expect(result.success).toBe(false);
|
|
348
|
+
expect(result.error).toBe('Release failed');
|
|
349
|
+
});
|
|
350
|
+
});
|
|
262
351
|
});
|
|
@@ -56,8 +56,21 @@ import {
|
|
|
56
56
|
type SpeakOnTrigger,
|
|
57
57
|
type LogPayload,
|
|
58
58
|
type EntityType,
|
|
59
|
+
type SpawnPayload,
|
|
60
|
+
type SpawnResultPayload,
|
|
61
|
+
type ReleasePayload,
|
|
62
|
+
type ReleaseResultPayload,
|
|
59
63
|
PROTOCOL_VERSION,
|
|
60
64
|
} from '@agent-relay/protocol/types';
|
|
65
|
+
import {
|
|
66
|
+
type SpawnResult,
|
|
67
|
+
type ReleaseResult,
|
|
68
|
+
toSpawnResult,
|
|
69
|
+
toReleaseResult,
|
|
70
|
+
} from '@agent-relay/utils/client-helpers';
|
|
71
|
+
|
|
72
|
+
// Re-export types for consumers
|
|
73
|
+
export type { SpawnResult, ReleaseResult };
|
|
61
74
|
import type {
|
|
62
75
|
ChannelMessagePayload,
|
|
63
76
|
ChannelJoinEnvelope,
|
|
@@ -684,6 +697,139 @@ export class RelayClient {
|
|
|
684
697
|
|
|
685
698
|
return this.send(envelope);
|
|
686
699
|
}
|
|
700
|
+
|
|
701
|
+
// =============================================================================
|
|
702
|
+
// Spawn/Release Operations
|
|
703
|
+
// =============================================================================
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* Spawn a worker agent via the daemon.
|
|
707
|
+
* @param options - Spawn configuration
|
|
708
|
+
* @returns Promise resolving to spawn result
|
|
709
|
+
*/
|
|
710
|
+
async spawn(options: {
|
|
711
|
+
name: string;
|
|
712
|
+
cli: string;
|
|
713
|
+
task: string;
|
|
714
|
+
model?: string;
|
|
715
|
+
cwd?: string;
|
|
716
|
+
}): Promise<SpawnResult> {
|
|
717
|
+
if (this._state !== 'READY') {
|
|
718
|
+
return { success: false, error: 'Client not ready' };
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
try {
|
|
722
|
+
const payload: SpawnPayload = {
|
|
723
|
+
name: options.name,
|
|
724
|
+
cli: options.cli,
|
|
725
|
+
task: options.task,
|
|
726
|
+
model: options.model,
|
|
727
|
+
cwd: options.cwd,
|
|
728
|
+
spawnerName: this.config.agentName,
|
|
729
|
+
};
|
|
730
|
+
|
|
731
|
+
const result = await this.requestResponse<SpawnResultPayload>(
|
|
732
|
+
'SPAWN',
|
|
733
|
+
payload as unknown as Record<string, unknown>,
|
|
734
|
+
30000 // 30 second timeout for spawn
|
|
735
|
+
);
|
|
736
|
+
|
|
737
|
+
return toSpawnResult(result);
|
|
738
|
+
} catch (e) {
|
|
739
|
+
return { success: false, error: e instanceof Error ? e.message : String(e) };
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
/**
|
|
744
|
+
* Release (terminate) a worker agent via the daemon.
|
|
745
|
+
* @param name - Name of the agent to release
|
|
746
|
+
* @param reason - Optional reason for release
|
|
747
|
+
* @returns Promise resolving to release result
|
|
748
|
+
*/
|
|
749
|
+
async release(name: string, reason?: string): Promise<ReleaseResult> {
|
|
750
|
+
if (this._state !== 'READY') {
|
|
751
|
+
return { success: false, error: 'Client not ready' };
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
try {
|
|
755
|
+
const payload: ReleasePayload = { name, reason };
|
|
756
|
+
|
|
757
|
+
const result = await this.requestResponse<ReleaseResultPayload>(
|
|
758
|
+
'RELEASE',
|
|
759
|
+
payload as unknown as Record<string, unknown>,
|
|
760
|
+
10000 // 10 second timeout for release
|
|
761
|
+
);
|
|
762
|
+
|
|
763
|
+
return toReleaseResult(result);
|
|
764
|
+
} catch (e) {
|
|
765
|
+
return { success: false, error: e instanceof Error ? e.message : String(e) };
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* Send an envelope and wait for a matching response.
|
|
771
|
+
* Used for request/response operations like spawn/release.
|
|
772
|
+
*/
|
|
773
|
+
private requestResponse<T>(
|
|
774
|
+
type: string,
|
|
775
|
+
payload: Record<string, unknown>,
|
|
776
|
+
timeoutMs: number
|
|
777
|
+
): Promise<T> {
|
|
778
|
+
return new Promise<T>((resolve, reject) => {
|
|
779
|
+
if (!this.socket) {
|
|
780
|
+
reject(new Error('Not connected'));
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
const requestId = generateId();
|
|
785
|
+
let timeoutHandle: NodeJS.Timeout;
|
|
786
|
+
let responseHandler: (envelope: Envelope) => void;
|
|
787
|
+
|
|
788
|
+
const cleanup = (): void => {
|
|
789
|
+
clearTimeout(timeoutHandle);
|
|
790
|
+
// Remove the handler from pending response handlers
|
|
791
|
+
this.pendingResponses.delete(requestId);
|
|
792
|
+
};
|
|
793
|
+
|
|
794
|
+
timeoutHandle = setTimeout(() => {
|
|
795
|
+
cleanup();
|
|
796
|
+
reject(new Error(`Request timeout after ${timeoutMs}ms`));
|
|
797
|
+
}, timeoutMs);
|
|
798
|
+
|
|
799
|
+
responseHandler = (envelope: Envelope): void => {
|
|
800
|
+
// Match by replyTo or id
|
|
801
|
+
const replyTo = (envelope.payload as { replyTo?: string })?.replyTo;
|
|
802
|
+
if (replyTo === requestId || envelope.id === requestId) {
|
|
803
|
+
cleanup();
|
|
804
|
+
|
|
805
|
+
if (envelope.type === 'ERROR') {
|
|
806
|
+
const errorPayload = envelope.payload as { message?: string; code?: string };
|
|
807
|
+
reject(new Error(errorPayload.message || errorPayload.code || 'Unknown error'));
|
|
808
|
+
} else {
|
|
809
|
+
resolve(envelope.payload as T);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
};
|
|
813
|
+
|
|
814
|
+
// Register handler for this request
|
|
815
|
+
this.pendingResponses.set(requestId, responseHandler);
|
|
816
|
+
|
|
817
|
+
// Send the request envelope
|
|
818
|
+
const envelope: Envelope = {
|
|
819
|
+
v: PROTOCOL_VERSION,
|
|
820
|
+
type: type as Envelope['type'],
|
|
821
|
+
id: requestId,
|
|
822
|
+
ts: Date.now(),
|
|
823
|
+
payload,
|
|
824
|
+
};
|
|
825
|
+
|
|
826
|
+
this.send(envelope);
|
|
827
|
+
});
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// Map to track pending request/response handlers
|
|
831
|
+
private pendingResponses: Map<string, (envelope: Envelope) => void> = new Map();
|
|
832
|
+
|
|
687
833
|
private setState(state: ClientState): void {
|
|
688
834
|
this._state = state;
|
|
689
835
|
if (this.onStateChange) {
|
|
@@ -768,6 +914,17 @@ export class RelayClient {
|
|
|
768
914
|
}
|
|
769
915
|
|
|
770
916
|
private processFrame(envelope: Envelope): void {
|
|
917
|
+
// Check for pending request/response handlers first
|
|
918
|
+
// This handles SPAWN_RESULT, RELEASE_RESULT, and ERROR responses
|
|
919
|
+
const replyTo = (envelope.payload as { replyTo?: string })?.replyTo;
|
|
920
|
+
if (replyTo && this.pendingResponses.has(replyTo)) {
|
|
921
|
+
const handler = this.pendingResponses.get(replyTo);
|
|
922
|
+
if (handler) {
|
|
923
|
+
handler(envelope);
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
|
|
771
928
|
switch (envelope.type) {
|
|
772
929
|
case 'WELCOME':
|
|
773
930
|
this.handleWelcome(envelope as Envelope<WelcomePayload>);
|
|
@@ -793,6 +950,12 @@ export class RelayClient {
|
|
|
793
950
|
this.handleErrorFrame(envelope as Envelope<ErrorPayload>);
|
|
794
951
|
break;
|
|
795
952
|
|
|
953
|
+
case 'SPAWN_RESULT':
|
|
954
|
+
case 'RELEASE_RESULT':
|
|
955
|
+
// These should be handled by pending response handlers above
|
|
956
|
+
// If we get here, it's an orphaned response (no handler waiting)
|
|
957
|
+
break;
|
|
958
|
+
|
|
796
959
|
case 'BUSY':
|
|
797
960
|
console.warn('[client] Server busy, backing off');
|
|
798
961
|
break;
|
|
@@ -168,3 +168,32 @@ export {
|
|
|
168
168
|
RelayPtyOrchestrator,
|
|
169
169
|
type RelayPtyOrchestratorConfig,
|
|
170
170
|
} from './relay-pty-orchestrator.js';
|
|
171
|
+
|
|
172
|
+
// OpenCode HTTP API integration
|
|
173
|
+
export {
|
|
174
|
+
OpenCodeApi,
|
|
175
|
+
openCodeApi,
|
|
176
|
+
type OpenCodeApiConfig,
|
|
177
|
+
type OpenCodeSession,
|
|
178
|
+
type OpenCodeApiResponse,
|
|
179
|
+
} from './opencode-api.js';
|
|
180
|
+
|
|
181
|
+
// OpenCode wrapper (HTTP API + PTY fallback)
|
|
182
|
+
export {
|
|
183
|
+
OpenCodeWrapper,
|
|
184
|
+
type OpenCodeWrapperConfig,
|
|
185
|
+
} from './opencode-wrapper.js';
|
|
186
|
+
|
|
187
|
+
// Typed event definitions (inspired by opencode's BusEvent pattern)
|
|
188
|
+
export {
|
|
189
|
+
RelayEvent,
|
|
190
|
+
defineEvent,
|
|
191
|
+
relayEventBus,
|
|
192
|
+
emitEvent,
|
|
193
|
+
onEvent,
|
|
194
|
+
onAnyEvent,
|
|
195
|
+
generateEventSchemas,
|
|
196
|
+
type EventDefinition,
|
|
197
|
+
type EventProperties,
|
|
198
|
+
type EventPayload,
|
|
199
|
+
} from './wrapper-events.js';
|