@slashfi/agents-sdk 0.11.2 → 0.12.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.
Files changed (41) hide show
  1. package/dist/agent-definitions/auth.d.ts +4 -1
  2. package/dist/agent-definitions/auth.d.ts.map +1 -1
  3. package/dist/agent-definitions/auth.js +48 -3
  4. package/dist/agent-definitions/auth.js.map +1 -1
  5. package/dist/agent-definitions/integrations.d.ts +2 -14
  6. package/dist/agent-definitions/integrations.d.ts.map +1 -1
  7. package/dist/agent-definitions/integrations.js +46 -17
  8. package/dist/agent-definitions/integrations.js.map +1 -1
  9. package/dist/agent-definitions/remote-registry.d.ts +19 -14
  10. package/dist/agent-definitions/remote-registry.d.ts.map +1 -1
  11. package/dist/agent-definitions/remote-registry.js +207 -381
  12. package/dist/agent-definitions/remote-registry.js.map +1 -1
  13. package/dist/agent-definitions/users.d.ts.map +1 -1
  14. package/dist/agent-definitions/users.js +29 -1
  15. package/dist/agent-definitions/users.js.map +1 -1
  16. package/dist/define.d.ts +6 -4
  17. package/dist/define.d.ts.map +1 -1
  18. package/dist/define.js +82 -3
  19. package/dist/define.js.map +1 -1
  20. package/dist/index.d.ts +2 -2
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/jwt.js +1 -1
  24. package/dist/jwt.js.map +1 -1
  25. package/dist/server.d.ts +37 -0
  26. package/dist/server.d.ts.map +1 -1
  27. package/dist/server.js +215 -51
  28. package/dist/server.js.map +1 -1
  29. package/dist/types.d.ts +53 -1
  30. package/dist/types.d.ts.map +1 -1
  31. package/package.json +1 -1
  32. package/src/agent-definitions/auth.ts +57 -5
  33. package/src/agent-definitions/integrations.ts +51 -26
  34. package/src/agent-definitions/remote-registry.ts +210 -513
  35. package/src/agent-definitions/users.ts +35 -1
  36. package/src/define.ts +98 -6
  37. package/src/index.ts +2 -1
  38. package/src/jwt.ts +1 -1
  39. package/src/server.test.ts +284 -0
  40. package/src/server.ts +318 -59
  41. package/src/types.ts +44 -1
package/src/server.ts CHANGED
@@ -32,7 +32,7 @@ import {
32
32
  } from "./agent-definitions/secrets.js";
33
33
  import { verifyJwt } from "./jwt.js";
34
34
  import type { SigningKey } from "./jwt.js";
35
- import { generateSigningKey, importSigningKey, exportSigningKey, buildJwks, verifyJwtLocal, verifyJwtFromIssuer } from "./jwt.js";
35
+ import { generateSigningKey, importSigningKey, exportSigningKey, buildJwks, verifyJwtLocal, verifyJwtFromIssuer, signJwtES256 } from "./jwt.js";
36
36
  import type { AgentRegistry } from "./registry.js";
37
37
  import type { AgentDefinition, CallAgentRequest, Visibility } from "./types.js";
38
38
 
@@ -48,6 +48,37 @@ export interface TrustedIssuer {
48
48
  scopes: string[];
49
49
  }
50
50
 
