@slashfi/agents-sdk 0.25.1 → 0.26.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/src/events.ts CHANGED
@@ -10,26 +10,48 @@
10
10
  * Filtering happens in the callback, not the API.
11
11
  */
12
12
 
13
+ import type { CallAgentRequest, CallAgentResponse } from "./types.js";
13
14
  // =============================================================================
14
15
  // Event Types
15
16
  // =============================================================================
16
17
 
17
18
  /**
18
- * All supported event types.
19
+ * Built-in system event types managed by the runtime.
19
20
  */
20
- export type EventType =
21
+ export type SystemEventType =
21
22
  | "tool/call"
22
23
  | "tool/result"
23
24
  | "tool/error"
24
25
  | "step"
25
- | "invoke";
26
+ | "invoke"
27
+ | "call";
28
+
29
+ /**
30
+ * Augmentable map for custom event types. Consumers extend this
31
+ * via declaration merging to register their own events:
32
+ *
33
+ * ```ts
34
+ * declare module '@slashfi/agents-sdk' {
35
+ * interface CustomEventMap {
36
+ * 'callback/resolve': MyCallbackResolveEvent;
37
+ * }
38
+ * }
39
+ * ```
40
+ */
41
+ // eslint-disable-next-line @typescript-eslint/no-empty-interface
42
+ export interface CustomEventMap {}
43
+
44
+ /**
45
+ * All event types — system + consumer-defined custom events.
46
+ */
47
+ export type EventType = SystemEventType | Extract<keyof CustomEventMap, string>;
26
48
 
27
49
  /**
28
50
  * Base event shape — every event has these fields.
29
51
  */
