agent-relay 3.2.14 → 3.2.16
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/bin/agent-relay-broker-darwin-arm64 +0 -0
- package/bin/agent-relay-broker-darwin-x64 +0 -0
- package/bin/agent-relay-broker-linux-arm64 +0 -0
- package/bin/agent-relay-broker-linux-x64 +0 -0
- package/dist/index.cjs +3859 -17164
- package/package.json +8 -8
- package/packages/acp-bridge/package.json +2 -2
- package/packages/config/package.json +1 -1
- package/packages/hooks/package.json +4 -4
- package/packages/memory/package.json +2 -2
- package/packages/openclaw/package.json +2 -2
- package/packages/policy/package.json +2 -2
- package/packages/sdk/dist/broker-path.d.ts +19 -0
- package/packages/sdk/dist/broker-path.d.ts.map +1 -0
- package/packages/sdk/dist/broker-path.js +71 -0
- package/packages/sdk/dist/broker-path.js.map +1 -0
- package/packages/sdk/dist/cli-registry.d.ts.map +1 -1
- package/packages/sdk/dist/cli-registry.js +10 -1
- package/packages/sdk/dist/cli-registry.js.map +1 -1
- package/packages/sdk/dist/client.d.ts +6 -1
- package/packages/sdk/dist/client.d.ts.map +1 -1
- package/packages/sdk/dist/client.js +18 -0
- package/packages/sdk/dist/client.js.map +1 -1
- package/packages/sdk/dist/communicate/adapters/index.d.ts +0 -5
- package/packages/sdk/dist/communicate/adapters/index.d.ts.map +1 -1
- package/packages/sdk/dist/communicate/adapters/index.js +0 -5
- package/packages/sdk/dist/communicate/adapters/index.js.map +1 -1
- package/packages/sdk/dist/communicate/adapters/pi.d.ts +0 -1
- package/packages/sdk/dist/communicate/adapters/pi.d.ts.map +1 -1
- package/packages/sdk/dist/communicate/adapters/pi.js +0 -4
- package/packages/sdk/dist/communicate/adapters/pi.js.map +1 -1
- package/packages/sdk/dist/communicate/core.d.ts.map +1 -1
- package/packages/sdk/dist/communicate/core.js +2 -3
- package/packages/sdk/dist/communicate/core.js.map +1 -1
- package/packages/sdk/dist/communicate/index.d.ts +17 -1
- package/packages/sdk/dist/communicate/index.d.ts.map +1 -1
- package/packages/sdk/dist/communicate/index.js +40 -1
- package/packages/sdk/dist/communicate/index.js.map +1 -1
- package/packages/sdk/dist/communicate/transport.d.ts +0 -1
- package/packages/sdk/dist/communicate/transport.d.ts.map +1 -1
- package/packages/sdk/dist/communicate/transport.js +42 -134
- package/packages/sdk/dist/communicate/transport.js.map +1 -1
- package/packages/sdk/dist/http.d.ts +38 -0
- package/packages/sdk/dist/http.d.ts.map +1 -0
- package/packages/sdk/dist/http.js +60 -0
- package/packages/sdk/dist/http.js.map +1 -0
- package/packages/sdk/dist/protocol.d.ts +25 -0
- package/packages/sdk/dist/protocol.d.ts.map +1 -1
- package/packages/sdk/dist/relay.d.ts +26 -3
- package/packages/sdk/dist/relay.d.ts.map +1 -1
- package/packages/sdk/dist/relay.js +62 -4
- package/packages/sdk/dist/relay.js.map +1 -1
- package/packages/sdk/dist/workflows/api-executor.d.ts +16 -0
- package/packages/sdk/dist/workflows/api-executor.d.ts.map +1 -0
- package/packages/sdk/dist/workflows/api-executor.js +94 -0
- package/packages/sdk/dist/workflows/api-executor.js.map +1 -0
- package/packages/sdk/dist/workflows/builder.d.ts +14 -0
- package/packages/sdk/dist/workflows/builder.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/builder.js +26 -0
- package/packages/sdk/dist/workflows/builder.js.map +1 -1
- package/packages/sdk/dist/workflows/cloud-runner.d.ts +15 -0
- package/packages/sdk/dist/workflows/cloud-runner.d.ts.map +1 -0
- package/packages/sdk/dist/workflows/cloud-runner.js +41 -0
- package/packages/sdk/dist/workflows/cloud-runner.js.map +1 -0
- package/packages/sdk/dist/workflows/index.d.ts +2 -0
- package/packages/sdk/dist/workflows/index.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/index.js +1 -0
- package/packages/sdk/dist/workflows/index.js.map +1 -1
- package/packages/sdk/dist/workflows/run.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/run.js +4 -0
- package/packages/sdk/dist/workflows/run.js.map +1 -1
- package/packages/sdk/dist/workflows/runner.d.ts +14 -0
- package/packages/sdk/dist/workflows/runner.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/runner.js +154 -10
- package/packages/sdk/dist/workflows/runner.js.map +1 -1
- package/packages/sdk/dist/workflows/types.d.ts +13 -3
- package/packages/sdk/dist/workflows/types.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/types.js +5 -1
- package/packages/sdk/dist/workflows/types.js.map +1 -1
- package/packages/sdk/dist/workflows/validator.d.ts.map +1 -1
- package/packages/sdk/dist/workflows/validator.js +12 -0
- package/packages/sdk/dist/workflows/validator.js.map +1 -1
- package/packages/sdk/package.json +13 -3
- package/packages/sdk/src/__tests__/channel-management.test.ts +131 -0
- package/packages/sdk/src/__tests__/communicate/core.test.ts +36 -88
- package/packages/sdk/src/__tests__/communicate/transport.test.ts +41 -80
- package/packages/sdk/src/__tests__/orchestration-upgrades.test.ts +120 -0
- package/packages/sdk/src/__tests__/relay-channel-ops.test.ts +121 -0
- package/packages/sdk/src/__tests__/workflow-runner.test.ts +2 -2
- package/packages/sdk/src/broker-path.ts +74 -0
- package/packages/sdk/src/cli-registry.ts +10 -1
- package/packages/sdk/src/client.ts +28 -0
- package/packages/sdk/src/communicate/adapters/index.ts +0 -5
- package/packages/sdk/src/communicate/adapters/pi.ts +1 -5
- package/packages/sdk/src/communicate/core.ts +6 -10
- package/packages/sdk/src/communicate/index.ts +57 -1
- package/packages/sdk/src/communicate/transport.ts +46 -177
- package/packages/sdk/src/http.ts +96 -0
- package/packages/sdk/src/protocol.ts +24 -0
- package/packages/sdk/src/relay.ts +93 -8
- package/packages/sdk/src/workflows/README.md +5 -2
- package/packages/sdk/src/workflows/api-executor.ts +108 -0
- package/packages/sdk/src/workflows/builder.ts +40 -0
- package/packages/sdk/src/workflows/cloud-runner.ts +56 -0
- package/packages/sdk/src/workflows/index.ts +2 -0
- package/packages/sdk/src/workflows/run.ts +5 -0
- package/packages/sdk/src/workflows/runner.ts +181 -11
- package/packages/sdk/src/workflows/types.ts +19 -4
- package/packages/sdk/src/workflows/validator.ts +15 -0
- package/packages/sdk-py/README.md +7 -0
- package/packages/sdk-py/pyproject.toml +1 -1
- package/packages/sdk-py/src/agent_relay/__init__.py +2 -0
- package/packages/sdk-py/src/agent_relay/client.py +4 -0
- package/packages/sdk-py/src/agent_relay/communicate/adapters/__init__.py +0 -9
- package/packages/sdk-py/src/agent_relay/communicate/adapters/agno.py +5 -9
- package/packages/sdk-py/src/agent_relay/communicate/adapters/claude_sdk.py +5 -7
- package/packages/sdk-py/src/agent_relay/communicate/adapters/crewai.py +3 -13
- package/packages/sdk-py/src/agent_relay/communicate/adapters/google_adk.py +5 -2
- package/packages/sdk-py/src/agent_relay/communicate/adapters/openai_agents.py +5 -9
- package/packages/sdk-py/src/agent_relay/communicate/core.py +7 -24
- package/packages/sdk-py/src/agent_relay/communicate/transport.py +35 -212
- package/packages/sdk-py/src/agent_relay/communicate/types.py +1 -1
- package/packages/sdk-py/src/agent_relay/protocol.py +1 -0
- package/packages/sdk-py/src/agent_relay/relay.py +9 -1
- package/packages/sdk-py/tests/communicate/adapters/test_claude_sdk.py +6 -6
- package/packages/sdk-py/tests/communicate/conftest.py +86 -233
- package/packages/sdk-py/tests/communicate/integration/test_cross_framework.py +2 -2
- package/packages/sdk-py/tests/communicate/integration/test_end_to_end.py +14 -24
- package/packages/sdk-py/tests/communicate/test_transport.py +65 -54
- package/packages/sdk-py/tests/test_send_message_mode.py +91 -0
- package/packages/sdk-swift/Sources/AgentRelaySDK/RelayObserver.swift +323 -0
- package/packages/sdk-swift/Sources/AgentRelaySDK/RelayObserverTypes.swift +143 -0
- package/packages/sdk-swift/Tests/AgentRelaySDKTests/RelayObserverTests.swift +526 -0
- 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
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
type ProtocolEnvelope,
|
|
21
21
|
type ProtocolError,
|
|
22
22
|
type RestartPolicy,
|
|
23
|
+
type MessageInjectionMode,
|
|
23
24
|
} from './protocol.js';
|
|
24
25
|
|
|
25
26
|
export interface AgentRelayClientOptions {
|
|
@@ -99,6 +100,7 @@ export interface SendMessageInput {
|
|
|
99
100
|
workspaceAlias?: string;
|
|
100
101
|
priority?: number;
|
|
101
102
|
data?: Record<string, unknown>;
|
|
103
|
+
mode?: MessageInjectionMode;
|
|
102
104
|
}
|
|
103
105
|
|
|
104
106
|
export interface ListAgent {
|
|
@@ -369,6 +371,16 @@ export class AgentRelayClient {
|
|
|
369
371
|
return this.requestOk<{ name: string; bytes_written: number }>('send_input', { name, data });
|
|
370
372
|
}
|
|
371
373
|
|
|
374
|
+
async subscribeChannels(name: string, channels: string[]): Promise<void> {
|
|
375
|
+
await this.start();
|
|
376
|
+
await this.requestOk<void>('subscribe_channels', { name, channels });
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
async unsubscribeChannels(name: string, channels: string[]): Promise<void> {
|
|
380
|
+
await this.start();
|
|
381
|
+
await this.requestOk<void>('unsubscribe_channels', { name, channels });
|
|
382
|
+
}
|
|
383
|
+
|
|
372
384
|
async resizePty(
|
|
373
385
|
name: string,
|
|
374
386
|
rows: number,
|
|
@@ -423,6 +435,7 @@ export class AgentRelayClient {
|
|
|
423
435
|
workspace_alias: input.workspaceAlias,
|
|
424
436
|
priority: input.priority,
|
|
425
437
|
data: input.data,
|
|
438
|
+
mode: input.mode,
|
|
426
439
|
});
|
|
427
440
|
} catch (error) {
|
|
428
441
|
if (error instanceof AgentRelayProtocolError && error.code === 'unsupported_operation') {
|
|
@@ -1154,6 +1167,7 @@ export class HttpAgentRelayClient {
|
|
|
1154
1167
|
workspaceAlias: input.workspaceAlias,
|
|
1155
1168
|
priority: input.priority,
|
|
1156
1169
|
data: input.data,
|
|
1170
|
+
mode: input.mode,
|
|
1157
1171
|
}),
|
|
1158
1172
|
});
|
|
1159
1173
|
}
|
|
@@ -1176,6 +1190,20 @@ export class HttpAgentRelayClient {
|
|
|
1176
1190
|
return { name: typeof payload?.name === 'string' ? payload.name : name };
|
|
1177
1191
|
}
|
|
1178
1192
|
|
|
1193
|
+
async subscribeChannels(_name: string, _channels: string[]): Promise<void> {
|
|
1194
|
+
throw new Error(
|
|
1195
|
+
'subscribeChannels is only available via the broker protocol (BrokerAgentRelayClient). ' +
|
|
1196
|
+
'The HTTP API does not support dynamic channel subscription.'
|
|
1197
|
+
);
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
async unsubscribeChannels(_name: string, _channels: string[]): Promise<void> {
|
|
1201
|
+
throw new Error(
|
|
1202
|
+
'unsubscribeChannels is only available via the broker protocol (BrokerAgentRelayClient). ' +
|
|
1203
|
+
'The HTTP API does not support dynamic channel unsubscription.'
|
|
1204
|
+
);
|
|
1205
|
+
}
|
|
1206
|
+
|
|
1179
1207
|
async setModel(
|
|
1180
1208
|
name: string,
|
|
1181
1209
|
model: string,
|
|
@@ -1,7 +1,2 @@
|
|
|
1
1
|
export { onRelay as onPiRelay } from './pi.js';
|
|
2
2
|
export { onRelay as onClaudeRelay } from './claude-sdk.js';
|
|
3
|
-
export { onRelay as onCrewAIRelay, onCrewRelay } from './crewai.js';
|
|
4
|
-
export { onRelay as onOpenAIAgentsRelay } from './openai-agents.js';
|
|
5
|
-
export { onRelay as onLangGraphRelay } from './langgraph.js';
|
|
6
|
-
export { onRelay as onGoogleAdkRelay } from './google-adk.js';
|
|
7
|
-
export { onRelay as onAiSdkRelay } from './ai-sdk.js';
|
|
@@ -74,7 +74,7 @@ export function onRelay<TConfig extends PiConfigLike>(
|
|
|
74
74
|
name: string,
|
|
75
75
|
config: TConfig,
|
|
76
76
|
relay: RelayLike = new Relay(name)
|
|
77
|
-
): TConfig & { customTools: RelayTool[]; onSessionCreated: (session: PiSessionLike) => Promise<void
|
|
77
|
+
): TConfig & { customTools: RelayTool[]; onSessionCreated: (session: PiSessionLike) => Promise<void> } {
|
|
78
78
|
const customTools = [...(config.customTools ?? []), ...createRelayTools(relay)];
|
|
79
79
|
const originalOnSessionCreated = config.onSessionCreated;
|
|
80
80
|
let unsubscribe: (() => void) | undefined;
|
|
@@ -97,9 +97,5 @@ export function onRelay<TConfig extends PiConfigLike>(
|
|
|
97
97
|
await session.followUp(prompt);
|
|
98
98
|
});
|
|
99
99
|
},
|
|
100
|
-
cleanup() {
|
|
101
|
-
unsubscribe?.();
|
|
102
|
-
unsubscribe = undefined;
|
|
103
|
-
},
|
|
104
100
|
};
|
|
105
101
|
}
|
|
@@ -124,16 +124,12 @@ export class Relay {
|
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
if (!this.connectPromise) {
|
|
127
|
-
this.connectPromise = this.transport.connect().then(
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
this.connectPromise = undefined;
|
|
134
|
-
throw err;
|
|
135
|
-
},
|
|
136
|
-
);
|
|
127
|
+
this.connectPromise = this.transport.connect().then(() => {
|
|
128
|
+
this.connected = true;
|
|
129
|
+
}).catch((error) => {
|
|
130
|
+
this.connectPromise = undefined;
|
|
131
|
+
throw error;
|
|
132
|
+
});
|
|
137
133
|
}
|
|
138
134
|
|
|
139
135
|
await this.connectPromise;
|
|
@@ -1,3 +1,59 @@
|
|
|
1
1
|
export * from './types.js';
|
|
2
2
|
export { Relay } from './core.js';
|
|
3
|
-
export { onPiRelay, onClaudeRelay
|
|
3
|
+
export { onPiRelay, onClaudeRelay } from './adapters/index.js';
|
|
4
|
+
|
|
5
|
+
import { onRelay as onPiRelay } from './adapters/pi.js';
|
|
6
|
+
import { onRelay as onClaudeRelay } from './adapters/claude-sdk.js';
|
|
7
|
+
import { Relay } from './core.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Auto-detect the agent framework and apply the appropriate relay adapter.
|
|
11
|
+
*
|
|
12
|
+
* Requires a `framework` discriminator to avoid ambiguous detection.
|
|
13
|
+
* If you know which framework you're using, prefer importing the
|
|
14
|
+
* adapter directly: `onPiRelay` or `onClaudeRelay`.
|
|
15
|
+
*
|
|
16
|
+
* @param nameOrAgent - Agent name (string) or the agent/config object directly.
|
|
17
|
+
* @param configOrOptions - Config/options object when first arg is a name.
|
|
18
|
+
* @param maybeRelay - Optional pre-configured Relay instance.
|
|
19
|
+
* @returns The augmented agent config or options.
|
|
20
|
+
*/
|
|
21
|
+
export function onRelay(
|
|
22
|
+
nameOrAgent: string | Record<string, unknown>,
|
|
23
|
+
configOrOptions?: Record<string, unknown>,
|
|
24
|
+
maybeRelay?: Relay
|
|
25
|
+
): Record<string, unknown> {
|
|
26
|
+
const isStringName = typeof nameOrAgent === 'string';
|
|
27
|
+
const name = isStringName ? nameOrAgent : ((nameOrAgent as Record<string, unknown>).name as string) || 'Agent';
|
|
28
|
+
const target: unknown = isStringName ? configOrOptions : nameOrAgent;
|
|
29
|
+
const relay = (isStringName ? maybeRelay : configOrOptions) as Relay | undefined;
|
|
30
|
+
|
|
31
|
+
const relayInstance = relay || new Relay(name);
|
|
32
|
+
|
|
33
|
+
if (typeof target !== 'object' || target === null) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
`onRelay() received a non-object target for ${name}. ` +
|
|
36
|
+
'Pass the framework config/options object, or use onPiRelay / onClaudeRelay directly.'
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const obj = target as Record<string, unknown>;
|
|
41
|
+
|
|
42
|
+
// Detect Pi: has customTools or Agent constructor
|
|
43
|
+
if ('customTools' in obj || obj.constructor?.name === 'Agent') {
|
|
44
|
+
return onPiRelay(name, target as Parameters<typeof onPiRelay>[1], relayInstance);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Detect Claude SDK: has mcpServers or hooks, or is a plain empty options object
|
|
48
|
+
if ('mcpServers' in obj || 'hooks' in obj || Object.keys(obj).length === 0) {
|
|
49
|
+
return onClaudeRelay(name, target as Parameters<typeof onClaudeRelay>[1], relayInstance);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
throw new Error(
|
|
53
|
+
`onRelay() could not auto-detect framework for ${name}. ` +
|
|
54
|
+
'Use the framework-specific adapter instead: onPiRelay or onClaudeRelay.'
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Alias for onRelay — spec uses both names */
|
|
59
|
+
export const withRelay = onRelay;
|
|
@@ -83,113 +83,70 @@ export class RelayTransport {
|
|
|
83
83
|
return this.agentId;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
);
|
|
93
|
-
} catch (error) {
|
|
94
|
-
if (error instanceof RelayConnectionError && error.statusCode === 409) {
|
|
95
|
-
const agentPayload = await this.sendHttp<JsonObject>(
|
|
96
|
-
'GET',
|
|
97
|
-
`/v1/agents/${encodeURIComponent(this.agentName)}`,
|
|
98
|
-
);
|
|
99
|
-
const agentData = (agentPayload as any).data ?? agentPayload;
|
|
100
|
-
this.agentId = String(agentData.id);
|
|
101
|
-
|
|
102
|
-
const rotatePayload = await this.sendHttp<JsonObject>(
|
|
103
|
-
'POST',
|
|
104
|
-
`/v1/agents/${encodeURIComponent(this.agentName)}/rotate-token`,
|
|
105
|
-
);
|
|
106
|
-
const rotateData = (rotatePayload as any).data ?? rotatePayload;
|
|
107
|
-
this.token = String(rotateData.token);
|
|
108
|
-
return this.agentId;
|
|
86
|
+
const payload = await this.sendHttp<{ agent_id: string; token: string }>(
|
|
87
|
+
'POST',
|
|
88
|
+
'/v1/agents/register',
|
|
89
|
+
{
|
|
90
|
+
name: this.agentName,
|
|
91
|
+
workspace: this.config.workspace,
|
|
109
92
|
}
|
|
110
|
-
|
|
111
|
-
}
|
|
93
|
+
);
|
|
112
94
|
|
|
113
|
-
|
|
114
|
-
this.
|
|
115
|
-
this.token = String(data.token);
|
|
95
|
+
this.agentId = payload.agent_id;
|
|
96
|
+
this.token = payload.token;
|
|
116
97
|
return this.agentId;
|
|
117
98
|
}
|
|
118
99
|
|
|
119
100
|
async unregisterAgent(): Promise<void> {
|
|
120
|
-
if (!this.agentId
|
|
101
|
+
if (!this.agentId) {
|
|
121
102
|
return;
|
|
122
103
|
}
|
|
123
104
|
|
|
105
|
+
const agentId = this.agentId;
|
|
124
106
|
this.agentId = undefined;
|
|
125
|
-
const agentToken = this.token;
|
|
126
107
|
this.token = undefined;
|
|
127
|
-
await this.
|
|
108
|
+
await this.sendHttp('DELETE', `/v1/agents/${agentId}`);
|
|
128
109
|
}
|
|
129
110
|
|
|
130
111
|
async sendDm(to: string, text: string): Promise<string> {
|
|
131
112
|
await this.ensureRegistered();
|
|
132
|
-
const payload = await this.
|
|
133
|
-
|
|
134
|
-
|
|
113
|
+
const payload = await this.sendHttp<{ message_id: string }>('POST', '/v1/messages/dm', {
|
|
114
|
+
to,
|
|
115
|
+
text,
|
|
116
|
+
from: this.agentName,
|
|
117
|
+
});
|
|
118
|
+
return payload.message_id;
|
|
135
119
|
}
|
|
136
120
|
|
|
137
121
|
async postMessage(channel: string, text: string): Promise<string> {
|
|
138
122
|
await this.ensureRegistered();
|
|
139
|
-
const payload = await this.
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
return String(data.id ?? data.message_id ?? '');
|
|
123
|
+
const payload = await this.sendHttp<{ message_id: string }>('POST', '/v1/messages/channel', {
|
|
124
|
+
channel,
|
|
125
|
+
text,
|
|
126
|
+
from: this.agentName,
|
|
127
|
+
});
|
|
128
|
+
return payload.message_id;
|
|
146
129
|
}
|
|
147
130
|
|
|
148
131
|
async reply(messageId: string, text: string): Promise<string> {
|
|
149
132
|
await this.ensureRegistered();
|
|
150
|
-
const payload = await this.
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
);
|
|
155
|
-
|
|
156
|
-
return String(data.id ?? data.message_id ?? '');
|
|
133
|
+
const payload = await this.sendHttp<{ message_id: string }>('POST', '/v1/messages/reply', {
|
|
134
|
+
message_id: messageId,
|
|
135
|
+
text,
|
|
136
|
+
from: this.agentName,
|
|
137
|
+
});
|
|
138
|
+
return payload.message_id;
|
|
157
139
|
}
|
|
158
140
|
|
|
159
141
|
async checkInbox(): Promise<Message[]> {
|
|
160
142
|
await this.ensureRegistered();
|
|
161
|
-
const payload = await this.
|
|
162
|
-
|
|
163
|
-
const messages: Message[] = [];
|
|
164
|
-
|
|
165
|
-
for (const mention of ((data as any).mentions ?? []) as JsonObject[]) {
|
|
166
|
-
messages.push(this.messageFromPayload(mention));
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
for (const dm of ((data as any).unread_dms ?? []) as JsonObject[]) {
|
|
170
|
-
const last = (dm as any).last_message as JsonObject | undefined;
|
|
171
|
-
if (last?.text) {
|
|
172
|
-
messages.push({
|
|
173
|
-
sender: String(dm.from ?? dm.agent_name ?? 'unknown'),
|
|
174
|
-
text: String(last.text),
|
|
175
|
-
channel: undefined,
|
|
176
|
-
threadId: typeof dm.conversation_id === 'string' ? dm.conversation_id : undefined,
|
|
177
|
-
timestamp: typeof last.created_at === 'string' ? undefined : (last.created_at as number | undefined),
|
|
178
|
-
messageId: typeof last.id === 'string' ? last.id : undefined,
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
return messages;
|
|
143
|
+
const payload = await this.sendHttp<{ messages?: JsonObject[] }>('GET', `/v1/inbox/${this.agentId}`);
|
|
144
|
+
return (payload.messages ?? []).map((message) => this.messageFromPayload(message));
|
|
184
145
|
}
|
|
185
146
|
|
|
186
147
|
async listAgents(): Promise<string[]> {
|
|
187
|
-
const payload = await this.sendHttp<
|
|
188
|
-
|
|
189
|
-
if (Array.isArray(data)) {
|
|
190
|
-
return data.map((a: any) => (typeof a === 'string' ? a : String(a.name ?? a)));
|
|
191
|
-
}
|
|
192
|
-
return [];
|
|
148
|
+
const payload = await this.sendHttp<{ agents?: string[] }>('GET', '/v1/agents');
|
|
149
|
+
return [...(payload.agents ?? [])];
|
|
193
150
|
}
|
|
194
151
|
|
|
195
152
|
private async ensureRegistered(): Promise<void> {
|
|
@@ -213,71 +170,6 @@ export class RelayTransport {
|
|
|
213
170
|
}
|
|
214
171
|
}
|
|
215
172
|
|
|
216
|
-
private async sendHttpAsAgent<T = unknown>(
|
|
217
|
-
method: string,
|
|
218
|
-
path: string,
|
|
219
|
-
payload?: JsonObject,
|
|
220
|
-
overrideToken?: string,
|
|
221
|
-
): Promise<T> {
|
|
222
|
-
this.requireConfig();
|
|
223
|
-
|
|
224
|
-
const agentToken = overrideToken ?? this.token;
|
|
225
|
-
if (!agentToken) {
|
|
226
|
-
throw new RelayConfigError('Agent not registered; no agent token available.');
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const url = `${this.config.baseUrl}${path}`;
|
|
230
|
-
const headers: Record<string, string> = {
|
|
231
|
-
authorization: `Bearer ${agentToken}`,
|
|
232
|
-
};
|
|
233
|
-
if (payload !== undefined) {
|
|
234
|
-
headers['content-type'] = 'application/json';
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
for (let attempt = 1; attempt <= HTTP_RETRY_ATTEMPTS; attempt += 1) {
|
|
238
|
-
let response: Response;
|
|
239
|
-
try {
|
|
240
|
-
response = await fetch(url, {
|
|
241
|
-
method,
|
|
242
|
-
headers,
|
|
243
|
-
body: payload === undefined ? undefined : JSON.stringify(payload),
|
|
244
|
-
});
|
|
245
|
-
} catch (error) {
|
|
246
|
-
if (attempt < HTTP_RETRY_ATTEMPTS) {
|
|
247
|
-
await sleep(Math.min(2 ** (attempt - 1) * 1_000, WS_RECONNECT_MAX_DELAY_MS));
|
|
248
|
-
continue;
|
|
249
|
-
}
|
|
250
|
-
throw new RelayConnectionError(0, error instanceof Error ? error.message : String(error));
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
if (response.status === 401) {
|
|
254
|
-
throw new RelayAuthError(await this.errorMessage(response));
|
|
255
|
-
}
|
|
256
|
-
if (response.status >= 500 && response.status <= 599) {
|
|
257
|
-
const message = await this.errorMessage(response);
|
|
258
|
-
if (attempt < HTTP_RETRY_ATTEMPTS) {
|
|
259
|
-
await sleep(Math.min(2 ** (attempt - 1) * 1_000, WS_RECONNECT_MAX_DELAY_MS));
|
|
260
|
-
continue;
|
|
261
|
-
}
|
|
262
|
-
throw new RelayConnectionError(response.status, message);
|
|
263
|
-
}
|
|
264
|
-
if (response.status >= 400) {
|
|
265
|
-
throw new RelayConnectionError(response.status, await this.errorMessage(response));
|
|
266
|
-
}
|
|
267
|
-
if (response.status === 204) {
|
|
268
|
-
return undefined as T;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const contentType = response.headers.get('content-type') ?? '';
|
|
272
|
-
if (contentType.includes('application/json')) {
|
|
273
|
-
return (await response.json()) as T;
|
|
274
|
-
}
|
|
275
|
-
return (await response.text()) as T;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
throw new RelayConnectionError(500, 'Unexpected transport retry failure');
|
|
279
|
-
}
|
|
280
|
-
|
|
281
173
|
private async sendHttp<T = unknown>(
|
|
282
174
|
method: string,
|
|
283
175
|
path: string,
|
|
@@ -354,7 +246,7 @@ export class RelayTransport {
|
|
|
354
246
|
return this.wsConnectPromise;
|
|
355
247
|
}
|
|
356
248
|
|
|
357
|
-
const url = `${this.wsBaseUrl()}/v1/ws?token=${encodeURIComponent(this.token ?? '')}`;
|
|
249
|
+
const url = `${this.wsBaseUrl()}/v1/ws/${this.agentId}?token=${encodeURIComponent(this.token ?? '')}`;
|
|
358
250
|
const socket = new WebSocket(url);
|
|
359
251
|
|
|
360
252
|
this.wsConnectPromise = new Promise<void>((resolve, reject) => {
|
|
@@ -437,12 +329,7 @@ export class RelayTransport {
|
|
|
437
329
|
this.ws?.send(JSON.stringify({ type: 'pong' }));
|
|
438
330
|
return;
|
|
439
331
|
}
|
|
440
|
-
|
|
441
|
-
const messageEvents = new Set([
|
|
442
|
-
'message.created', 'dm.received', 'direct_message.received',
|
|
443
|
-
'thread.reply', 'message', 'group_dm.received',
|
|
444
|
-
]);
|
|
445
|
-
if (!messageEvents.has(payload.type as string) || !this.messageCallback) {
|
|
332
|
+
if (payload.type !== 'message' || !this.messageCallback) {
|
|
446
333
|
return;
|
|
447
334
|
}
|
|
448
335
|
|
|
@@ -450,40 +337,22 @@ export class RelayTransport {
|
|
|
450
337
|
}
|
|
451
338
|
|
|
452
339
|
private messageFromPayload(payload: JsonObject): Message {
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
: payload
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
const text = String(m.text ?? '');
|
|
462
|
-
const channel = String(
|
|
463
|
-
m.channel ?? m.channel_name ?? m.channelName ?? payload.channel ?? payload.channel_name ?? '',
|
|
464
|
-
) || undefined;
|
|
465
|
-
const threadId = String(
|
|
466
|
-
m.thread_id ?? m.threadId ?? m.conversation_id ?? m.conversationId ?? payload.thread_id ?? '',
|
|
467
|
-
) || undefined;
|
|
468
|
-
const rawTs = m.timestamp ?? m.created_at ?? m.createdAt ?? payload.timestamp;
|
|
469
|
-
const timestamp = typeof rawTs === 'number' ? rawTs : undefined;
|
|
470
|
-
const messageId = String(
|
|
471
|
-
m.id ?? m.message_id ?? m.messageId ?? payload.message_id ?? '',
|
|
472
|
-
) || undefined;
|
|
473
|
-
|
|
474
|
-
return { sender, text, channel, threadId, timestamp, messageId };
|
|
340
|
+
return {
|
|
341
|
+
sender: String(payload.sender ?? ''),
|
|
342
|
+
text: String(payload.text ?? ''),
|
|
343
|
+
channel: typeof payload.channel === 'string' ? payload.channel : undefined,
|
|
344
|
+
threadId: typeof payload.thread_id === 'string' ? payload.thread_id : undefined,
|
|
345
|
+
timestamp: typeof payload.timestamp === 'number' ? payload.timestamp : undefined,
|
|
346
|
+
messageId: typeof payload.message_id === 'string' ? payload.message_id : undefined,
|
|
347
|
+
};
|
|
475
348
|
}
|
|
476
349
|
|
|
477
350
|
private async errorMessage(response: Response): Promise<string> {
|
|
478
|
-
const text = await response.text().catch(() => '');
|
|
479
351
|
try {
|
|
480
|
-
const payload =
|
|
481
|
-
if (typeof payload.error === 'object' && payload.error?.message) {
|
|
482
|
-
return payload.error.message;
|
|
483
|
-
}
|
|
352
|
+
const payload = (await response.json()) as { message?: string };
|
|
484
353
|
return payload.message ?? response.statusText ?? 'Request failed';
|
|
485
354
|
} catch {
|
|
486
|
-
return text || response.statusText || 'Request failed';
|
|
355
|
+
return (await response.text()) || response.statusText || 'Request failed';
|
|
487
356
|
}
|
|
488
357
|
}
|
|
489
358
|
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lightweight HTTP client for Relaycast API — safe for bundlers (Vite, Rollup, Webpack).
|
|
3
|
+
* No native dependencies, no broker binary, no process spawning.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* import { RelayCast, createWorkspace, registerAgent } from '@agent-relay/sdk/http';
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// Re-export the HTTP-safe parts of @relaycast/sdk
|
|
10
|
+
export { RelayCast, RelayError } from '@relaycast/sdk';
|
|
11
|
+
export type { RelayCastOptions, CreateWorkspaceResponse } from '@relaycast/sdk';
|
|
12
|
+
|
|
13
|
+
// Re-export useful protocol types from the SDK
|
|
14
|
+
export type { AgentSpec, AgentRuntime } from './protocol.js';
|
|
15
|
+
|
|
16
|
+
const DEFAULT_BASE_URL = 'https://api.relaycast.dev';
|
|
17
|
+
|
|
18
|
+
interface CreateWorkspaceResult {
|
|
19
|
+
workspace_id: string;
|
|
20
|
+
api_key: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface RegisterAgentResult {
|
|
24
|
+
name: string;
|
|
25
|
+
token: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Create a new Relaycast workspace using plain fetch.
|
|
30
|
+
* This function has zero dependencies beyond the Fetch API.
|
|
31
|
+
*
|
|
32
|
+
* @param name - Human-readable workspace name
|
|
33
|
+
* @param baseUrl - API base URL (defaults to https://api.relaycast.dev)
|
|
34
|
+
* @returns The workspace ID and API key
|
|
35
|
+
*/
|
|
36
|
+
export async function createWorkspace(
|
|
37
|
+
name: string,
|
|
38
|
+
baseUrl: string = DEFAULT_BASE_URL,
|
|
39
|
+
): Promise<CreateWorkspaceResult> {
|
|
40
|
+
const url = `${baseUrl}/v1/workspaces`;
|
|
41
|
+
const response = await fetch(url, {
|
|
42
|
+
method: 'POST',
|
|
43
|
+
headers: { 'Content-Type': 'application/json' },
|
|
44
|
+
body: JSON.stringify({ name }),
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
if (!response.ok) {
|
|
48
|
+
const body = await response.text().catch(() => '');
|
|
49
|
+
throw new Error(
|
|
50
|
+
`Failed to create workspace (HTTP ${response.status}): ${body}`,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const body = (await response.json()) as
|
|
55
|
+
| CreateWorkspaceResult
|
|
56
|
+
| { ok: boolean; data: CreateWorkspaceResult };
|
|
57
|
+
// The API wraps responses in { ok, data } — unwrap transparently
|
|
58
|
+
return 'data' in body && body.data ? body.data : body as CreateWorkspaceResult;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Register an agent in a workspace using plain fetch.
|
|
63
|
+
* This function has zero dependencies beyond the Fetch API.
|
|
64
|
+
*
|
|
65
|
+
* @param apiKey - Workspace API key
|
|
66
|
+
* @param name - Agent name to register
|
|
67
|
+
* @param baseUrl - API base URL (defaults to https://api.relaycast.dev)
|
|
68
|
+
* @returns The registered agent name and token
|
|
69
|
+
*/
|
|
70
|
+
export async function registerAgent(
|
|
71
|
+
apiKey: string,
|
|
72
|
+
name: string,
|
|
73
|
+
baseUrl: string = DEFAULT_BASE_URL,
|
|
74
|
+
): Promise<RegisterAgentResult> {
|
|
75
|
+
const url = `${baseUrl}/v1/agents`;
|
|
76
|
+
const response = await fetch(url, {
|
|
77
|
+
method: 'POST',
|
|
78
|
+
headers: {
|
|
79
|
+
'Content-Type': 'application/json',
|
|
80
|
+
Authorization: `Bearer ${apiKey}`,
|
|
81
|
+
},
|
|
82
|
+
body: JSON.stringify({ name }),
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
if (!response.ok) {
|
|
86
|
+
const errBody = await response.text().catch(() => '');
|
|
87
|
+
throw new Error(
|
|
88
|
+
`Failed to register agent (HTTP ${response.status}): ${errBody}`,
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const body = (await response.json()) as
|
|
93
|
+
| RegisterAgentResult
|
|
94
|
+
| { ok: boolean; data: RegisterAgentResult };
|
|
95
|
+
return 'data' in body && body.data ? body.data : body as RegisterAgentResult;
|
|
96
|
+
}
|
|
@@ -25,6 +25,8 @@ export interface AgentSpec {
|
|
|
25
25
|
restart_policy?: RestartPolicy;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
export type MessageInjectionMode = 'wait' | 'steer';
|
|
29
|
+
|
|
28
30
|
export interface RelayDelivery {
|
|
29
31
|
delivery_id: string;
|
|
30
32
|
event_id: string;
|
|
@@ -35,6 +37,7 @@ export interface RelayDelivery {
|
|
|
35
37
|
body: string;
|
|
36
38
|
thread_id?: string;
|
|
37
39
|
priority?: number;
|
|
40
|
+
injection_mode?: MessageInjectionMode;
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
export interface ProtocolEnvelope<TPayload> {
|
|
@@ -64,6 +67,7 @@ export type SdkToBroker =
|
|
|
64
67
|
workspace_alias?: string;
|
|
65
68
|
priority?: number;
|
|
66
69
|
data?: Record<string, unknown>;
|
|
70
|
+
mode?: MessageInjectionMode;
|
|
67
71
|
};
|
|
68
72
|
}
|
|
69
73
|
| {
|
|
@@ -74,6 +78,14 @@ export type SdkToBroker =
|
|
|
74
78
|
type: 'send_input';
|
|
75
79
|
payload: { name: string; data: string };
|
|
76
80
|
}
|
|
81
|
+
| {
|
|
82
|
+
type: 'subscribe_channels';
|
|
83
|
+
payload: { name: string; channels: string[] };
|
|
84
|
+
}
|
|
85
|
+
| {
|
|
86
|
+
type: 'unsubscribe_channels';
|
|
87
|
+
payload: { name: string; channels: string[] };
|
|
88
|
+
}
|
|
77
89
|
| {
|
|
78
90
|
type: 'set_model';
|
|
79
91
|
payload: { name: string; model: string; timeout_ms?: number };
|
|
@@ -221,6 +233,8 @@ export type BrokerEvent =
|
|
|
221
233
|
target: string;
|
|
222
234
|
body: string;
|
|
223
235
|
thread_id?: string;
|
|
236
|
+
mode?: MessageInjectionMode;
|
|
237
|
+
injection_mode?: MessageInjectionMode;
|
|
224
238
|
}
|
|
225
239
|
| {
|
|
226
240
|
kind: 'worker_stream';
|
|
@@ -280,6 +294,16 @@ export type BrokerEvent =
|
|
|
280
294
|
delivery_id: string;
|
|
281
295
|
event_id: string;
|
|
282
296
|
}
|
|
297
|
+
| {
|
|
298
|
+
kind: 'channel_subscribed';
|
|
299
|
+
name: string;
|
|
300
|
+
channels: string[];
|
|
301
|
+
}
|
|
302
|
+
| {
|
|
303
|
+
kind: 'channel_unsubscribed';
|
|
304
|
+
name: string;
|
|
305
|
+
channels: string[];
|
|
306
|
+
}
|
|
283
307
|
| {
|
|
284
308
|
kind: 'worker_ready';
|
|
285
309
|
name: string;
|