@slashfi/agents-sdk 0.41.7 → 0.41.9

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.
@@ -24,6 +24,7 @@ import type {
24
24
  RefEntry,
25
25
  RegistryEntry,
26
26
  ResolvedRef,
27
+ ResolvedRegistry,
27
28
  } from "./define-config.js";
28
29
  import { normalizeRef } from "./define-config.js";
29
30
  import { createRegistryConsumer } from "./registry-consumer.js";
@@ -211,6 +212,28 @@ function refName(entry: RefEntry): string {
211
212
  return normalizeRef(entry).name;
212
213
  }
213
214
 
215
+ /**
216
+ * Find a ref by name, trying both with and without `@` prefix.
217
+ * Refs may be stored as `@foo` or `foo` depending on how they were added;
218
+ * this ensures lookups work regardless of which form the caller uses.
219
+ */
220
+ function findRef(refs: RefEntry[], name: string): RefEntry | undefined {
221
+ const match = refs.find((r) => refName(r) === name);
222
+ if (match) return match;
223
+ const alt = name.startsWith("@") ? name.slice(1) : `@${name}`;
224
+ return refs.find((r) => refName(r) === alt);
225
+ }
226
+
227
+ /**
228
+ * Match a ref name with @ normalization (for filter/map operations).
229
+ */
230
+ function refNameMatches(entry: RefEntry, name: string): boolean {
231
+ const n = refName(entry);
232
+ if (n === name) return true;
233
+ const alt = name.startsWith("@") ? name.slice(1) : `@${name}`;
234
+ return n === alt;
235
+ }
236
+
214
237
  function registryDisplayName(r: string | RegistryEntry): string {
215
238
  return typeof r === "string" ? r : (r.name ?? r.url);
216
239
  }
@@ -287,7 +310,7 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
287
310
 
288
311
  async function readRefSecret(name: string, key: string): Promise<string | null> {
289
312
  const config = await readConfig();
290
- const entry = (config.refs ?? []).find((r) => refName(r) === name);
313
+ const entry = findRef(config.refs ?? [], name);
291
314
  const value = entry?.config?.[key];
292
315
  if (typeof value !== "string") return null;
293
316
  if (value.startsWith(SECRET_PREFIX) && options.encryptionKey) {
@@ -481,6 +504,59 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
481
504
  );
482
505
  }
483
506
 
507
+ /**
508
+ * Build a consumer that includes the ref's sourceRegistry if present.
509
+ * This ensures calls/inspect route to the correct registry endpoint.
510
+ */
511
+ async function buildConsumerForRef(entry: RefEntry): Promise<RegistryConsumer> {
512
+ const config = await readConfig();
513
+ let registries = config.registries ?? [];
514
+
515
+ // If the ref has a sourceRegistry, ensure it's included in the consumer's registries
516
+ if (entry.sourceRegistry?.url) {
517
+ const sourceUrl = entry.sourceRegistry.url;
518
+ const alreadyIncluded = registries.some((r) =>
519
+ typeof r === "string" ? r === sourceUrl : r.url === sourceUrl,
520
+ );
521
+ if (!alreadyIncluded) {
522
+ registries = [...registries, { url: sourceUrl, name: sourceUrl }];
523
+ }
524
+ }
525
+
526
+ const resolved = options.encryptionKey
527
+ ? await Promise.all(
528
+ registries.map(async (r) => {
529
+ if (typeof r === "string") return r;
530
+ const decrypted = await decryptConfigSecrets(
531
+ r as unknown as Record<string, unknown>,
532
+ options.encryptionKey!,
533
+ );
534
+ return decrypted as unknown as RegistryEntry;
535
+ }),
536
+ )
537
+ : registries;
538
+
539
+ return createRegistryConsumer(
540
+ { registries: resolved, refs: config.refs ?? [] },
541
+ { token: options.token },
542
+ );
543
+ }
544
+
545
+ /**
546
+ * Resolve the correct registry for a ref.
547
+ * If the ref has a sourceRegistry, use that; otherwise fall back to the first registry.
548
+ */
549
+ function resolveRegistryForRef(consumer: RegistryConsumer, entry: RefEntry): ResolvedRegistry {
550
+ const regs = consumer.registries();
551
+ if (entry.sourceRegistry?.url) {
552
+ const match = regs.find((r) => r.url === entry.sourceRegistry!.url);
553
+ if (match) return match;
554
+ }
555
+ const fallback = regs[0];
556
+ if (!fallback) throw new Error("No registry available");
557
+ return fallback;
558
+ }
559
+
484
560
  // ==========================================