30
52
  export interface BaseEvent {
31
53
  /** Event type */
32
- type: EventType;
54
+ type: string;
33
55
  /** Agent path (e.g., '/agents/atlas-slack') */
34
56
  agentPath: string;
35
57
  /** Timestamp */
@@ -102,31 +124,54 @@ export interface InvokeEvent extends BaseEvent {
102
124
  }
103
125
 
104
126
  /**
105
- * Union of all event types.
127
+ * Event emitted when a call_agent request is received.
128
+ * Call `resolve(response)` to short-circuit the default handler.
129
+ * If no listener resolves, the default call handler runs.
130
+ */
131
+ export interface CallEvent extends BaseEvent {
132
+ type: "call";
133
+ /** The incoming call_agent request */
134
+ request: CallAgentRequest;
135
+ /** Run the default call handler and return its result */
136
+ next(): Promise<CallAgentResponse>;
137
+ /** Short-circuit with a response (skips default handler if next() not called) */
138
+ resolve(response: CallAgentResponse): void;
139
+ }
140
+
141
+ /**
142
+ * Union of all built-in event types.
106
143
  */
107
144
  export type AgentEvent =
108
145
  | ToolCallEvent
109
146
  | ToolResultEvent
110
147
  | ToolErrorEvent
111
148
  | StepEvent
112
- | InvokeEvent;
149
+ | InvokeEvent
150
+ | CallEvent;
113
151
 
114
152
  /**
115
- * Map from event type string to event interface.
153
+ * Map from system event type string to event interface.
116
154
  */
117
- export interface EventMap {
155
+ export interface SystemEventMap {
118
156
  "tool/call": ToolCallEvent;
119
157
  "tool/result": ToolResultEvent;
120
158
  "tool/error": ToolErrorEvent;
121
159
  step: StepEvent;
122
160
  invoke: InvokeEvent;
161
+ call: CallEvent;
123
162
  }
124
163
 
164
+ /**
165
+ * Map from event type string to event interface.
166
+ * Combines system events with custom events.
167
+ */
168
+ export interface EventMap extends SystemEventMap, CustomEventMap {}
169
+
125
170
  /**
126
171
  * Callback for a specific event type.
127
172
  */
128
173
  export type EventCallback<T extends EventType = EventType> = (
129
- event: EventMap[T],
174
+ event: T extends keyof EventMap ? EventMap[T] : BaseEvent,
130
175
  ) => void | Promise<void>;
131
176
 
132
177
  // =============================================================================
@@ -136,7 +181,7 @@ export type EventCallback<T extends EventType = EventType> = (
136
181
  /**
137
182
  * Listener entry — callback + optional scope for agent/tool filtering.
138
183
  */
139
- interface ListenerEntry {
184
+ export interface ListenerEntry {
140
185
  eventType: EventType;
141
186
  callback: EventCallback<EventType>;
142
187
  /** If set, only fire for events matching this agent path */
@@ -161,7 +206,7 @@ export interface EventBus {
161
206
  * Listeners are called in registration order.
162
207
  * Errors in listeners are caught and logged, never propagated.
163
208
  */
164
- emit(event: AgentEvent): Promise<void>;
209
+ emit(event: AgentEvent | (BaseEvent & { type: string })): Promise<void>;
165
210
 
166
211
  /**
167
212
  * Register a scoped listener (used internally by agent.on / tool.on).
@@ -202,7 +247,9 @@ export function createEventBus(): EventBus {
202
247
  });
203
248
  }
204
249
 
205
- async function emit(event: AgentEvent): Promise<void> {
250
+ async function emit(
251
+ event: AgentEvent | (BaseEvent & { type: string }),
252
+ ): Promise<void> {
206
253
  for (const listener of listeners) {
207
254
  // Match event type
208
255
  if (listener.eventType !== event.type) continue;
@@ -222,7 +269,7 @@ export function createEventBus(): EventBus {
222
269
  }
223
270
 
224
271
  try {
225
- await listener.callback(event);
272
+ await listener.callback(event as never);
226
273
  } catch (err) {
227
274
  // Never propagate listener errors — log and continue
228
275
  console.error(
package/src/index.ts CHANGED
@@ -112,14 +112,20 @@ export { createEventBus } from "./events.js";
112
112
  export type {
113
113
  EventBus,
114
114
  EventType,
115
+ SystemEventType,
116
+ CustomEventMap,
115
117
  EventCallback,
116
118
  AgentEvent,
119
+ BaseEvent,
117
120
  ToolCallEvent,
118
121
  ToolResultEvent,
119
122
  ToolErrorEvent,
120
123
  StepEvent,
121
124
  InvokeEvent,
125
+ CallEvent,
122
126
  EventMap,
127
+ SystemEventMap,
128
+ ListenerEntry,
123
129
  } from "./events.js";
124
130
 
125
131
  // Server
package/src/registry.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  */
6
6
 
7
7
  import { dirname, resolve } from "node:path";
8
- import type { AgentEvent, EventCallback, EventType } from "./events.js";
8
+ import type { AgentEvent, BaseEvent, CustomEventMap, EventCallback, EventType } from "./events.js";
9
9
  import { createEventBus } from "./events.js";
10
10
  import type { SerializedAgentDefinition } from "./serialized.js";
11
11
  import type {
@@ -55,6 +55,7 @@ export interface RegistryMiddleware {
55
55
  registry: AgentRegistry;
56
56
  },
57
57
  ) => Promise<CallAgentLoadResponse>;
58
+
58
59
  }
59
60
 
60
61
  /**
@@ -97,6 +98,21 @@ export interface AgentRegistry {
97
98
 
98
99
  /** Emit an event to all listeners. Used by the runtime to push lifecycle events. */
99
100
  emit(event: AgentEvent): Promise<void>;
101
+
102
+ /**
103
+ * Trigger a custom event. Only accepts custom event types (not system events
104
+ * like tool/call, tool/result, etc. which are managed by the runtime).
105
+ *
106
+ * @example
107
+ * ```ts
108
+ * // After augmenting CustomEventMap:
109
+ * registry.trigger('callback/resolve', { type: 'callback/resolve', ... });
110
+ * ```
111
+ */
112
+ trigger<T extends Extract<keyof CustomEventMap, string>>(
113
+ eventType: T,
114
+ event: CustomEventMap[T] & BaseEvent,
115
+ ): Promise<void>;
100
116
  }
101
117
 
102
118
  // ============================================
@@ -412,7 +428,7 @@ export function createAgentRegistry(
412
428
  };
413
429
  }
414
430
 
415
- const registry: AgentRegistry = {
431
+ const registryObj: AgentRegistry = {
416
432
  register(input: AgentDefinition | SerializedAgentDefinition): void {
417
433
  let agent: AgentDefinition;
418
434
  if (isSerialized(input)) {
@@ -473,7 +489,42 @@ export function createAgentRegistry(
473
489
  await eventBus.emit(event);
474
490
  },
475
491
 
492
+ async trigger<T extends Extract<keyof CustomEventMap, string>>(
493
+ _eventType: T,
494
+ event: CustomEventMap[T] & BaseEvent,
495
+ ): Promise<void> {
496
+ await eventBus.emit(event as never);
497
+ },
498
+
476
499
  async call(request: CallAgentRequest): Promise<CallAgentResponse> {
500
+ // Emit call event — listeners can next()/resolve() to control flow
501
+ let intercepted: CallAgentResponse | undefined;
502
+ let nextCalled = false;
503
+ let nextResult: CallAgentResponse | undefined;
504
+ await eventBus.emit({
505
+ type: "call",
506
+ agentPath: request.path,
507
+ timestamp: Date.now(),
508
+ request,
509
+ async next() {
510
+ nextCalled = true;
511
+ nextResult = await callInternal(request);
512
+ return nextResult;
513
+ },
514
+ resolve(response: CallAgentResponse) {
515
+ intercepted = response;
516
+ },
517
+ });
518
+ if (intercepted) return intercepted;
519
+ if (nextCalled) return nextResult!;
520
+ // No listener engaged — run default
521
+ return callInternal(request);
522
+ },
523
+ };
524
+
525
+ return registryObj;
526
+
527
+ async function callInternal(request: CallAgentRequest): Promise<CallAgentResponse> {
477
528
  const agent = agents.get(request.path);
478
529
 
479
530
  if (!agent) {
@@ -673,7 +724,7 @@ export function createAgentRegistry(
673
724
  return options.middleware.load(defaultLoad, {
674
725
  agent,
675
726
  request,
676
- registry,
727
+ registry: registryObj,
677
728
  });
678
729
  }
679
730
  return defaultLoad(agent, request);
@@ -717,8 +768,5 @@ export function createAgentRegistry(
717
768
  } as CallAgentErrorResponse;
718
769
  }
719
770
  }
720
- },
721
- };
722
-
723
- return registry;
724
- }
771
+ }
772
+ }