@syfthub/sdk 0.2.1 → 0.3.1

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/index.js CHANGED
@@ -677,6 +677,116 @@ var APITokensResource = class {
677
677
  }
678
678
  };
679
679
 
680
+ // src/resources/aggregators.ts
681
+ var AggregatorsResource = class {
682
+ constructor(http) {
683
+ this.http = http;
684
+ }
685
+ /**
686
+ * List all aggregator configurations for the current user.
687
+ *
688
+ * @returns Array of UserAggregator objects
689
+ * @throws {AuthenticationError} If not authenticated
690
+ *
691
+ * @example
692
+ * const aggregators = await client.users.aggregators.list();
693
+ * for (const agg of aggregators) {
694
+ * if (agg.isDefault) {
695
+ * console.log(`Default: ${agg.name}`);
696
+ * }
697
+ * }
698
+ */
699
+ async list() {
700
+ return this.http.get("/api/v1/users/me/aggregators");
701
+ }
702
+ /**
703
+ * Get a specific aggregator configuration by ID.
704
+ *
705
+ * @param aggregatorId - The aggregator ID
706
+ * @returns The UserAggregator object
707
+ * @throws {AuthenticationError} If not authenticated
708
+ * @throws {NotFoundError} If aggregator not found
709
+ *
710
+ * @example
711
+ * const agg = await client.users.aggregators.get(1);
712
+ * console.log(`${agg.name}: ${agg.url}`);
713
+ */
714
+ async get(aggregatorId) {
715
+ return this.http.get(`/api/v1/users/me/aggregators/${aggregatorId}`);
716
+ }
717
+ /**
718
+ * Create a new aggregator configuration.
719
+ *
720
+ * The first aggregator created is automatically set as the default.
721
+ *
722
+ * @param input - Aggregator creation input
723
+ * @returns The created UserAggregator object
724
+ * @throws {AuthenticationError} If not authenticated
725
+ * @throws {ValidationError} If input is invalid
726
+ *
727
+ * @example
728
+ * const agg = await client.users.aggregators.create({
729
+ * name: 'My Custom Aggregator',
730
+ * url: 'https://my-aggregator.example.com'
731
+ * });
732
+ * console.log(`Created: ${agg.id}`);
733
+ */
734
+ async create(input) {
735
+ return this.http.post("/api/v1/users/me/aggregators", input);
736
+ }
737
+ /**
738
+ * Update an aggregator configuration.
739
+ *
740
+ * Only provided fields will be updated.
741
+ *
742
+ * @param aggregatorId - The aggregator ID to update
743
+ * @param input - Fields to update
744
+ * @returns The updated UserAggregator object
745
+ * @throws {AuthenticationError} If not authenticated
746
+ * @throws {NotFoundError} If aggregator not found
747
+ * @throws {ValidationError} If input is invalid
748
+ *
749
+ * @example
750
+ * const agg = await client.users.aggregators.update(1, {
751
+ * name: 'Updated Name'
752
+ * });
753
+ */
754
+ async update(aggregatorId, input) {
755
+ return this.http.put(`/api/v1/users/me/aggregators/${aggregatorId}`, input);
756
+ }
757
+ /**
758
+ * Delete an aggregator configuration.
759
+ *
760
+ * @param aggregatorId - The aggregator ID to delete
761
+ * @throws {AuthenticationError} If not authenticated
762
+ * @throws {NotFoundError} If aggregator not found
763
+ *
764
+ * @example
765
+ * await client.users.aggregators.delete(1);
766
+ */
767
+ async delete(aggregatorId) {
768
+ await this.http.delete(`/api/v1/users/me/aggregators/${aggregatorId}`);
769
+ }
770
+ /**
771
+ * Set an aggregator as the default.
772
+ *
773
+ * Only one aggregator can be the default at a time. Setting a new default
774
+ * automatically unsets the previous one.
775
+ *
776
+ * @param aggregatorId - The aggregator ID to set as default
777
+ * @returns The updated UserAggregator object with isDefault=true
778
+ * @throws {AuthenticationError} If not authenticated
779
+ * @throws {NotFoundError} If aggregator not found
780
+ *
781
+ * @example
782
+ * const agg = await client.users.aggregators.setDefault(2);
783
+ * console.log(`${agg.name} is now the default`);
784
+ */
785
+ async setDefault(aggregatorId) {
786
+ return this.http.patch(`/api/v1/users/me/aggregators/${aggregatorId}/default`);
787
+ }
788
+ };
789
+
680
790
  // src/resources/auth.ts