485
561
  // Registry API
486
562
  // ==========================================
@@ -599,9 +675,38 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
599
675
  const config = await readConfig();
600
676
  const hasRegistries = (config.registries ?? []).length > 0;
601
677
 
602
- if (hasRegistries) {
678
+ // Validate scheme is always specified
679
+ if (!entry.scheme) {
680
+ throw new AdkError({
681
+ code: "REF_INVALID",
682
+ message: `Cannot add ref "${entry.ref}": scheme is required`,
683
+ hint: "Specify scheme: 'registry' (with sourceRegistry), 'mcp' (with url), or 'https' (with url)",
684
+ details: { ref: entry.ref },
685
+ });
686
+ }
687
+
688
+ // Validate scheme-specific requirements
689
+ if (entry.scheme === "registry" && !entry.sourceRegistry?.url) {
690
+ throw new AdkError({
691
+ code: "REF_INVALID",
692
+ message: `Cannot add ref "${entry.ref}": scheme 'registry' requires sourceRegistry`,
693
+ hint: "Provide sourceRegistry: { url: '...', agentPath: '...' }",
694
+ details: { ref: entry.ref, scheme: entry.scheme },
695
+ });
696
+ }
697
+
698
+ if ((entry.scheme === "mcp" || entry.scheme === "https") && !entry.url) {
699
+ throw new AdkError({
700
+ code: "REF_INVALID",
701
+ message: `Cannot add ref "${entry.ref}": scheme '${entry.scheme}' requires url`,
702
+ hint: "Provide the direct agent URL with: url: 'https://...'",
703
+ details: { ref: entry.ref, scheme: entry.scheme },
704
+ });
705
+ }
706
+
707
+ if (hasRegistries || entry.sourceRegistry?.url) {
603
708
  try {
604
- const consumer = await buildConsumer();
709
+ const consumer = await buildConsumerForRef(entry);
605
710
  const agentToInspect = entry.sourceRegistry?.agentPath ?? entry.ref;
606
711
  const info = await consumer.inspect(agentToInspect);
607
712
 
@@ -669,7 +774,7 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
669
774
  const config = await readConfig();
670
775
  if (!config.refs?.length) return false;
671
776
  const before = config.refs.length;
672
- const refs = config.refs.filter((r) => refName(r) !== name);
777
+ const refs = config.refs.filter((r) => !refNameMatches(r, name));
673
778
  if (refs.length === before) return false;
674
779
  await writeConfig({ ...config, refs });
675
780
  return true;
@@ -682,7 +787,7 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
682
787
 
683
788
  async get(name: string): Promise<RefEntry | null> {
684
789
  const config = await readConfig();
685
- return (config.refs ?? []).find((r) => refName(r) === name) ?? null;
790
+ return findRef(config.refs ?? [], name) ?? null;
686
791
  },
687
792
 
688
793
  async update(name: string, updates: Partial<RefEntry>): Promise<boolean> {
@@ -690,7 +795,7 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
690
795
  if (!config.refs?.length) return false;
691
796
  let found = false;
692
797
  const refs = config.refs.map((r): RefEntry => {
693
- if (refName(r) !== name) return r;
798
+ if (!refNameMatches(r, name)) return r;
694
799
  found = true;
695
800
  const updated = { ...r };
696
801
  if (updates.url) updated.url = updates.url;
@@ -707,16 +812,20 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
707
812
 
708
813
  async inspect(name: string, opts?: { full?: boolean }): Promise<AgentListing | null> {
709
814
  const config = await readConfig();
710
- const entry = (config.refs ?? []).find((r) => refName(r) === name);
815
+ const entry = findRef(config.refs ?? [], name);
711
816
  if (!entry) throw new Error(`Ref "${name}" not found`);
712
817
 
713
- const consumer = await buildConsumer();
714
- return consumer.inspect(entry.ref, undefined, opts);
818
+ const consumer = await buildConsumerForRef(entry);
819
+ return consumer.inspect(
820
+ entry.sourceRegistry?.agentPath ?? entry.ref,
821
+ entry.sourceRegistry?.url,
822
+ opts,
823
+ );
715
824
  },
716
825
 
717
826
  async call(name: string, tool: string, params?: Record<string, unknown>): Promise<CallAgentResponse> {
718
827
  const config = await readConfig();
719
- const entry = (config.refs ?? []).find((r) => refName(r) === name);
828
+ const entry = findRef(config.refs ?? [], name);
720
829
  if (!entry) throw new Error(`Ref "${name}" not found`);
721
830
 
722
831
  const accessToken = await readRefSecret(name, "access_token");
@@ -727,13 +836,12 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
727
836
  return callMcpDirect(entry.url, tool, params ?? {}, accessToken);
728
837
  }
729
838
 
730
- const consumer = await buildConsumer();
731
- const reg = consumer.registries()[0];
732
- if (!reg) throw new Error("No registry available");
839
+ const consumer = await buildConsumerForRef(entry);
840
+ const reg = resolveRegistryForRef(consumer, entry);
733
841
 
734
842
  return consumer.callRegistry(reg, {
735
843
  action: "execute_tool",
736
- path: entry.ref,
844
+ path: entry.sourceRegistry?.agentPath ?? entry.ref,
737
845
  tool,
738
846
  params: params ?? {},
739
847
  });
@@ -741,44 +849,42 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
741
849
 
742
850
  async resources(name: string): Promise<CallAgentResponse> {
743
851
  const config = await readConfig();
744
- const entry = (config.refs ?? []).find((r) => refName(r) === name);
852
+ const entry = findRef(config.refs ?? [], name);
745
853
  if (!entry) throw new Error(`Ref "${name}" not found`);
746
854
 
747
- const consumer = await buildConsumer();
748
- const reg = consumer.registries()[0];
749
- if (!reg) throw new Error("No registry available");
855
+ const consumer = await buildConsumerForRef(entry);
856
+ const reg = resolveRegistryForRef(consumer, entry);
750
857
 
751
858
  return consumer.callRegistry(reg, {
752
859
  action: "list_resources",
753
- path: entry.ref,
860
+ path: entry.sourceRegistry?.agentPath ?? entry.ref,
754
861
  });
755
862
  },
756
863
 
757
864
  async read(name: string, uris: string[]): Promise<CallAgentResponse> {
758
865
  const config = await readConfig();
759
- const entry = (config.refs ?? []).find((r) => refName(r) === name);
866
+ const entry = findRef(config.refs ?? [], name);
760
867
  if (!entry) throw new Error(`Ref "${name}" not found`);
761
868
 
762
- const consumer = await buildConsumer();
763
- const reg = consumer.registries()[0];
764
- if (!reg) throw new Error("No registry available");
869
+ const consumer = await buildConsumerForRef(entry);
870
+ const reg = resolveRegistryForRef(consumer, entry);
765
871
 
766
872
  return consumer.callRegistry(reg, {
767
873
  action: "read_resources",
768
- path: entry.ref,
874
+ path: entry.sourceRegistry?.agentPath ?? entry.ref,
769
875
  uris,
770
876
  });
771
877
  },
772
878
 
773
879
  async authStatus(name: string): Promise<RefAuthStatus> {
774
880
  const config = await readConfig();
775
- const entry = (config.refs ?? []).find((r) => refName(r) === name);
881
+ const entry = findRef(config.refs ?? [], name);
776
882
  if (!entry) throw new Error(`Ref "${name}" not found`);
777
883
 
778
884
  let security: SecuritySchemeSummary | null = null;
779
885
  try {
780
- const consumer = await buildConsumer();
781
- const info = await consumer.inspect(entry.ref);
886
+ const consumer = await buildConsumerForRef(entry);
887
+ const info = await consumer.inspect(entry.sourceRegistry?.agentPath ?? entry.ref);
782
888
  if (info?.security) security = info.security;
783
889
  } catch {
784
890
  // Can't reach registry
@@ -863,7 +969,7 @@ export function createAdk(fs: FsStore, options: AdkOptions = {}): Adk {
863
969
  apiKey?: string;
864
970
  }): Promise<AuthStartResult> {
865
971
  const config = await readConfig();
866
- const entry = (config.refs ?? []).find((r) => refName(r) === name);
972
+ const entry = findRef(config.refs ?? [], name);
867
973
  if (!entry) throw new Error(`Ref "${name}" not found`);
868
974
 
869
975
  const status = await ref.authStatus(name);