agent-relay 3.2.15 → 3.2.17
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 +3865 -17179
- package/dist/src/cli/commands/setup.d.ts.map +1 -1
- package/dist/src/cli/commands/setup.js +2 -0
- package/dist/src/cli/commands/setup.js.map +1 -1
- 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 +4 -0
- 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 +169 -28
- 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/broker-path.ts +74 -0
- package/packages/sdk/src/cli-registry.ts +4 -0
- 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 +197 -30
- 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/builder.py +64 -7
- 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/src/agent_relay/types.py +1 -0
- 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_builder.py +58 -0
- package/packages/sdk-py/tests/test_dry_run.py +215 -0
- package/packages/sdk-py/tests/test_send_message_mode.py +91 -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
|
@@ -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;
|