@slashfi/agents-sdk 0.25.0 → 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/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
+ }
package/src/server.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  * Minimal JSON-RPC server implementing the MCP protocol for agent interaction.
5
5
  * Handles only core SDK concerns:
6
6
  * - MCP protocol (initialize, tools/list, tools/call)
7
- * - Agent registry routing (call_agent, list_agents)
7
+ * - Agent registry routing (call_agent, list_agents, search_agent_tools)
8
8
  * - Auth resolution (Bearer tokens, root key, JWT)
9
9
  * - OAuth2 token exchange (client_credentials)
10
10
  * - Health check
@@ -30,6 +30,7 @@ import {
30
30
  type SecretStore,
31
31
  processSecretParams,
32
32
  } from "./agent-definitions/secrets.js";
33
+ import { type BM25Document, createBM25Index } from "./bm25.js";
33
34
  import { verifyJwt } from "./jwt.js";
34
35
  import type { SigningKey } from "./jwt.js";
35
36
  import {
@@ -442,6 +443,32 @@ function getToolDefinitions() {
442
443
  properties: {},
443
444
  },
444
445
  },
446
+ {
447
+ name: "search_agent_tools",
448
+ description:
449
+ "Search across all registered agent tools using natural language. Returns tools ranked by relevance using BM25 scoring.",
450
+ inputSchema: {
451
+ type: "object",
452
+ properties: {
453
+ query: {
454
+ type: "string",
455
+ description:
456
+ "Natural language search query (e.g. 'send a message', 'database query')",
457
+ },
458
+ agents: {
459
+ type: "array",
460
+ items: { type: "string" },
461
+ description:
462
+ "Optional list of agent paths to search within (e.g. ['@notifications', '@db']). Searches all agents if omitted.",
463
+ },
464
+ limit: {
465
+ type: "number",
466
+ description: "Maximum number of results to return (default: 10)",
467
+ },
468
+ },
469
+ required: ["query"],
470
+ },
471
+ },
445
472
  ];
446
473
  }
447
474
 
@@ -609,6 +636,104 @@ export function createAgentServer(
609
636
  });
610
637
  }
611
638
 
639
+ case "search_agent_tools": {
640
+ const { query, agents: agentFilter, limit: resultLimit } = args as {
641
+ query: string;
642
+ agents?: string[];
643
+ limit?: number;
644
+ };
645
+
646
+ const agents = registry.list();
647
+ const visible = agents.filter((agent) => {
648
+ if (!canSeeAgent(agent, auth)) return false;
649
+ if (agentFilter && agentFilter.length > 0) {
650
+ return agentFilter.includes(agent.path);
651
+ }
652
+ return true;
653
+ });
654
+
655
+ // Build search documents from all visible tools
656
+ const documents: (BM25Document & {
657
+ agentPath: string;
658
+ toolName: string;
659
+ description: string;
660
+ agentName?: string;
661
+ agentDescription?: string;
662
+ })[] = [];
663
+
664
+ for (const agent of visible) {
665
+ const visibleTools = agent.tools.filter((t) => {
666
+ const tv = t.visibility ?? "internal";
667
+ if (auth?.isRoot) return true;
668
+ if (tv === "public") return true;
669
+ if (
670
+ tv === "authenticated" &&
671
+ auth?.callerId &&
672
+ auth.callerId !== "anonymous"
673
+ )
674
+ return true;
675
+ if (tv === "internal" && auth) return true;
676
+ return false;
677
+ });
678
+
679
+ for (const tool of visibleTools) {
680
+ // Build searchable text from tool name, description, agent context, and schema
681
+ const parts = [
682
+ tool.name,
683
+ tool.description,
684
+ agent.config?.name ?? "",
685
+ agent.config?.description ?? "",
686
+ agent.path,
687
+ ];
688
+
689
+ // Include property names and descriptions from input schema
690
+ const schema = tool.inputSchema as any;
691
+ if (schema?.properties) {
692
+ for (const [key, prop] of Object.entries(schema.properties)) {
693
+ parts.push(key);
694
+ if ((prop as any)?.description) {
695
+ parts.push((prop as any).description);
696
+ }
697
+ }
698
+ }
699
+
700
+ documents.push({
701
+ id: `${agent.path}/${tool.name}`,
702
+ text: parts.join(" "),
703
+ agentPath: agent.path,
704
+ toolName: tool.name,
705
+ description: tool.description,
706
+ agentName: agent.config?.name,
707
+ agentDescription: agent.config?.description,
708
+ });
709
+ }
710
+ }
711
+
712
+ const index = createBM25Index(documents);
713
+ const results = index.search(query, resultLimit ?? 10);
714
+
715
+ // Map results back to tool details
716
+ const docMap = new Map(documents.map((d) => [d.id, d]));
717
+ const matches = results.map((r) => {
718
+ const doc = docMap.get(r.id)!;
719
+ return {
720
+ agentPath: doc.agentPath,
721
+ tool: doc.toolName,
722
+ description: doc.description,
723
+ agentName: doc.agentName,
724
+ agentDescription: doc.agentDescription,
725
+ score: r.score,
726
+ };
727
+ });
728
+
729
+ return mcpResult({
730
+ success: true,
731
+ query,
732
+ results: matches,
733
+ total: matches.length,
734
+ });
735
+ }
736
+
612
737
  default:
613
738
  throw new Error(`Unknown tool: ${toolName}`);
614
739
  }