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