681
791
  init_errors();
682
792
  var AuthResource = class {
@@ -1064,116 +1174,6 @@ var AuthResource = class {
1064
1174
  }
1065
1175
  };
1066
1176
 
1067
- // src/resources/aggregators.ts
1068
- var AggregatorsResource = class {
1069
- constructor(http) {
1070
- this.http = http;
1071
- }
1072
- /**
1073
- * List all aggregator configurations for the current user.
1074
- *
1075
- * @returns Array of UserAggregator objects
1076
- * @throws {AuthenticationError} If not authenticated
1077
- *
1078
- * @example
1079
- * const aggregators = await client.users.aggregators.list();
1080
- * for (const agg of aggregators) {
1081
- * if (agg.isDefault) {
1082
- * console.log(`Default: ${agg.name}`);
1083
- * }
1084
- * }
1085
- */
1086
- async list() {
1087
- return this.http.get("/api/v1/users/me/aggregators");
1088
- }
1089
- /**
1090
- * Get a specific aggregator configuration by ID.
1091
- *
1092
- * @param aggregatorId - The aggregator ID
1093
- * @returns The UserAggregator object
1094
- * @throws {AuthenticationError} If not authenticated
1095
- * @throws {NotFoundError} If aggregator not found
1096
- *
1097
- * @example
1098
- * const agg = await client.users.aggregators.get(1);
1099
- * console.log(`${agg.name}: ${agg.url}`);
1100
- */
1101
- async get(aggregatorId) {
1102
- return this.http.get(`/api/v1/users/me/aggregators/${aggregatorId}`);
1103
- }
1104
- /**
1105
- * Create a new aggregator configuration.
1106
- *
1107
- * The first aggregator created is automatically set as the default.
1108
- *
1109
- * @param input - Aggregator creation input
1110
- * @returns The created UserAggregator object
1111
- * @throws {AuthenticationError} If not authenticated
1112
- * @throws {ValidationError} If input is invalid
1113
- *
1114
- * @example
1115
- * const agg = await client.users.aggregators.create({
1116
- * name: 'My Custom Aggregator',
1117
- * url: 'https://my-aggregator.example.com'
1118
- * });
1119
- * console.log(`Created: ${agg.id}`);
1120
- */
1121
- async create(input) {
1122
- return this.http.post("/api/v1/users/me/aggregators", input);
1123
- }
1124
- /**
1125
- * Update an aggregator configuration.
1126
- *
1127
- * Only provided fields will be updated.
1128
- *
1129
- * @param aggregatorId - The aggregator ID to update
1130
- * @param input - Fields to update
1131
- * @returns The updated UserAggregator object
1132
- * @throws {AuthenticationError} If not authenticated
1133
- * @throws {NotFoundError} If aggregator not found
1134
- * @throws {ValidationError} If input is invalid
1135
- *
1136
- * @example
1137
- * const agg = await client.users.aggregators.update(1, {
1138
- * name: 'Updated Name'
1139
- * });
1140
- */
1141
- async update(aggregatorId, input) {
1142
- return this.http.put(`/api/v1/users/me/aggregators/${aggregatorId}`, input);
1143
- }
1144
- /**
1145
- * Delete an aggregator configuration.
1146
- *
1147
- * @param aggregatorId - The aggregator ID to delete
1148
- * @throws {AuthenticationError} If not authenticated
1149
- * @throws {NotFoundError} If aggregator not found
1150
- *
1151
- * @example
1152
- * await client.users.aggregators.delete(1);
1153
- */
1154
- async delete(aggregatorId) {
1155
- await this.http.delete(`/api/v1/users/me/aggregators/${aggregatorId}`);
1156
- }
1157
- /**
1158
- * Set an aggregator as the default.
1159
- *
1160
- * Only one aggregator can be the default at a time. Setting a new default
1161
- * automatically unsets the previous one.
1162
- *
1163
- * @param aggregatorId - The aggregator ID to set as default
1164
- * @returns The updated UserAggregator object with isDefault=true
1165
- * @throws {AuthenticationError} If not authenticated
1166
- * @throws {NotFoundError} If aggregator not found
1167
- *
1168
- * @example
1169
- * const agg = await client.users.aggregators.setDefault(2);
1170
- * console.log(`${agg.name} is now the default`);
1171
- */
1172
- async setDefault(aggregatorId) {
1173
- return this.http.patch(`/api/v1/users/me/aggregators/${aggregatorId}/default`);
1174
- }
1175
- };
1176
-
1177
1177
  // src/resources/users.ts