51
+
52
+
53
+ /** OAuth identity provider for /oauth/authorize + /oauth/callback flows */
54
+ export interface OAuthIdentityProvider {
55
+ /**
56
+ * Handle /oauth/authorize — redirect the user to an external IdP.
57
+ * Return a Response (typically a 302 redirect).
58
+ */
59
+ authorize(req: Request, params: {
60
+ /** The verified JWT from the foreign registry */
61
+ token: string;
62
+ /** Claims from the verified JWT */
63
+ claims: Record<string, unknown>;
64
+ /** Where to redirect after completion */
65
+ redirectUri: string;
66
+ /** Base URL of this server */
67
+ baseUrl: string;
68
+ /** OAuth scope (e.g. "setup" for tenant creation flow) */
69
+ scope?: string;
70
+ }): Promise<Response>;
71
+
72
+ /**
73
+ * Handle /oauth/callback — process the IdP response.
74
+ * Should link the foreign identity to a local user and redirect back.
75
+ * Return a Response (typically a 302 redirect to redirectUri).
76
+ */
77
+ callback(req: Request, params: {
78
+ /** Base URL of this server */
79
+ baseUrl: string;
80
+ }): Promise<Response>;
81
+ }
51
82
  export interface AgentServerOptions {
52
83
  /** Port to listen on (default: 3000) */
53
84
  port?: number;
@@ -67,9 +98,13 @@ export interface AgentServerOptions {
67
98
  trustedIssuers?: (TrustedIssuer | string)[];
68
99
  /** Pre-generated signing key (if not provided, one is generated on start) */
69
100
  signingKey?: SigningKey;
101
+ /** OAuth identity provider for cross-registry user linking */
102
+ oauthIdentityProvider?: OAuthIdentityProvider;
70
103
  }
71
104
 
72
105
  export interface AgentServer {
106
+ /** Initialize signing keys without starting HTTP server */
107
+ initKeys(): Promise<void>;
73
108
  /** Start the server */
74
109
  start(): Promise<void>;
75
110
  /** Stop the server */
@@ -80,6 +115,10 @@ export interface AgentServer {
80
115
  url: string | null;
81
116
  /** The agent registry this server uses */
82
117
  registry: AgentRegistry;
118
+ /** Sign a JWT with the server's signing key (for outbound calls) */
119
+ signJwt(claims: Record<string, unknown>): Promise<string>;
120
+ /** Dynamically add a trusted JWT issuer at runtime */
121
+ addTrustedIssuer(issuerUrl: string, scopes?: string[]): void;
83
122
  }
84
123
 
85
124
  // ============================================
@@ -111,6 +150,7 @@ export interface AuthConfig {
111
150
  }
112
151
 
113
152
  export interface ResolvedAuth {
153
+ issuer?: string;
114
154
  callerId: string;
115
155
  callerType: "agent" | "user" | "system";
116
156
  scopes: string[];
@@ -401,6 +441,7 @@ export function createAgentServer(
401
441
  serverName = "agents-sdk",
402
442
  serverVersion = "1.0.0",
403
443
  secretStore,
444
+ oauthIdentityProvider,
404
445
  } = options;
405
446
 
406
447
  // Signing keys for JWKS-based auth
@@ -487,6 +528,7 @@ export function createAgentServer(
487
528
  if (!req.metadata) req.metadata = {};
488
529
  req.metadata.scopes = auth.scopes;
489
530
  req.metadata.isRoot = auth.isRoot;
531
+ if (auth.issuer) req.metadata.issuer = auth.issuer;
490
532
  }
491
533
  if (auth?.isRoot) {
492
534
  req.callerType = "system";
@@ -552,73 +594,193 @@ export function createAgentServer(
552
594
  }
553
595
 
554
596
  const contentType = req.headers.get("Content-Type") ?? "";
555
- let grantType: string;
556
- let clientId: string;
557
- let clientSecret: string;
597
+ let params: Record<string, string>;
558
598
 
559
599
  if (contentType.includes("application/x-www-form-urlencoded")) {
560
600
  const body = await req.text();
561
- const params = new URLSearchParams(body);
562
- grantType = params.get("grant_type") ?? "";
563
- clientId = params.get("client_id") ?? "";
564
- clientSecret = params.get("client_secret") ?? "";
601
+ const urlParams = new URLSearchParams(body);
602
+ params = Object.fromEntries(urlParams.entries());
565
603
  } else {
566
- const body = (await req.json()) as Record<string, string>;
567
- grantType = body.grant_type ?? "";
568
- clientId = body.client_id ?? "";
569
- clientSecret = body.client_secret ?? "";
604
+ params = (await req.json()) as Record<string, string>;
570
605
  }
571
606
 
572
- if (grantType !== "client_credentials") {
573
- return jsonResponse(
574
- {
575
- error: "unsupported_grant_type",
576
- error_description: "Only client_credentials is supported",
577
- },
578
- 400,
579
- );
580
- }
607
+ const grantType = params.grant_type ?? "";
581
608
 
582
- if (!clientId || !clientSecret) {
583
- return jsonResponse(
584
- {
585
- error: "invalid_request",
586
- error_description: "Missing client_id or client_secret",
587
- },
588
- 400,
589
- );
609
+ // ── jwt_exchange grant: verify foreign JWT, resolve local identity ──
610
+ if (grantType === "jwt_exchange") {
611
+ const assertion = params.assertion ?? "";
612
+ if (!assertion) {
613
+ return jsonResponse(
614
+ { error: "invalid_request", error_description: "Missing assertion parameter" },
615
+ 400,
616
+ );
617
+ }
618
+
619
+ try {
620
+ const result = await registry.call({
621
+ action: "execute_tool",
622
+ path: "@auth",
623
+ tool: "exchange_token",
624
+ params: { token: assertion },
625
+ callerType: "system",
626
+ });
627
+
628
+ const exchangeResult = (result as any)?.result;
629
+ // If the tool call failed, forward the error
630
+ if ((result as any)?.success === false) {
631
+ return jsonResponse(
632
+ { error: "server_error", error_description: (result as any)?.error ?? "Exchange tool failed", raw: JSON.stringify(result)?.slice(0, 300) },
633
+ 500,
634
+ );
635
+ }
636
+
637
+ // ── Reverse registration: if caller is an agent-registry, auto-store connection ──
638
+ try {
639
+ const assertionParts = assertion.split(".");
640
+ if (assertionParts.length === 3) {
641
+ const assertionPayload = JSON.parse(atob(assertionParts[1].replace(/-/g, "+").replace(/_/g, "/"))) as any;
642
+ if (assertionPayload.type === "agent-registry" && assertionPayload.iss) {
643
+ // Find or create @remote-registry agent and store the reverse connection
644
+ const rrAgent = registry.get("@remote-registry") ?? registry.get("/agents/@remote-registry");
645
+ if (rrAgent) {
646
+ const setupTool = (rrAgent as any).tools?.find((t: any) => t.name === "setup_integration");
647
+ if (setupTool?.execute) {
648
+ try {
649
+ await setupTool.execute(
650
+ { url: assertionPayload.iss, name: assertionPayload.name ?? "remote-registry" },
651
+ { callerId: "system", callerType: "system", tenantId: "default", agentPath: "@remote-registry" },
652
+ );
653
+ console.error(`[jwt_exchange] Reverse connection stored for ${assertionPayload.iss}`);
654
+ } catch (setupErr) {
655
+ console.error(`[jwt_exchange] Reverse registration setup failed:`, setupErr);
656
+ }
657
+ } else {
658
+ console.error("[jwt_exchange] @remote-registry has no setup_integration tool — reverse registration skipped");
659
+ }
660
+ } else {
661
+ console.error("[jwt_exchange] @remote-registry agent not found — reverse registration skipped");
662
+ }
663
+ }
664
+ }
665
+ } catch (reverseErr) {
666
+ console.error("[jwt_exchange] Reverse registration check failed:", reverseErr);
667
+ }
668
+
669
+ if (!exchangeResult) {
670
+ return jsonResponse(
671
+ { error: "server_error", error_description: `Exchange returned null: ${JSON.stringify(result)?.slice(0, 300)}` },
672
+ 500,
673
+ );
674
+ }
675
+
676
+ // User not linked yet — needs OAuth identity linking
677
+ if (exchangeResult.needsAuth) {
678
+ const baseUrl = new URL(req.url).origin;
679
+ const authorizeUrl = new URL(`${baseUrl}${basePath}/oauth/authorize`);
680
+ authorizeUrl.searchParams.set("token", assertion);
681
+ if (params.redirect_uri) {
682
+ authorizeUrl.searchParams.set("redirect_uri", params.redirect_uri);
683
+ }
684
+ if (params.scope) {
685
+ authorizeUrl.searchParams.set("scope", params.scope);
686
+ }
687
+ return jsonResponse(
688
+ {
689
+ error: "identity_required",
690
+ error_description: "User identity not linked. Redirect to authorize_url to complete linking.",
691
+ authorize_url: authorizeUrl.toString(),
692
+ tenant_id: exchangeResult.tenantId,
693
+ },
694
+ 403,
695
+ );
696
+ }
697
+
698
+ // User found — sign a local access token
699
+ if (exchangeResult.userId && serverSigningKeys.length > 0) {
700
+ const sigKey = serverSigningKeys[0];
701
+ const token = await signJwtES256(
702
+ {
703
+ sub: exchangeResult.userId,
704
+ name: exchangeResult.userId,
705
+ scopes: ["*"],
706
+ tenantId: exchangeResult.tenantId,
707
+ },
708
+ sigKey.privateKey,
709
+ sigKey.kid,
710
+ new URL(req.url).origin,
711
+ `${authConfig.tokenTtl ?? 3600}s`,
712
+ );
713
+
714
+ return jsonResponse({
715
+ access_token: token,
716
+ token_type: "Bearer",
717
+ expires_in: authConfig.tokenTtl ?? 3600,
718
+ user_id: exchangeResult.userId,
719
+ tenant_id: exchangeResult.tenantId,
720
+ });
721
+ }
722
+
723
+ return jsonResponse(exchangeResult);
724
+ } catch (err) {
725
+ console.error("[oauth] JWT exchange error:", err);
726
+ return jsonResponse(
727
+ { error: "server_error", error_description: `JWT exchange failed: ${err instanceof Error ? err.message : String(err)}` },
728
+ 500,
729
+ );
730
+ }
590
731
  }
591
732
 
592
- try {
593
- const result = await registry.call({
594
- action: "execute_tool",
595
- path: "@auth",
596
- tool: "token",
597
- params: { clientId, clientSecret },
598
- callerType: "system",
599
- });
733
+ // ── client_credentials grant ──
734
+ if (grantType === "client_credentials") {
735
+ const clientId = params.client_id ?? "";
736
+ const clientSecret = params.client_secret ?? "";
600
737
 
601
- const tokenResult = (result as any)?.result;
602
- if (!tokenResult?.accessToken) {
738
+ if (!clientId || !clientSecret) {
603
739
  return jsonResponse(
604
- { error: "invalid_client", error_description: "Authentication failed" },
605
- 401,
740
+ { error: "invalid_request", error_description: "Missing client_id or client_secret" },
741
+ 400,
606
742
  );
607
743
  }
608
744
 
609
- return jsonResponse({
610
- access_token: tokenResult.accessToken,
611
- token_type: "Bearer",
612
- expires_in: tokenResult.expiresIn ?? authConfig.tokenTtl,
613
- refresh_token: tokenResult.refreshToken,
614
- });
615
- } catch (err) {
616
- console.error("[oauth] Token error:", err);
617
- return jsonResponse(
618
- { error: "server_error", error_description: "Token exchange failed" },
619
- 500,
620
- );
745
+ try {
746
+ const result = await registry.call({
747
+ action: "execute_tool",
748
+ path: "@auth",
749
+ tool: "token",
750
+ params: { clientId, clientSecret },
751
+ callerType: "system",
752
+ });
753
+
754
+ const tokenResult = (result as any)?.result;
755
+ if (!tokenResult?.accessToken) {
756
+ return jsonResponse(
757
+ { error: "invalid_client", error_description: "Authentication failed" },
758
+ 401,
759
+ );
760
+ }
761
+
762
+ return jsonResponse({
763
+ access_token: tokenResult.accessToken,
764
+ token_type: "Bearer",
765
+ expires_in: tokenResult.expiresIn ?? authConfig.tokenTtl,
766
+ refresh_token: tokenResult.refreshToken,
767
+ });
768
+ } catch (err) {
769
+ console.error("[oauth] Token error:", err);
770
+ return jsonResponse(
771
+ { error: "server_error", error_description: "Token exchange failed" },
772
+ 500,
773
+ );
774
+ }
621
775
  }
776
+
777
+ return jsonResponse(
778
+ {
779
+ error: "unsupported_grant_type",
780
+ error_description: "Supported grant types: client_credentials, jwt_exchange",
781
+ },
782
+ 400,
783
+ );
622
784
  }
623
785
 
624
786
  // ──────────────────────────────────────────
@@ -667,12 +829,81 @@ export function createAgentServer(
667
829
  return cors ? addCors(jsonResponse(result)) : jsonResponse(result);
668
830
  }
669
831
 
670
- // ── POST /oauth/token → OAuth2 client_credentials ──
832
+ // ── POST /oauth/token → OAuth2 token exchange ──
671
833
  if (path === "/oauth/token" && req.method === "POST") {
672
834
  const res = await handleOAuthToken(req);
673
835
  return cors ? addCors(res) : res;
674
836
  }
675
837
 
838
+ // ── GET /oauth/authorize → Identity linking redirect (browser flow) ──
839
+ if (path === "/oauth/authorize" && req.method === "GET") {
840
+ if (!oauthIdentityProvider) {
841
+ const res = jsonResponse(
842
+ { error: "not_configured", error_description: "No OAuth identity provider configured" },
843
+ 404,
844
+ );
845
+ return cors ? addCors(res) : res;
846
+ }
847
+ const url = new URL(req.url);
848
+ const token = url.searchParams.get("token") ?? "";
849
+ const redirectUri = url.searchParams.get("redirect_uri") ?? "";
850
+
851
+ if (!token) {
852
+ const res = jsonResponse(
853
+ { error: "invalid_request", error_description: "Missing token parameter" },
854
+ 400,
855
+ );
856
+ return cors ? addCors(res) : res;
857
+ }
858
+
859
+ // Verify the JWT against trusted issuers
860
+ let claims: Record<string, unknown> | null = null;
861
+ const issuerUrls = configTrustedIssuers.map(i => typeof i === "string" ? i : i.issuer);
862
+ for (const issuerUrl of issuerUrls) {
863
+ try {
864
+ const result = await verifyJwtFromIssuer(token, issuerUrl);
865
+ if (result) {
866
+ claims = result as unknown as Record<string, unknown>;
867
+ break;
868
+ }
869
+ } catch { /* try next issuer */ }
870
+ }
871
+ if (!claims) {
872
+ const res = jsonResponse(
873
+ { error: "invalid_token", error_description: "JWT verification failed against all trusted issuers" },
874
+ 401,
875
+ );
876
+ return cors ? addCors(res) : res;
877
+ }
878
+
879
+ const baseUrl = new URL(req.url).origin;
880
+ const scope = url.searchParams.get("scope") ?? undefined;
881
+ const res = await oauthIdentityProvider.authorize(req, {
882
+ token,
883
+ claims,
884
+ redirectUri,
885
+ baseUrl: baseUrl + basePath,
886
+ scope,
887
+ });
888
+ return cors ? addCors(res) : res;
889
+ }
890
+
891
+ // ── GET /oauth/callback → Identity linking callback ──
892
+ if (path === "/oauth/callback" && req.method === "GET") {
893
+ if (!oauthIdentityProvider) {
894
+ const res = jsonResponse(
895
+ { error: "not_configured", error_description: "No OAuth identity provider configured" },
896
+ 404,
897
+ );
898
+ return cors ? addCors(res) : res;
899
+ }
900
+ const baseUrl = new URL(req.url).origin;
901
+ const res = await oauthIdentityProvider.callback(req, {
902
+ baseUrl: baseUrl + basePath,
903
+ });
904
+ return cors ? addCors(res) : res;
905
+ }
906
+
676
907
  // ── GET /health → Health check ──
677
908
  if (path === "/health" && req.method === "GET") {
678
909
  const res = jsonResponse({ status: "ok", agents: registry.listPaths() });
@@ -697,7 +928,8 @@ export function createAgentServer(
697
928
  token_endpoint: `${baseUrl}/oauth/token`,
698
929
  agents_endpoint: `${baseUrl}/list`,
699
930
  call_endpoint: baseUrl,
700
- supported_grant_types: ["client_credentials"],
931
+ supported_grant_types: ["client_credentials", "jwt_exchange"],
932
+ authorization_endpoint: `${baseUrl}/oauth/authorize`,
701
933
  agents: registry.listPaths(),
702
934
  });
703
935
  return cors ? addCors(res) : res;
@@ -769,11 +1001,11 @@ export function createAgentServer(
769
1001
  url: null,
770
1002
  registry,
771
1003
 
772
- async start() {
773
- // Load or generate signing key for JWKS
774
- if (options.signingKey) {
1004
+ async initKeys() {
1005
+ // Load or generate signing keys (without starting Bun.serve)
1006
+ if (options.signingKey && serverSigningKeys.length === 0) {
775
1007
  serverSigningKeys.push(options.signingKey);
776
- } else if (authConfig?.store?.getSigningKeys) {
1008
+ } else if (authConfig?.store?.getSigningKeys && serverSigningKeys.length === 0) {
777
1009
  const stored = await authConfig.store.getSigningKeys() ?? [];
778
1010
  for (const exported of stored) {
779
1011
  serverSigningKeys.push(await importSigningKey(exported));
@@ -786,6 +1018,10 @@ export function createAgentServer(
786
1018
  await authConfig.store.storeSigningKey(await exportSigningKey(key));
787
1019
  }
788
1020
  }
1021
+ },
1022
+
1023
+ async start() {
1024
+ await this.initKeys();
789
1025
 
790
1026
  serverInstance = Bun.serve({
791
1027
  port,
@@ -805,5 +1041,28 @@ export function createAgentServer(
805
1041
  },
806
1042
 
807
1043
  fetch,
1044
+
1045
+ async signJwt(claims: Record<string, unknown>): Promise<string> {
1046
+ if (serverSigningKeys.length === 0) {
1047
+ throw new Error('No signing keys available. Call start() or initKeys() first.');
1048
+ }
1049
+ const key = serverSigningKeys[0];
1050
+ return signJwtES256(
1051
+ { sub: 'system', name: 'atlas-os', scopes: ['*'], ...claims } as any,
1052
+ key.privateKey,
1053
+ key.kid,
1054
+ options.serverName ?? 'agents-sdk',
1055
+ '1h',
1056
+ );
1057
+ },
1058
+
1059
+ addTrustedIssuer(issuerUrl: string, scopes?: string[]): void {
1060
+ // Avoid duplicates
1061
+ const existing = configTrustedIssuers.find(i => i.issuer === issuerUrl);
1062
+ if (!existing) {
1063
+ configTrustedIssuers.push({ issuer: issuerUrl, scopes: scopes ?? ['*'] });
1064
+ console.error(`[agent-server] Added trusted issuer: ${issuerUrl}`);
1065
+ }
1066
+ },
808
1067
  };
809
1068
  }
package/src/types.ts CHANGED
@@ -20,6 +20,11 @@ export type JsonSchema = {
20
20
  enum?: unknown[];
21
21
  default?: unknown;
22
22
  additionalProperties?: boolean | JsonSchema;
23
+ /** Agent refs (paths to other agents this agent can call) */
24
+ refs?: Record<string, { description?: string }>;
25
+
26
+ /** Tools to expose publicly (by name) */
27
+ public?: { tools?: string[] };
23
28
  [key: string]: unknown;
24
29
  };
25
30
 
@@ -73,6 +78,30 @@ export interface IntegrationMethods {
73
78
  update(params: Record<string, unknown>, ctx: IntegrationMethodContext): Promise<IntegrationMethodResult>;
74
79
  }
75
80
 
81
+
82
+
83
+ /** Hooks for agents that implement the integrations interface. */
84
+ export interface IntegrationHooks {
85
+ /** Provider metadata */
86
+ provider: string;
87
+ displayName: string;
88
+ icon?: string;
89
+ category?: string;
90
+ description?: string;
91
+
92
+ /** Set up this integration (discover, configure, establish trust) */
93
+ setup?(params: Record<string, unknown>, ctx: ToolContext): Promise<IntegrationMethodResult>;
94
+ /** Connect a user to this integration (OAuth, identity linking) */
95
+ connect?(params: Record<string, unknown>, ctx: ToolContext): Promise<IntegrationMethodResult>;
96
+ /** Discover available instances of this integration */
97
+ discover?(params: Record<string, unknown>, ctx: ToolContext): Promise<IntegrationMethodResult>;
98
+ /** List connected instances */
99
+ list?(params: Record<string, unknown>, ctx: ToolContext): Promise<IntegrationMethodResult>;
100
+ /** Get details of a specific instance */
101
+ get?(params: Record<string, unknown>, ctx: ToolContext): Promise<IntegrationMethodResult>;
102
+ /** Update an existing instance config */
103
+ update?(params: Record<string, unknown>, ctx: ToolContext): Promise<IntegrationMethodResult>;
104
+ }
76
105
  export interface IntegrationConfig {
77
106
  /** Provider identifier (e.g., "databases", "slack", "github") */
78
107
  provider: string;
@@ -134,6 +163,11 @@ export interface AgentConfig {
134
163
  modelParams?: {
135
164
  maxTokens?: number;
136
165
  temperature?: number;
166
+ /** Agent refs (paths to other agents this agent can call) */
167
+ refs?: Record<string, { description?: string }>;
168
+
169
+ /** Tools to expose publicly (by name) */
170
+ public?: { tools?: string[] };
137
171
  [key: string]: unknown;
138
172
  };
139
173
 
@@ -145,6 +179,11 @@ export interface AgentConfig {
145
179
  integration?: IntegrationConfig;
146
180
 
147
181
  /** Additional configuration */
182
+ /** Agent refs (paths to other agents this agent can call) */
183
+ refs?: Record<string, { description?: string }>;
184
+
185
+ /** Tools to expose publicly (by name) */
186
+ public?: { tools?: string[] };
148
187
  [key: string]: unknown;
149
188
  }
150
189
 
@@ -197,6 +236,11 @@ export interface ToolContext extends CoreContext {
197
236
  callerType: CallerType;
198
237
 
199
238
  /** Application-specific extensions (e.g., OS services, stores) */
239
+ /** Agent refs (paths to other agents this agent can call) */
240
+ refs?: Record<string, { description?: string }>;
241
+
242
+ /** Tools to expose publicly (by name) */
243
+ public?: { tools?: string[] };
200
244
  [key: string]: unknown;
201
245
  }
202
246
 
@@ -492,7 +536,6 @@ export interface AgentDefinition<TContext extends ToolContext = ToolContext> {
492
536
  * When set alongside config.integration, this agent can be called
493
537
  * by @integrations via standard methods (setup, list, connect, get, update).
494
538
  */
495
- integrationMethods?: IntegrationMethods;
496
539
 
497
540
  /**
498
541
  * Lazy loader for lifecycle listeners.