@slashfi/agents-sdk 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-definitions/auth.d.ts.map +1 -1
- package/dist/agent-definitions/auth.js +44 -11
- package/dist/agent-definitions/auth.js.map +1 -1
- package/dist/agent-definitions/integrations.d.ts.map +1 -1
- package/dist/agent-definitions/integrations.js +106 -45
- package/dist/agent-definitions/integrations.js.map +1 -1
- package/dist/agent-definitions/remote-registry.d.ts.map +1 -1
- package/dist/agent-definitions/remote-registry.js +174 -45
- package/dist/agent-definitions/remote-registry.js.map +1 -1
- package/dist/agent-definitions/secrets.d.ts.map +1 -1
- package/dist/agent-definitions/secrets.js +1 -4
- package/dist/agent-definitions/secrets.js.map +1 -1
- package/dist/agent-definitions/users.d.ts.map +1 -1
- package/dist/agent-definitions/users.js +14 -3
- package/dist/agent-definitions/users.js.map +1 -1
- package/dist/define-config.d.ts +125 -0
- package/dist/define-config.d.ts.map +1 -0
- package/dist/define-config.js +75 -0
- package/dist/define-config.js.map +1 -0
- package/dist/define.d.ts +11 -2
- package/dist/define.d.ts.map +1 -1
- package/dist/define.js +57 -26
- package/dist/define.js.map +1 -1
- package/dist/events.d.ts +133 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +57 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +16 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -3
- package/dist/index.js.map +1 -1
- package/dist/integration-interface.d.ts +3 -3
- package/dist/integration-interface.d.ts.map +1 -1
- package/dist/integration-interface.js +29 -21
- package/dist/integration-interface.js.map +1 -1
- package/dist/integrations-store.d.ts +2 -2
- package/dist/integrations-store.d.ts.map +1 -1
- package/dist/integrations-store.js +3 -3
- package/dist/integrations-store.js.map +1 -1
- package/dist/jwt.d.ts.map +1 -1
- package/dist/jwt.js +7 -5
- package/dist/jwt.js.map +1 -1
- package/dist/key-manager.d.ts.map +1 -1
- package/dist/key-manager.js +5 -3
- package/dist/key-manager.js.map +1 -1
- package/dist/oidc-signin.d.ts +32 -0
- package/dist/oidc-signin.d.ts.map +1 -0
- package/dist/oidc-signin.js +138 -0
- package/dist/oidc-signin.js.map +1 -0
- package/dist/registry-consumer.d.ts +104 -0
- package/dist/registry-consumer.d.ts.map +1 -0
- package/dist/registry-consumer.js +230 -0
- package/dist/registry-consumer.js.map +1 -0
- package/dist/registry.d.ts +5 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +76 -4
- package/dist/registry.js.map +1 -1
- package/dist/secret-collection.d.ts.map +1 -1
- package/dist/secret-collection.js.map +1 -1
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +222 -27
- package/dist/server.js.map +1 -1
- package/dist/test-utils/mock-oidc-server.d.ts +36 -0
- package/dist/test-utils/mock-oidc-server.d.ts.map +1 -0
- package/dist/test-utils/mock-oidc-server.js +96 -0
- package/dist/test-utils/mock-oidc-server.js.map +1 -0
- package/dist/types.d.ts +106 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -1
- package/src/agent-definitions/auth.ts +106 -38
- package/src/agent-definitions/integrations.ts +201 -73
- package/src/agent-definitions/remote-registry.ts +262 -65
- package/src/agent-definitions/secrets.ts +22 -8
- package/src/agent-definitions/users.ts +16 -4
- package/src/cli.ts +293 -0
- package/src/codegen.test.ts +527 -0
- package/src/codegen.ts +1348 -0
- package/src/consumer.test.ts +536 -0
- package/src/define-config.ts +205 -0
- package/src/define.ts +134 -46
- package/src/events.ts +237 -0
- package/src/index.ts +107 -8
- package/src/integration-interface.ts +52 -28
- package/src/integrations-store.ts +9 -5
- package/src/jwt.ts +48 -19
- package/src/key-manager.test.ts +22 -13
- package/src/key-manager.ts +8 -10
- package/src/oidc-signin.ts +223 -0
- package/src/registry-consumer.ts +413 -0
- package/src/registry.ts +115 -9
- package/src/secret-collection.ts +2 -1
- package/src/server.test.ts +304 -238
- package/src/server.ts +371 -69
- package/src/test-utils/mock-oidc-server.ts +123 -0
- package/src/types.ts +172 -18
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* defineConfig — Declarative configuration for agent consumers.
|
|
3
|
+
*
|
|
4
|
+
* A consumer's config declares which registries to connect to and which
|
|
5
|
+
* agent refs to use. This is the "package.json" of the agent world:
|
|
6
|
+
* registries are like npm registries, refs are like dependencies.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { defineConfig } from '@slashfi/agents-sdk';
|
|
11
|
+
*
|
|
12
|
+
* export default defineConfig({
|
|
13
|
+
* registries: [
|
|
14
|
+
* { url: 'https://registry.slash.com' },
|
|
15
|
+
* { url: 'https://twin.slash.com/tenants/slash', auth: { type: 'bearer' } },
|
|
16
|
+
* ],
|
|
17
|
+
* refs: [
|
|
18
|
+
* 'notion',
|
|
19
|
+
* { ref: 'postgres', as: 'prod-db', config: { url: 'https://twin.slash.com/secrets/crdb-url' } },
|
|
20
|
+
* { ref: 'postgres', as: 'staging', config: { url: 'https://twin.slash.com/secrets/staging-url' } },
|
|
21
|
+
* ],
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
// ============================================
|
|
27
|
+
// Registry Config
|
|
28
|
+
// ============================================
|
|
29
|
+
|
|
30
|
+
/** Authentication methods for connecting to a registry */
|
|
31
|
+
export type RegistryAuth =
|
|
32
|
+
| { type: "none" }
|
|
33
|
+
| { type: "bearer"; token?: string; tokenUrl?: string }
|
|
34
|
+
| { type: "api-key"; key?: string; header?: string }
|
|
35
|
+
| { type: "jwt"; issuer?: string };
|
|
36
|
+
|
|
37
|
+
/** A registry endpoint the consumer connects to */
|
|
38
|
+
export interface RegistryEntry {
|
|
39
|
+
/** Registry URL (e.g., 'https://registry.slash.com') */
|
|
40
|
+
url: string;
|
|
41
|
+
|
|
42
|
+
/** How to authenticate with this registry */
|
|
43
|
+
auth?: RegistryAuth;
|
|
44
|
+
|
|
45
|
+
/** Human-readable name / alias for this registry */
|
|
46
|
+
name?: string;
|
|
47
|
+
|
|
48
|
+
/** Publisher name shown in the app store UI */
|
|
49
|
+
publisher?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ============================================
|
|
53
|
+
// Ref Config
|
|
54
|
+
// ============================================
|
|
55
|
+
|
|
56
|
+
/** Inline config for a ref — values can be literals or secret URLs */
|
|
57
|
+
export type RefConfig = Record<string, string | number | boolean>;
|
|
58
|
+
|
|
59
|
+
/** A ref can be a simple string or a full object */
|
|
60
|
+
export type RefEntry =
|
|
61
|
+
| string
|
|
62
|
+
| {
|
|
63
|
+
/** Agent definition path (resolved from registries) */
|
|
64
|
+
ref: string;
|
|
65
|
+
|
|
66
|
+
/** Direct URL to the agent (e.g. http://localhost:3000/agents/notion) */
|
|
67
|
+
url?: string;
|
|
68
|
+
|
|
69
|
+
/** Local alias for this instance (required for multi-instance) */
|
|
70
|
+
as?: string;
|
|
71
|
+
|
|
72
|
+
/** Per-instance config (secrets as URIs, literals as values) */
|
|
73
|
+
config?: RefConfig;
|
|
74
|
+
|
|
75
|
+
/** Override the registry to resolve from */
|
|
76
|
+
registry?: string;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// ============================================
|
|
80
|
+
// Consumer Config
|
|
81
|
+
// ============================================
|
|
82
|
+
|
|
83
|
+
/** The full consumer configuration */
|
|
84
|
+
export interface ConsumerConfig {
|
|
85
|
+
/** Registries to connect to, in resolution order */
|
|
86
|
+
registries?: (string | RegistryEntry)[];
|
|
87
|
+
|
|
88
|
+
/** Agent refs to use — your "dependencies" */
|
|
89
|
+
refs?: RefEntry[];
|
|
90
|
+
|
|
91
|
+
/** Optional metadata */
|
|
92
|
+
meta?: {
|
|
93
|
+
/** Config owner (user ID, tenant ID, etc.) */
|
|
94
|
+
owner?: string;
|
|
95
|
+
/** Human-readable description */
|
|
96
|
+
description?: string;
|
|
97
|
+
[key: string]: unknown;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ============================================
|
|
102
|
+
// Resolved Config (indexed output)
|
|
103
|
+
// ============================================
|
|
104
|
+
|
|
105
|
+
/** A normalized registry entry (after resolution) */
|
|
106
|
+
export interface ResolvedRegistry {
|
|
107
|
+
url: string;
|
|
108
|
+
name: string;
|
|
109
|
+
publisher: string;
|
|
110
|
+
auth: RegistryAuth;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** A normalized ref entry (after resolution) */
|
|
114
|
+
export interface ResolvedRef {
|
|
115
|
+
/** Original ref name from the definition */
|
|
116
|
+
ref: string;
|
|
117
|
+
|
|
118
|
+
/** Local name (alias or ref name) */
|
|
119
|
+
name: string;
|
|
120
|
+
|
|
121
|
+
/** Which registry this was resolved from */
|
|
122
|
+
registry: string;
|
|
123
|
+
|
|
124
|
+
/** Resolved config (secret URLs NOT resolved — kept as URLs) */
|
|
125
|
+
config: RefConfig;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** The serialized/indexed output stored in VCS */
|
|
129
|
+
export interface ResolvedConfig {
|
|
130
|
+
/** Timestamp of resolution */
|
|
131
|
+
resolvedAt: string;
|
|
132
|
+
|
|
133
|
+
/** Source config hash (for cache invalidation) */
|
|
134
|
+
sourceHash: string;
|
|
135
|
+
|
|
136
|
+
/** Normalized registries */
|
|
137
|
+
registries: ResolvedRegistry[];
|
|
138
|
+
|
|
139
|
+
/** Normalized refs */
|
|
140
|
+
refs: ResolvedRef[];
|
|
141
|
+
|
|
142
|
+
/** Metadata */
|
|
143
|
+
meta?: ConsumerConfig["meta"];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ============================================
|
|
147
|
+
// Helpers
|
|
148
|
+
// ============================================
|
|
149
|
+
|
|
150
|
+
/** Normalize a ref entry to its full form */
|
|
151
|
+
export function normalizeRef(entry: RefEntry): {
|
|
152
|
+
ref: string;
|
|
153
|
+
name: string;
|
|
154
|
+
config: RefConfig;
|
|
155
|
+
registry?: string;
|
|
156
|
+
} {
|
|
157
|
+
if (typeof entry === "string") {
|
|
158
|
+
return { ref: entry, name: entry, config: {} };
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
ref: entry.ref,
|
|
162
|
+
name: entry.as ?? entry.ref,
|
|
163
|
+
config: entry.config ?? {},
|
|
164
|
+
registry: entry.registry,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** Normalize a registry entry to its full form */
|
|
169
|
+
export function normalizeRegistry(
|
|
170
|
+
entry: string | RegistryEntry,
|
|
171
|
+
): ResolvedRegistry {
|
|
172
|
+
if (typeof entry === "string") {
|
|
173
|
+
const url = new URL(entry);
|
|
174
|
+
return {
|
|
175
|
+
url: entry,
|
|
176
|
+
name: url.hostname,
|
|
177
|
+
publisher: url.hostname.split(".")[0],
|
|
178
|
+
auth: { type: "none" },
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
const url = new URL(entry.url);
|
|
182
|
+
return {
|
|
183
|
+
url: entry.url,
|
|
184
|
+
name: entry.name ?? url.hostname,
|
|
185
|
+
publisher: entry.publisher ?? url.hostname.split(".")[0],
|
|
186
|
+
auth: entry.auth ?? { type: "none" },
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Supported secret URI schemes */
|
|
191
|
+
const SECRET_SCHEMES = ["file:", "https:", "http:", "env:"];
|
|
192
|
+
|
|
193
|
+
/** Check if a value is a secret URI (file://, https://, env://) */
|
|
194
|
+
export function isSecretUri(value: unknown): boolean {
|
|
195
|
+
if (typeof value !== "string") return false;
|
|
196
|
+
try {
|
|
197
|
+
const url = new URL(value);
|
|
198
|
+
return SECRET_SCHEMES.includes(url.protocol);
|
|
199
|
+
} catch {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** @deprecated Use isSecretUri instead */
|
|
205
|
+
export const isSecretUrl = isSecretUri;
|
package/src/define.ts
CHANGED
|
@@ -4,12 +4,14 @@
|
|
|
4
4
|
* Factory functions for creating agent and tool definitions.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import type { EventCallback, EventType } from "./events.js";
|
|
7
8
|
import type {
|
|
8
|
-
IntegrationHooks,
|
|
9
9
|
AgentConfig,
|
|
10
10
|
AgentDefinition,
|
|
11
11
|
AgentRuntime,
|
|
12
|
+
IntegrationHooks,
|
|
12
13
|
JsonSchema,
|
|
14
|
+
ListenerEntry,
|
|
13
15
|
ToolContext,
|
|
14
16
|
ToolDefinition,
|
|
15
17
|
Visibility,
|
|
@@ -65,14 +67,37 @@ export interface DefineToolOptions<
|
|
|
65
67
|
* });
|
|
66
68
|
* ```
|
|
67
69
|
*/
|
|
70
|
+
/** A ToolDefinition with .on() chaining support */
|
|
71
|
+
export type ToolWithHooks<
|
|
72
|
+
TContext extends ToolContext = ToolContext,
|
|
73
|
+
TInput = unknown,
|
|
74
|
+
TOutput = unknown,
|
|
75
|
+
> = ToolDefinition<TContext, TInput, TOutput> & {
|
|
76
|
+
on<T extends EventType>(
|
|
77
|
+
eventType: T,
|
|
78
|
+
callback: EventCallback<T>,
|
|
79
|
+
): ToolWithHooks<TContext, TInput, TOutput>;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/** An AgentDefinition with .on() chaining support */
|
|
83
|
+
export type AgentWithHooks<TContext extends ToolContext = ToolContext> =
|
|
84
|
+
AgentDefinition<TContext> & {
|
|
85
|
+
on<T extends EventType>(
|
|
86
|
+
eventType: T,
|
|
87
|
+
callback: EventCallback<T>,
|
|
88
|
+
): AgentWithHooks<TContext>;
|
|
89
|
+
};
|
|
90
|
+
|
|
68
91
|
export function defineTool<
|
|
69
92
|
TContext extends ToolContext = ToolContext,
|
|
70
93
|
TInput = unknown,
|
|
71
94
|
TOutput = unknown,
|
|
72
95
|
>(
|
|
73
96
|
options: DefineToolOptions<TContext, TInput, TOutput>,
|
|
74
|
-
):
|
|
75
|
-
|
|
97
|
+
): ToolWithHooks<TContext, TInput, TOutput> {
|
|
98
|
+
const listeners: ListenerEntry[] = [];
|
|
99
|
+
|
|
100
|
+
const def: ToolWithHooks<TContext, TInput, TOutput> = {
|
|
76
101
|
name: options.name,
|
|
77
102
|
description: options.description,
|
|
78
103
|
inputSchema: options.inputSchema,
|
|
@@ -80,7 +105,17 @@ export function defineTool<
|
|
|
80
105
|
visibility: options.visibility,
|
|
81
106
|
allowedCallers: options.allowedCallers,
|
|
82
107
|
execute: options.execute,
|
|
108
|
+
_listeners: listeners,
|
|
109
|
+
on<T extends EventType>(eventType: T, callback: EventCallback<T>) {
|
|
110
|
+
listeners.push({
|
|
111
|
+
eventType,
|
|
112
|
+
callback: callback as EventCallback<EventType>,
|
|
113
|
+
});
|
|
114
|
+
return def;
|
|
115
|
+
},
|
|
83
116
|
};
|
|
117
|
+
|
|
118
|
+
return def;
|
|
84
119
|
}
|
|
85
120
|
|
|
86
121
|
// ============================================
|
|
@@ -179,68 +214,111 @@ export function defineAgent<TContext extends ToolContext = ToolContext>(
|
|
|
179
214
|
|
|
180
215
|
if (h.setup) {
|
|
181
216
|
const fn = h.setup;
|
|
182
|
-
tools.push(
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
217
|
+
tools.push(
|
|
218
|
+
defineTool({
|
|
219
|
+
name: "setup_integration",
|
|
220
|
+
description: `Set up ${h.displayName} integration.`,
|
|
221
|
+
visibility: "public" as const,
|
|
222
|
+
inputSchema: {
|
|
223
|
+
type: "object" as const,
|
|
224
|
+
properties: {
|
|
225
|
+
url: { type: "string" },
|
|
226
|
+
name: { type: "string" },
|
|
227
|
+
config: { type: "object" },
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
execute: (input: any, ctx: any) => fn(input, ctx),
|
|
231
|
+
}) as any,
|
|
232
|
+
);
|
|
189
233
|
}
|
|
190
234
|
if (h.connect) {
|
|
191
235
|
const fn = h.connect;
|
|
192
|
-
tools.push(
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
236
|
+
tools.push(
|
|
237
|
+
defineTool({
|
|
238
|
+
name: "connect_integration",
|
|
239
|
+
description: `Connect a user to ${h.displayName}.`,
|
|
240
|
+
visibility: "public" as const,
|
|
241
|
+
inputSchema: {
|
|
242
|
+
type: "object" as const,
|
|
243
|
+
properties: {
|
|
244
|
+
registryId: { type: "string" },
|
|
245
|
+
oidcUserId: { type: "string" },
|
|
246
|
+
redirectUri: { type: "string" },
|
|
247
|
+
},
|
|
248
|
+
required: ["registryId"] as const,
|
|
249
|
+
},
|
|
250
|
+
execute: (input: any, ctx: any) => fn(input, ctx),
|
|
251
|
+
}) as any,
|
|
252
|
+
);
|
|
199
253
|
}
|
|
200
254
|
if (h.discover) {
|
|
201
255
|
const fn = h.discover;
|
|
202
|
-
tools.push(
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
256
|
+
tools.push(
|
|
257
|
+
defineTool({
|
|
258
|
+
name: "discover_integrations",
|
|
259
|
+
description: `Discover available ${h.displayName} instances.`,
|
|
260
|
+
visibility: "public" as const,
|
|
261
|
+
inputSchema: {
|
|
262
|
+
type: "object" as const,
|
|
263
|
+
properties: { url: { type: "string" } },
|
|
264
|
+
},
|
|
265
|
+
execute: (input: any, ctx: any) => fn(input, ctx),
|
|
266
|
+
}) as any,
|
|
267
|
+
);
|
|
209
268
|
}
|
|
210
269
|
if (h.list) {
|
|
211
270
|
const fn = h.list;
|
|
212
|
-
tools.push(
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
271
|
+
tools.push(
|
|
272
|
+
defineTool({
|
|
273
|
+
name: "list_integrations",
|
|
274
|
+
description: `List connected ${h.displayName} instances.`,
|
|
275
|
+
visibility: "public" as const,
|
|
276
|
+
inputSchema: { type: "object" as const, properties: {} },
|
|
277
|
+
execute: (input: any, ctx: any) => fn(input, ctx),
|
|
278
|
+
}) as any,
|
|
279
|
+
);
|
|
219
280
|
}
|
|
220
281
|
if (h.get) {
|
|
221
282
|
const fn = h.get;
|
|
222
|
-
tools.push(
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
283
|
+
tools.push(
|
|
284
|
+
defineTool({
|
|
285
|
+
name: "get_integration",
|
|
286
|
+
description: `Get details of a ${h.displayName} instance.`,
|
|
287
|
+
visibility: "public" as const,
|
|
288
|
+
inputSchema: {
|
|
289
|
+
type: "object" as const,
|
|
290
|
+
properties: { registryId: { type: "string" } },
|
|
291
|
+
required: ["registryId"] as const,
|
|
292
|
+
},
|
|
293
|
+
execute: (input: any, ctx: any) => fn(input, ctx),
|
|
294
|
+
}) as any,
|
|
295
|
+
);
|
|
229
296
|
}
|
|
230
297
|
if (h.update) {
|
|
231
298
|
const fn = h.update;
|
|
232
|
-
tools.push(
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
299
|
+
tools.push(
|
|
300
|
+
defineTool({
|
|
301
|
+
name: "update_integration",
|
|
302
|
+
description: `Update a ${h.displayName} instance.`,
|
|
303
|
+
visibility: "public" as const,
|
|
304
|
+
inputSchema: {
|
|
305
|
+
type: "object" as const,
|
|
306
|
+
properties: {
|
|
307
|
+
registryId: { type: "string" },
|
|
308
|
+
name: { type: "string" },
|
|
309
|
+
url: { type: "string" },
|
|
310
|
+
},
|
|
311
|
+
required: ["registryId"] as const,
|
|
312
|
+
},
|
|
313
|
+
execute: (input: any, ctx: any) => fn(input, ctx),
|
|
314
|
+
}) as any,
|
|
315
|
+
);
|
|
239
316
|
}
|
|
240
317
|
}
|
|
241
318
|
|
|
319
|
+
const agentListeners: ListenerEntry[] = [];
|
|
242
320
|
|
|
243
|
-
|
|
321
|
+
const def: AgentWithHooks<TContext> = {
|
|
244
322
|
path: options.path,
|
|
245
323
|
entrypoint: options.entrypoint,
|
|
246
324
|
config,
|
|
@@ -249,5 +327,15 @@ export function defineAgent<TContext extends ToolContext = ToolContext>(
|
|
|
249
327
|
visibility: options.visibility,
|
|
250
328
|
allowedCallers: options.allowedCallers,
|
|
251
329
|
loadListeners: options.loadListeners,
|
|
330
|
+
_listeners: agentListeners,
|
|
331
|
+
on<T extends EventType>(eventType: T, callback: EventCallback<T>) {
|
|
332
|
+
agentListeners.push({
|
|
333
|
+
eventType,
|
|
334
|
+
callback: callback as EventCallback<EventType>,
|
|
335
|
+
});
|
|
336
|
+
return def;
|
|
337
|
+
},
|
|
252
338
|
};
|
|
339
|
+
|
|
340
|
+
return def;
|
|
253
341
|
}
|
package/src/events.ts
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event Bus — Generic event system for the agents-sdk.
|
|
3
|
+
*
|
|
4
|
+
* Three scopes, one bus:
|
|
5
|
+
* registry.on(event, cb) — global, all agents
|
|
6
|
+
* agent.on(event, cb) — scoped to one agent
|
|
7
|
+
* tool.on(event, cb) — scoped to one tool
|
|
8
|
+
*
|
|
9
|
+
* All are sugar for the same underlying EventBus.
|
|
10
|
+
* Filtering happens in the callback, not the API.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// =============================================================================
|
|
14
|
+
// Event Types
|
|
15
|
+
// =============================================================================
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* All supported event types.
|
|
19
|
+
*/
|
|
20
|
+
export type EventType =
|
|
21
|
+
| "tool/call"
|
|
22
|
+
| "tool/result"
|
|
23
|
+
| "tool/error"
|
|
24
|
+
| "step"
|
|
25
|
+
| "invoke";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Base event shape — every event has these fields.
|
|
29
|
+
*/
|
|
30
|
+
export interface BaseEvent {
|
|
31
|
+
/** Event type */
|
|
32
|
+
type: EventType;
|
|
33
|
+
/** Agent path (e.g., '/agents/atlas-slack') */
|
|
34
|
+
agentPath: string;
|
|
35
|
+
/** Timestamp */
|
|
36
|
+
timestamp: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Event emitted before a tool executes.
|
|
41
|
+
*/
|
|
42
|
+
export interface ToolCallEvent extends BaseEvent {
|
|
43
|
+
type: "tool/call";
|
|
44
|
+
/** Tool name */
|
|
45
|
+
tool: string;
|
|
46
|
+
/** Input parameters */
|
|
47
|
+
params: unknown;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Event emitted after a tool succeeds.
|
|
52
|
+
*/
|
|
53
|
+
export interface ToolResultEvent extends BaseEvent {
|
|
54
|
+
type: "tool/result";
|
|
55
|
+
/** Tool name */
|
|
56
|
+
tool: string;
|
|
57
|
+
/** Input parameters */
|
|
58
|
+
params: unknown;
|
|
59
|
+
/** Tool result */
|
|
60
|
+
result: unknown;
|
|
61
|
+
/** Execution duration in ms */
|
|
62
|
+
durationMs: number;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Event emitted when a tool throws.
|
|
67
|
+
*/
|
|
68
|
+
export interface ToolErrorEvent extends BaseEvent {
|
|
69
|
+
type: "tool/error";
|
|
70
|
+
/** Tool name */
|
|
71
|
+
tool: string;
|
|
72
|
+
/** Input parameters */
|
|
73
|
+
params: unknown;
|
|
74
|
+
/** The error */
|
|
75
|
+
error: unknown;
|
|
76
|
+
/** Execution duration in ms */
|
|
77
|
+
durationMs: number;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Event emitted when a step finishes.
|
|
82
|
+
*/
|
|
83
|
+
export interface StepEvent extends BaseEvent {
|
|
84
|
+
type: "step";
|
|
85
|
+
/** Branch ID */
|
|
86
|
+
branchId: string;
|
|
87
|
+
/** Step outcome */
|
|
88
|
+
stepResult: "continue" | "stop";
|
|
89
|
+
/** Tools called in this step */
|
|
90
|
+
toolNames?: string[];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Event emitted when an agent is invoked.
|
|
95
|
+
*/
|
|
96
|
+
export interface InvokeEvent extends BaseEvent {
|
|
97
|
+
type: "invoke";
|
|
98
|
+
/** The prompt */
|
|
99
|
+
prompt: string;
|
|
100
|
+
/** Session/branch ID */
|
|
101
|
+
sessionId?: string;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Union of all event types.
|
|
106
|
+
*/
|
|
107
|
+
export type AgentEvent =
|
|
108
|
+
| ToolCallEvent
|
|
109
|
+
| ToolResultEvent
|
|
110
|
+
| ToolErrorEvent
|
|
111
|
+
| StepEvent
|
|
112
|
+
| InvokeEvent;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Map from event type string to event interface.
|
|
116
|
+
*/
|
|
117
|
+
export interface EventMap {
|
|
118
|
+
"tool/call": ToolCallEvent;
|
|
119
|
+
"tool/result": ToolResultEvent;
|
|
120
|
+
"tool/error": ToolErrorEvent;
|
|
121
|
+
step: StepEvent;
|
|
122
|
+
invoke: InvokeEvent;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Callback for a specific event type.
|
|
127
|
+
*/
|
|
128
|
+
export type EventCallback<T extends EventType = EventType> = (
|
|
129
|
+
event: EventMap[T],
|
|
130
|
+
) => void | Promise<void>;
|
|
131
|
+
|
|
132
|
+
// =============================================================================
|
|
133
|
+
// Event Bus
|
|
134
|
+
// =============================================================================
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Listener entry — callback + optional scope for agent/tool filtering.
|
|
138
|
+
*/
|
|
139
|
+
interface ListenerEntry {
|
|
140
|
+
eventType: EventType;
|
|
141
|
+
callback: EventCallback<EventType>;
|
|
142
|
+
/** If set, only fire for events matching this agent path */
|
|
143
|
+
agentScope?: string;
|
|
144
|
+
/** If set, only fire for tool events matching this tool name */
|
|
145
|
+
toolScope?: string;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export interface EventBus {
|
|
149
|
+
/**
|
|
150
|
+
* Register a listener for an event type.
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```ts
|
|
154
|
+
* bus.on('tool/result', (event) => { ... })
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
157
|
+
on<T extends EventType>(eventType: T, callback: EventCallback<T>): void;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Emit an event to all matching listeners.
|
|
161
|
+
* Listeners are called in registration order.
|
|
162
|
+
* Errors in listeners are caught and logged, never propagated.
|
|
163
|
+
*/
|
|
164
|
+
emit(event: AgentEvent): Promise<void>;
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Register a scoped listener (used internally by agent.on / tool.on).
|
|
168
|
+
*/
|
|
169
|
+
_onScoped<T extends EventType>(
|
|
170
|
+
eventType: T,
|
|
171
|
+
callback: EventCallback<T>,
|
|
172
|
+
scope: { agentPath?: string; toolName?: string },
|
|
173
|
+
): void;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Create an event bus.
|
|
178
|
+
*/
|
|
179
|
+
export function createEventBus(): EventBus {
|
|
180
|
+
const listeners: ListenerEntry[] = [];
|
|
181
|
+
|
|
182
|
+
function on<T extends EventType>(
|
|
183
|
+
eventType: T,
|
|
184
|
+
callback: EventCallback<T>,
|
|
185
|
+
): void {
|
|
186
|
+
listeners.push({
|
|
187
|
+
eventType,
|
|
188
|
+
callback: callback as EventCallback<EventType>,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function _onScoped<T extends EventType>(
|
|
193
|
+
eventType: T,
|
|
194
|
+
callback: EventCallback<T>,
|
|
195
|
+
scope: { agentPath?: string; toolName?: string },
|
|
196
|
+
): void {
|
|
197
|
+
listeners.push({
|
|
198
|
+
eventType,
|
|
199
|
+
callback: callback as EventCallback<EventType>,
|
|
200
|
+
agentScope: scope.agentPath,
|
|
201
|
+
toolScope: scope.toolName,
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
async function emit(event: AgentEvent): Promise<void> {
|
|
206
|
+
for (const listener of listeners) {
|
|
207
|
+
// Match event type
|
|
208
|
+
if (listener.eventType !== event.type) continue;
|
|
209
|
+
|
|
210
|
+
// Match agent scope
|
|
211
|
+
if (listener.agentScope && listener.agentScope !== event.agentPath) {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// Match tool scope (only for tool:* events)
|
|
216
|
+
if (
|
|
217
|
+
listener.toolScope &&
|
|
218
|
+
"tool" in event &&
|
|
219
|
+
listener.toolScope !== event.tool
|
|
220
|
+
) {
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
await listener.callback(event);
|
|
226
|
+
} catch (err) {
|
|
227
|
+
// Never propagate listener errors — log and continue
|
|
228
|
+
console.error(
|
|
229
|
+
`[agents-sdk] Event listener error for ${event.type}:`,
|
|
230
|
+
err,
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return { on, emit, _onScoped };
|
|
237
|
+
}
|