1178
1178
  var UsersResource = class {
1179
1179
  constructor(http) {
@@ -2187,10 +2187,11 @@ function getEndpointPublicPath(endpoint) {
2187
2187
 
2188
2188
  // src/resources/chat.ts
2189
2189
  var AggregatorError = class extends SyftHubError {
2190
- constructor(message, status, detail) {
2190
+ constructor(message, status, detail, billing) {
2191
2191
  super(message);
2192
2192
  this.status = status;
2193
2193
  this.detail = detail;
2194
+ this.billing = billing;
2194
2195
  this.name = "AggregatorError";
2195
2196
  }
2196
2197
  };
@@ -2396,7 +2397,7 @@ var ChatResource = class _ChatResource {
2396
2397
  * Resolves endpoints, fetches tokens, and builds the aggregator request body.
2397
2398
  * Returns the request body and the resolved aggregator URL.
2398
2399
  */
2399
- async prepareRequest(options, stream) {
2400
+ async prepareRequest(options, stream, retrievalOnly = false) {
2400
2401
  const modelRef = await this.resolveEndpointRef(options.model, "model");
2401
2402
  const expandedDataSources = await this.expandCollectivePaths(options.dataSources ?? []);
2402
2403
  const dsRefs = [];
@@ -2431,7 +2432,8 @@ var ChatResource = class _ChatResource {
2431
2432
  stream,
2432
2433
  messages: options.messages,
2433
2434
  peerToken,
2434
- peerChannel
2435
+ peerChannel,
2436
+ retrievalOnly
2435
2437
  }
2436
2438
  );
2437
2439
  const effectiveAggregatorUrl = (options.aggregatorUrl ?? this.aggregatorUrl).replace(
@@ -2445,12 +2447,14 @@ var ChatResource = class _ChatResource {
2445
2447
  */
2446
2448
  async handleAggregatorErrorResponse(response) {
2447
2449
  let message = `HTTP ${response.status}`;
2450
+ let billing;
2448
2451
  try {
2449
2452
  const data = await response.json();
2450
- message = String(data["message"] ?? data["error"] ?? message);
2453
+ message = String(data["message"] ?? data["error"] ?? data["detail"] ?? message);
2454
+ billing = this.parseBilling(data);
2451
2455
  } catch {
2452
2456
  }
2453
- throw new AggregatorError(`Aggregator error: ${message}`, response.status);
2457
+ throw new AggregatorError(`Aggregator error: ${message}`, response.status, void 0, billing);
2454
2458
  }
2455
2459
  /**
2456
2460
  * Build the request body for the aggregator.
@@ -2494,6 +2498,9 @@ var ChatResource = class _ChatResource {
2494
2498
  if (options.peerChannel) {
2495
2499
  body["peer_channel"] = options.peerChannel;
2496
2500
  }
2501
+ if (options.retrievalOnly) {
2502
+ body["retrieval_only"] = true;
2503
+ }
2497
2504
  return body;
2498
2505
  }
2499
2506
  /**
@@ -2527,6 +2534,60 @@ var ChatResource = class _ChatResource {
2527
2534
  totalTokens: Number(data["total_tokens"] ?? 0)
2528
2535
  };
2529
2536
  }
2537
+ /**
2538
+ * Parse a single billing/policy-metadata entry from a raw (snake_case) dict.
2539
+ *
2540
+ * Shared by {@link parseBilling} (aggregated, carries `source`) and the
2541
+ * direct-path `policy_metadata` parsing in {@link SyftAIResource}.
2542
+ */
2543
+ static parseBillingEntry(raw) {
2544
+ const recipientRaw = raw["recipient"];
2545
+ const recipient = recipientRaw && typeof recipientRaw === "object" ? {
2546
+ username: recipientRaw["username"],
2547
+ email: recipientRaw["email"],
2548
+ walletAddress: recipientRaw["wallet_address"]
2549
+ } : void 0;
2550
+ const transactionRaw = raw["transaction"];
2551
+ const transaction = transactionRaw && typeof transactionRaw === "object" ? {
2552
+ rail: String(transactionRaw["rail"] ?? ""),
2553
+ id: String(transactionRaw["id"] ?? ""),
2554
+ reference: transactionRaw["reference"]
2555
+ } : void 0;
2556
+ return {
2557
+ source: raw["source"],
2558
+ policyType: String(raw["policy_type"] ?? ""),
2559
+ kind: String(raw["kind"] ?? ""),
2560
+ status: String(raw["status"] ?? ""),
2561
+ amount: raw["amount"] == null ? void 0 : Number(raw["amount"]),
2562
+ currency: raw["currency"],
2563
+ recipient,
2564
+ transaction,
2565
+ reasonCode: raw["reason_code"],
2566
+ reason: raw["reason"],
2567
+ details: raw["details"] ?? {}
2568
+ };
2569
+ }
2570
+ /**
2571
+ * Parse the aggregated `billing` block from a raw aggregator response.
2572
+ *
2573
+ * Returns undefined when no `billing` object is present (e.g. an error body
2574
+ * with no policy metadata). The wire keys are snake_case.
2575
+ */
2576
+ parseBilling(data) {
2577
+ const b = data["billing"];
2578
+ if (!b || typeof b !== "object") {
2579
+ return void 0;
2580
+ }
2581
+ const billing = b;
2582
+ const entriesRaw = Array.isArray(billing["entries"]) ? billing["entries"] : [];
2583
+ return {
2584
+ totalCost: billing["total_cost"] == null ? null : Number(billing["total_cost"]),
2585
+ currency: billing["currency"] ?? null,
2586
+ entries: entriesRaw.map(
2587
+ (e) => _ChatResource.parseBillingEntry(e)
2588
+ )
2589
+ };
2590
+ }
2530
2591
  /**
2531
2592
  * Parse document sources from raw data.
2532
2593
  * The new format is a dict mapping document title to {slug, content}.
@@ -2594,15 +2655,82 @@ var ChatResource = class _ChatResource {
2594
2655
  const usageData = data["usage"];
2595
2656
  const usage = usageData ? this.parseUsage(usageData) : void 0;
2596
2657
  const profitShare = data["profit_share"];
2658
+ const billing = this.parseBilling(data);
2597
2659
  return {
2598
2660
  response: String(data["response"] ?? ""),
2599
2661
  sources,
2600
2662
  retrievalInfo,
2601
2663
  metadata,
2602
2664
  usage,
2603
- profitShare
2665
+ profitShare,
2666
+ billing
2604
2667
  };
2605
2668
  }
2669
+ /**
2670
+ * Placeholder model for retrieval-only requests. The aggregator requires a
2671
+ * `model` field on every request, but short-circuits before dereferencing it
2672
+ * when `retrieval_only` is set, so an empty ref is never contacted.
2673
+ */
2674
+ static RETRIEVAL_ONLY_MODEL = {
2675
+ url: "",
2676
+ slug: "",
2677
+ name: "retrieval-only"
2678
+ };
2679
+ /**
2680
+ * Retrieve documents from data sources without model generation.
2681
+ *
2682
+ * Drives the aggregator's retrieval-only path: data sources are queried in
2683
+ * parallel (with satellite-token auth and MPP payment handled server-side,
2684
+ * exactly like {@link complete}), but no model is invoked.
2685
+ *
2686
+ * Prefer the symmetric `client.search.query(...)` facade; this is the
2687
+ * underlying primitive.
2688
+ *
2689
+ * @param options - Search options
2690
+ * @returns SearchResponse with retrieved documents and per-source metadata
2691
+ * @throws {EndpointResolutionError} If a data source cannot be resolved
2692
+ * @throws {AggregatorError} If the aggregator service fails
2693
+ */
2694
+ async retrieve(options) {
2695
+ const chatOptions = {
2696
+ prompt: options.prompt,
2697
+ model: _ChatResource.RETRIEVAL_ONLY_MODEL,
2698
+ dataSources: options.dataSources,
2699
+ topK: options.topK,
2700
+ similarityThreshold: options.similarityThreshold,
2701
+ aggregatorUrl: options.aggregatorUrl,
2702
+ guestMode: options.guestMode
2703
+ };
2704
+ const { requestBody, effectiveAggregatorUrl } = await this.prepareRequest(
2705
+ chatOptions,
2706
+ false,
2707
+ true
2708
+ );
2709
+ const response = await fetch(`${effectiveAggregatorUrl}/chat`, {
2710
+ method: "POST",
2711
+ headers: { "Content-Type": "application/json" },
2712
+ body: JSON.stringify(requestBody),
2713
+ signal: options.signal
2714
+ });
2715
+ if (!response.ok) {
2716
+ return this.handleAggregatorErrorResponse(response);
2717
+ }
2718
+ const data = await response.json();
2719
+ const sources = this.parseDocumentSources(
2720
+ data["sources"]
2721
+ );
2722
+ const documents = Object.entries(sources).map(([title, source]) => ({
2723
+ title,
2724
+ slug: source.slug,
2725
+ content: source.content
2726
+ }));
2727
+ const retrievalInfo = this.parseRetrievalInfo(
2728
+ data["retrieval_info"]
2729
+ );
2730
+ const metadata = this.parseMetadata(data["metadata"] ?? {});
2731
+ const billing = this.parseBilling(data);
2732
+ return { documents, retrievalInfo, metadata, billing };
2733
+ }
2606
2734
  /**
2607
2735
  * Send a chat request and stream response events.
2608
2736
  *
@@ -2701,13 +2829,24 @@ var ChatResource = class _ChatResource {
2701
2829
  const usageData = data["usage"];
2702
2830
  const usage = usageData ? this.parseUsage(usageData) : void 0;
2703
2831
  const profitShare = data["profit_share"];
2832
+ const billing = this.parseBilling(data);
2704
2833
  const response = data["response"];
2705
- return { type: "done", sources, retrievalInfo, metadata, usage, profitShare, response };
2834
+ return {
2835
+ type: "done",
2836
+ sources,
2837
+ retrievalInfo,
2838
+ metadata,
2839
+ usage,
2840
+ profitShare,
2841
+ billing,
2842
+ response
2843
+ };
2706
2844
  }
2707
2845
  case "error":
2708
2846
  return {
2709
2847
  type: "error",
2710
- message: String(data["message"] ?? "Unknown error")
2848
+ message: String(data["message"] ?? "Unknown error"),
2849
+ billing: this.parseBilling(data)
2711
2850
  };
2712
2851
  default:
2713
2852
  console.warn(`[SyftHub] Unknown SSE event type received from aggregator: ${eventType}`);
@@ -2748,6 +2887,34 @@ var ChatResource = class _ChatResource {
2748
2887
  }
2749
2888
  };
2750
2889
 
2890
+ // src/resources/search.ts
2891
+ var SearchResource = class {
2892
+ /**
2893
+ * @param chat - The chat resource that owns aggregator communication and
2894
+ * request preparation (satellite tokens, MPP, collective expansion). Search
2895
+ * reuses it rather than duplicating that logic.
2896
+ */
2897
+ constructor(chat) {
2898
+ this.chat = chat;
2899
+ }
2900
+ /**
2901
+ * Retrieve documents from data sources without model generation.
2902
+ *
2903
+ * @param options - Search options (prompt, data sources, top-k, etc.)
2904
+ * @returns SearchResponse with retrieved documents and per-source metadata
2905
+ *
2906
+ * @example
2907
+ * const result = await client.search.query({
2908
+ * prompt: 'Hello, world!',
2909
+ * dataSources: ['epfl-news/epfl-news'],
2910
+ * });
2911
+ * console.log(result.documents.length, 'documents');
2912
+ */
2913
+ async query(options) {
2914
+ return this.chat.retrieve(options);
2915
+ }
2916
+ };
2917
+
2751
2918
  // src/resources/syftai.ts
2752
2919
  init_errors();
2753
2920
  var RetrievalError = class extends SyftHubError {
@@ -2863,6 +3030,28 @@ var SyftAIResource = class {
2863
3030
  }
2864
3031
  return documents;
2865
3032
  }
3033
+ /**
3034
+ * Parse the raw `policy_metadata` block from a syft-space response.
3035
+ *
3036
+ * The direct path (Boundary A) carries a top-level `policy_metadata` object
3037
+ * shaped `{ outcome, entries: [...] }`. Entries reuse the {@link BillingEntry}
3038
+ * shape (with `source` absent), so this delegates entry parsing to
3039
+ * {@link ChatResource.parseBillingEntry}. The wire keys are snake_case.
3040
+ */
3041
+ parsePolicyMetadata(data) {
3042
+ const pm = data["policy_metadata"];
3043
+ if (!pm || typeof pm !== "object") {
3044
+ return void 0;
3045
+ }
3046
+ const meta = pm;
3047
+ const entriesRaw = Array.isArray(meta["entries"]) ? meta["entries"] : [];
3048
+ return {
3049
+ outcome: String(meta["outcome"] ?? ""),
3050
+ entries: entriesRaw.map(
3051
+ (e) => ChatResource.parseBillingEntry(e)
3052
+ )
3053
+ };
3054
+ }
2866
3055
  /**
2867
3056
  * Query a data source endpoint directly.
2868
3057
  *
@@ -2872,7 +3061,9 @@ var SyftAIResource = class {
2872
3061
  * owner is known (`ownerUsername` option or `endpoint.ownerUsername`).
2873
3062
  *
2874
3063
  * @param options - Query options
2875
- * @returns Array of Document objects
3064
+ * @returns DataSourceQueryResult the retrieved documents plus the raw
3065
+ * `policyMetadata` block from the syft-space response (price, recipient,
3066
+ * transaction, status).
2876
3067
  * @throws {RetrievalError} If the query fails
2877
3068
  */
2878
3069
  async queryDataSource(options) {
@@ -2943,8 +3134,10 @@ var SyftAIResource = class {
2943
3134
  throw new RetrievalError(`Data source query failed: ${message}`, endpoint.slug);
2944
3135
  }
2945
3136
  const data = await response.json();
2946
- const documents = this.parseDocuments(data);
2947
- return documents;
3137
+ return {
3138
+ documents: this.parseDocuments(data),
3139
+ policyMetadata: this.parsePolicyMetadata(data)
3140
+ };
2948
3141
  }
2949
3142
  /**
2950
3143
  * Query a model endpoint directly.
@@ -3081,8 +3274,10 @@ var SyftHubClient = class {
3081
3274
  _myEndpoints;
3082
3275
  _hub;
3083
3276
  _accounting;
3277
+ _aggregators;
3084
3278
  _agent;
3085
3279
  _chat;
3280
+ _search;
3086
3281
  _syftai;
3087
3282
  _apiTokens;
3088
3283
  /**
@@ -3201,43 +3396,23 @@ var SyftHubClient = class {
3201
3396
  * const balance = await client.accounting.getBalance();
3202
3397
  */
3203
3398
  get accounting() {
3204
- if (this._accounting) {
3205
- return this._accounting;
3206
- }
3207
- throw new AuthenticationError(
3208
- "Accounting not initialized. Call `await client.initAccounting()` after login."
3209
- );
3210
- }
3211
- /**
3212
- * Initialize the accounting (wallet) resource.
3213
- *
3214
- * The wallet API uses the same SyftHub authentication as other resources.
3215
- * This method simply verifies authentication and creates the resource.
3216
- *
3217
- * @returns The initialized AccountingResource
3218
- * @throws {AuthenticationError} If not authenticated
3219
- *
3220
- * @example
3221
- * // Login first, then initialize accounting
3222
- * await client.auth.login('alice', 'password');
3223
- * await client.initAccounting();
3224
- *
3225
- * // Now accounting is available
3226
- * const wallet = await client.accounting.getWallet();
3227
- * const balance = await client.accounting.getBalance();
3228
- */
3229
- async initAccounting() {
3230
- if (this._accounting) {
3231
- return this._accounting;
3232
- }
3233
3399
  if (!this.isAuthenticated) {
3234
3400
  throw new AuthenticationError(
3235
3401
  "Must be logged in to use accounting. Call client.auth.login() first."
3236
3402
  );
3237
3403
  }
3238
- this._accounting = new AccountingResource(this.http);
3404
+ if (!this._accounting) {
3405
+ this._accounting = new AccountingResource(this.http);
3406
+ }
3239
3407
  return this._accounting;
3240
3408
  }
3409
+ /**
3410
+ * @deprecated Accounting is now initialized automatically on first access.
3411
+ * This method is kept for backward compatibility and is a no-op.
3412
+ */
3413
+ async initAccounting() {
3414
+ return this.accounting;
3415
+ }
3241
3416
  /**
3242
3417
  * Chat resource for RAG-augmented conversations via the Aggregator.
3243
3418
  *
@@ -3270,6 +3445,28 @@ var SyftHubClient = class {
3270
3445
  }
3271
3446
  return this._chat;
3272
3447
  }
3448
+ /**
3449
+ * Retrieval-only search via the Aggregator (no model generation).
3450
+ *
3451
+ * Symmetric counterpart to {@link chat}: queries data sources for relevant
3452
+ * documents without invoking a model. Satellite-token auth and MPP payment
3453
+ * are handled by the aggregator exactly as for chat.
3454
+ *
3455
+ * @example
3456
+ * const result = await client.search.query({
3457
+ * prompt: 'Hello, world!',
3458
+ * dataSources: ['epfl-news/epfl-news'],
3459
+ * });
3460
+ * for (const doc of result.documents) {
3461
+ * console.log(doc.title, doc.content.slice(0, 80));
3462
+ * }
3463
+ */
3464
+ get search() {
3465
+ if (!this._search) {
3466
+ this._search = new SearchResource(this.chat);
3467
+ }
3468
+ return this._search;
3469
+ }
3273
3470
  /**
3274
3471
  * SyftAI-Space resource for direct endpoint queries (low-level API).
3275
3472
  *
@@ -3320,6 +3517,12 @@ var SyftHubClient = class {
3320
3517
  * // Revoke a token
3321
3518
  * await client.apiTokens.revoke(tokenId);
3322
3519
  */
3520
+ get aggregators() {
3521
+ if (!this._aggregators) {
3522
+ this._aggregators = new AggregatorsResource(this.http);
3523
+ }
3524
+ return this._aggregators;
3525
+ }
3323
3526
  get apiTokens() {
3324
3527
  if (!this._apiTokens) {
3325
3528
  this._apiTokens = new APITokensResource(this.http);
@@ -3402,6 +3605,6 @@ var SyftHubClient = class {
3402
3605
  // src/index.ts
3403
3606
  init_errors();
3404
3607
 
3405
- export { APIError, APITokensResource, AccountingAccountExistsError, AccountingResource, AccountingServiceUnavailableError, AgentResource, AgentSessionClient, AgentSessionError, AggregatorError, AggregatorsResource, AuthenticationError, AuthorizationError, ChatResource, ConfigurationError, EndpointResolutionError, EndpointType, GenerationError, InvalidAccountingPasswordError, NetworkError, NotFoundError, PageIterator, RetrievalError, SyftAIResource, SyftHubClient, SyftHubError, UserAlreadyExistsError, UserRole, ValidationError, Visibility, createAccountingResource, getEndpointPublicPath };
3608
+ export { APIError, APITokensResource, AccountingAccountExistsError, AccountingResource, AccountingServiceUnavailableError, AgentResource, AgentSessionClient, AgentSessionError, AggregatorError, AggregatorsResource, AuthenticationError, AuthorizationError, ChatResource, ConfigurationError, EndpointResolutionError, EndpointType, GenerationError, InvalidAccountingPasswordError, NetworkError, NotFoundError, PageIterator, RetrievalError, SearchResource, SyftAIResource, SyftHubClient, SyftHubError, UserAlreadyExistsError, UserRole, ValidationError, Visibility, createAccountingResource, getEndpointPublicPath };
3406
3609
  //# sourceMappingURL=index.js.map
3407
3610
  //# sourceMappingURL=index.js.map