@vectros-ai/mcp-server 0.18.2 → 0.19.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/dist/index.js CHANGED
@@ -42,7 +42,7 @@ module.exports = __toCommonJS(index_exports);
42
42
  // src/server.ts
43
43
  var import_server = require("@modelcontextprotocol/sdk/server/index.js");
44
44
 
45
- // ../../node_modules/@vectros-ai/sdk/dist/index.mjs
45
+ // node_modules/@vectros-ai/sdk/dist/index.mjs
46
46
  var __defProp2 = Object.defineProperty;
47
47
  var __export2 = (target, all) => {
48
48
  for (var name in all)
@@ -63,6 +63,7 @@ __export2(api_exports, {
63
63
  ContentTooLargeError: () => ContentTooLargeError,
64
64
  DocumentAskRequest: () => DocumentAskRequest,
65
65
  DocumentContextEvent: () => DocumentContextEvent,
66
+ DocumentExtraction: () => DocumentExtraction,
66
67
  DocumentLookupRequest: () => DocumentLookupRequest,
67
68
  DocumentPatchRequest: () => DocumentPatchRequest,
68
69
  DocumentRequest: () => DocumentRequest,
@@ -115,6 +116,7 @@ __export2(api_exports, {
115
116
  UserExistsResponse: () => UserExistsResponse,
116
117
  UserRequest: () => UserRequest,
117
118
  UserResponse: () => UserResponse,
119
+ agents: () => agents_exports,
118
120
  auth: () => auth_exports,
119
121
  compliance: () => compliance_exports,
120
122
  documents: () => documents_exports,
@@ -380,6 +382,7 @@ var UnprocessableEntityError = class extends VectrosError {
380
382
  this.name = this.constructor.name;
381
383
  }
382
384
  };
385
+ var agents_exports = {};
383
386
  var auth_exports = {};
384
387
  var ErasureRequest;
385
388
  ((ErasureRequest2) => {
@@ -584,6 +587,18 @@ var DocumentContextEvent;
584
587
  DocumentContext: "document_context"
585
588
  };
586
589
  })(DocumentContextEvent || (DocumentContextEvent = {}));
590
+ var DocumentExtraction;
591
+ ((DocumentExtraction2) => {
592
+ DocumentExtraction2.Outcome = {
593
+ Ok: "OK",
594
+ Truncated: "TRUNCATED",
595
+ NoText: "NO_TEXT",
596
+ Encrypted: "ENCRYPTED",
597
+ Unsupported: "UNSUPPORTED",
598
+ TooLarge: "TOO_LARGE",
599
+ Unreadable: "UNREADABLE"
600
+ };
601
+ })(DocumentExtraction || (DocumentExtraction = {}));
587
602
  var DocumentRequest;
588
603
  ((DocumentRequest2) => {
589
604
  DocumentRequest2.IndexMode = {
@@ -1889,13 +1904,13 @@ function joinPathSegments(left, right) {
1889
1904
  }
1890
1905
  return `${left}/${right}`;
1891
1906
  }
1892
- function trimSlashes(str) {
1893
- if (!str) return str;
1907
+ function trimSlashes(str2) {
1908
+ if (!str2) return str2;
1894
1909
  let start = 0;
1895
- let end = str.length;
1896
- if (str.startsWith("/")) start = 1;
1897
- if (str.endsWith("/")) end = str.length - 1;
1898
- return start === 0 && end === str.length ? str : str.slice(start, end);
1910
+ let end = str2.length;
1911
+ if (str2.startsWith("/")) start = 1;
1912
+ if (str2.endsWith("/")) end = str2.length - 1;
1913
+ return start === 0 && end === str2.length ? str2 : str2.slice(start, end);
1899
1914
  }
1900
1915
  var Supplier = {
1901
1916
  get: async (supplier) => {
@@ -2410,48 +2425,675 @@ function normalizeClientOptionsWithAuth(options) {
2410
2425
  normalized.authProvider = options.auth;
2411
2426
  return normalized;
2412
2427
  }
2413
- Object.assign(normalized, options.auth);
2414
- }
2415
- const normalizedWithNoOpAuthProvider = withNoOpAuthProvider(normalized);
2416
- normalized.authProvider ?? (normalized.authProvider = new BearerAuthProvider(normalizedWithNoOpAuthProvider));
2417
- return normalized;
2418
- }
2419
- function withNoOpAuthProvider(options) {
2420
- return {
2421
- ...options,
2422
- authProvider: new NoOpAuthProvider()
2423
- };
2424
- }
2425
- function handleNonStatusCodeError(error, rawResponse, method, path) {
2426
- switch (error.reason) {
2427
- case "non-json":
2428
- throw new VectrosError({
2429
- statusCode: error.statusCode,
2430
- body: error.rawBody,
2431
- rawResponse
2432
- });
2433
- case "body-is-null":
2434
- throw new VectrosError({
2435
- statusCode: error.statusCode,
2436
- rawResponse
2437
- });
2438
- case "timeout":
2439
- throw new VectrosTimeoutError(`Timeout exceeded when calling ${method} ${path}.`, {
2440
- cause: error.cause
2441
- });
2442
- case "unknown":
2443
- throw new VectrosError({
2444
- message: error.errorMessage,
2445
- rawResponse,
2446
- cause: error.cause
2447
- });
2448
- default:
2449
- throw new VectrosError({
2450
- message: "Unknown error",
2451
- rawResponse
2452
- });
2428
+ Object.assign(normalized, options.auth);
2429
+ }
2430
+ const normalizedWithNoOpAuthProvider = withNoOpAuthProvider(normalized);
2431
+ normalized.authProvider ?? (normalized.authProvider = new BearerAuthProvider(normalizedWithNoOpAuthProvider));
2432
+ return normalized;
2433
+ }
2434
+ function withNoOpAuthProvider(options) {
2435
+ return {
2436
+ ...options,
2437
+ authProvider: new NoOpAuthProvider()
2438
+ };
2439
+ }
2440
+ function handleNonStatusCodeError(error, rawResponse, method, path) {
2441
+ switch (error.reason) {
2442
+ case "non-json":
2443
+ throw new VectrosError({
2444
+ statusCode: error.statusCode,
2445
+ body: error.rawBody,
2446
+ rawResponse
2447
+ });
2448
+ case "body-is-null":
2449
+ throw new VectrosError({
2450
+ statusCode: error.statusCode,
2451
+ rawResponse
2452
+ });
2453
+ case "timeout":
2454
+ throw new VectrosTimeoutError(`Timeout exceeded when calling ${method} ${path}.`, {
2455
+ cause: error.cause
2456
+ });
2457
+ case "unknown":
2458
+ throw new VectrosError({
2459
+ message: error.errorMessage,
2460
+ rawResponse,
2461
+ cause: error.cause
2462
+ });
2463
+ default:
2464
+ throw new VectrosError({
2465
+ message: "Unknown error",
2466
+ rawResponse
2467
+ });
2468
+ }
2469
+ }
2470
+ var AgentsClient = class {
2471
+ constructor(options) {
2472
+ this._options = normalizeClientOptionsWithAuth(options);
2473
+ }
2474
+ /**
2475
+ * Returns a page of the agents in the app context, newest first, each at its latest version. A caller holding `runs:c` but not `agents:r` gets only each agent's `id`, `name`, `description`, `latestVersion` and `status`. Retired and purged agents are left out unless `includeRetired=true`. Requires `agents:r` or `runs:c`.
2476
+ *
2477
+ * @param {Vectros.ListAgentsRequest} request
2478
+ * @param {AgentsClient.RequestOptions} requestOptions - Request-specific configuration.
2479
+ *
2480
+ * @throws {@link Vectros.ForbiddenError}
2481
+ *
2482
+ * @example
2483
+ * await client.agents.listAgents()
2484
+ */
2485
+ listAgents(request = {}, requestOptions) {
2486
+ return HttpResponsePromise.fromPromise(this.__listAgents(request, requestOptions));
2487
+ }
2488
+ async __listAgents(request = {}, requestOptions) {
2489
+ const { contextId, includeRetired, startFrom, limit } = request;
2490
+ const _queryParams = {
2491
+ contextId,
2492
+ includeRetired,
2493
+ startFrom,
2494
+ limit
2495
+ };
2496
+ const _authRequest = await this._options.authProvider.getAuthRequest();
2497
+ const _headers = mergeHeaders(
2498
+ _authRequest.headers,
2499
+ this._options?.headers,
2500
+ requestOptions?.headers
2501
+ );
2502
+ const _response = await fetcher({
2503
+ url: url_exports.join(
2504
+ await Supplier.get(this._options.baseUrl) ?? await Supplier.get(this._options.environment),
2505
+ "v1/agents"
2506
+ ),
2507
+ method: "GET",
2508
+ headers: _headers,
2509
+ queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
2510
+ timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
2511
+ maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
2512
+ abortSignal: requestOptions?.abortSignal,
2513
+ fetchFn: this._options?.fetch,
2514
+ logging: this._options.logging
2515
+ });
2516
+ if (_response.ok) {
2517
+ return { data: _response.body, rawResponse: _response.rawResponse };
2518
+ }
2519
+ if (_response.error.reason === "status-code") {
2520
+ switch (_response.error.statusCode) {
2521
+ case 403:
2522
+ throw new ForbiddenError(_response.error.body, _response.rawResponse);
2523
+ default:
2524
+ throw new VectrosError({
2525
+ statusCode: _response.error.statusCode,
2526
+ body: _response.error.body,
2527
+ rawResponse: _response.rawResponse
2528
+ });
2529
+ }
2530
+ }
2531
+ return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/agents");
2532
+ }
2533
+ /**
2534
+ * Stores a new agent at version 1, or returns the existing agent of this name unchanged. With `?upsert=true` an existing agent whose definition differs gets a new version instead. Requires the `agents:c` scope (also `agents:u` with `?upsert=true`).
2535
+ *
2536
+ * @param {Vectros.CreateAgentRequest} request
2537
+ * @param {AgentsClient.RequestOptions} requestOptions - Request-specific configuration.
2538
+ *
2539
+ * @throws {@link Vectros.BadRequestError}
2540
+ * @throws {@link Vectros.ForbiddenError}
2541
+ * @throws {@link Vectros.ConflictError}
2542
+ * @throws {@link Vectros.TooManyRequestsError}
2543
+ *
2544
+ * @example
2545
+ * await client.agents.createAgent({
2546
+ * body: {
2547
+ * model: "claude-sonnet-5-5"
2548
+ * }
2549
+ * })
2550
+ */
2551
+ createAgent(request, requestOptions) {
2552
+ return HttpResponsePromise.fromPromise(this.__createAgent(request, requestOptions));
2553
+ }
2554
+ async __createAgent(request, requestOptions) {
2555
+ const { upsert, contextId, body: _body } = request;
2556
+ const _queryParams = {
2557
+ upsert,
2558
+ contextId
2559
+ };
2560
+ const _authRequest = await this._options.authProvider.getAuthRequest();
2561
+ const _headers = mergeHeaders(
2562
+ _authRequest.headers,
2563
+ this._options?.headers,
2564
+ requestOptions?.headers
2565
+ );
2566
+ const _response = await fetcher({
2567
+ url: url_exports.join(
2568
+ await Supplier.get(this._options.baseUrl) ?? await Supplier.get(this._options.environment),
2569
+ "v1/agents"
2570
+ ),
2571
+ method: "POST",
2572
+ headers: _headers,
2573
+ contentType: "application/json",
2574
+ queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
2575
+ requestType: "json",
2576
+ body: _body,
2577
+ timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
2578
+ maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
2579
+ abortSignal: requestOptions?.abortSignal,
2580
+ fetchFn: this._options?.fetch,
2581
+ logging: this._options.logging
2582
+ });
2583
+ if (_response.ok) {
2584
+ return { data: _response.body, rawResponse: _response.rawResponse };
2585
+ }
2586
+ if (_response.error.reason === "status-code") {
2587
+ switch (_response.error.statusCode) {
2588
+ case 400:
2589
+ throw new BadRequestError(_response.error.body, _response.rawResponse);
2590
+ case 403:
2591
+ throw new ForbiddenError(_response.error.body, _response.rawResponse);
2592
+ case 409:
2593
+ throw new ConflictError(_response.error.body, _response.rawResponse);
2594
+ case 429:
2595
+ throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
2596
+ default:
2597
+ throw new VectrosError({
2598
+ statusCode: _response.error.statusCode,
2599
+ body: _response.error.body,
2600
+ rawResponse: _response.rawResponse
2601
+ });
2602
+ }
2603
+ }
2604
+ return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/agents");
2605
+ }
2606
+ /**
2607
+ * Returns the agent at its latest version, or at `version`. Requires the `agents:r` scope.
2608
+ *
2609
+ * @param {Vectros.GetAgentRequest} request
2610
+ * @param {AgentsClient.RequestOptions} requestOptions - Request-specific configuration.
2611
+ *
2612
+ * @throws {@link Vectros.ForbiddenError}
2613
+ * @throws {@link Vectros.NotFoundError}
2614
+ *
2615
+ * @example
2616
+ * await client.agents.getAgent({
2617
+ * id: "id"
2618
+ * })
2619
+ */
2620
+ getAgent(request, requestOptions) {
2621
+ return HttpResponsePromise.fromPromise(this.__getAgent(request, requestOptions));
2622
+ }
2623
+ async __getAgent(request, requestOptions) {
2624
+ const { id, contextId, version } = request;
2625
+ const _queryParams = {
2626
+ contextId,
2627
+ version
2628
+ };
2629
+ const _authRequest = await this._options.authProvider.getAuthRequest();
2630
+ const _headers = mergeHeaders(
2631
+ _authRequest.headers,
2632
+ this._options?.headers,
2633
+ requestOptions?.headers
2634
+ );
2635
+ const _response = await fetcher({
2636
+ url: url_exports.join(
2637
+ await Supplier.get(this._options.baseUrl) ?? await Supplier.get(this._options.environment),
2638
+ `v1/agents/${url_exports.encodePathParam(id)}`
2639
+ ),
2640
+ method: "GET",
2641
+ headers: _headers,
2642
+ queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
2643
+ timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
2644
+ maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
2645
+ abortSignal: requestOptions?.abortSignal,
2646
+ fetchFn: this._options?.fetch,
2647
+ logging: this._options.logging
2648
+ });
2649
+ if (_response.ok) {
2650
+ return { data: _response.body, rawResponse: _response.rawResponse };
2651
+ }
2652
+ if (_response.error.reason === "status-code") {
2653
+ switch (_response.error.statusCode) {
2654
+ case 403:
2655
+ throw new ForbiddenError(_response.error.body, _response.rawResponse);
2656
+ case 404:
2657
+ throw new NotFoundError(_response.error.body, _response.rawResponse);
2658
+ default:
2659
+ throw new VectrosError({
2660
+ statusCode: _response.error.statusCode,
2661
+ body: _response.error.body,
2662
+ rawResponse: _response.rawResponse
2663
+ });
2664
+ }
2665
+ }
2666
+ return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/agents/{id}");
2667
+ }
2668
+ /**
2669
+ * Stores the body as the agent's next version. Earlier versions never change. Every optional field you leave out takes its default; `model` is required. With `?restore=true` a retired agent becomes active again with this version. Requires the `agents:u` scope.
2670
+ *
2671
+ * @param {Vectros.UpdateAgentRequest} request
2672
+ * @param {AgentsClient.RequestOptions} requestOptions - Request-specific configuration.
2673
+ *
2674
+ * @throws {@link Vectros.BadRequestError}
2675
+ * @throws {@link Vectros.ForbiddenError}
2676
+ * @throws {@link Vectros.NotFoundError}
2677
+ * @throws {@link Vectros.ConflictError}
2678
+ * @throws {@link Vectros.TooManyRequestsError}
2679
+ *
2680
+ * @example
2681
+ * await client.agents.updateAgent({
2682
+ * id: "id",
2683
+ * body: {
2684
+ * model: "claude-sonnet-5-5"
2685
+ * }
2686
+ * })
2687
+ */
2688
+ updateAgent(request, requestOptions) {
2689
+ return HttpResponsePromise.fromPromise(this.__updateAgent(request, requestOptions));
2690
+ }
2691
+ async __updateAgent(request, requestOptions) {
2692
+ const { id, restore, contextId, body: _body } = request;
2693
+ const _queryParams = {
2694
+ restore,
2695
+ contextId
2696
+ };
2697
+ const _authRequest = await this._options.authProvider.getAuthRequest();
2698
+ const _headers = mergeHeaders(
2699
+ _authRequest.headers,
2700
+ this._options?.headers,
2701
+ requestOptions?.headers
2702
+ );
2703
+ const _response = await fetcher({
2704
+ url: url_exports.join(
2705
+ await Supplier.get(this._options.baseUrl) ?? await Supplier.get(this._options.environment),
2706
+ `v1/agents/${url_exports.encodePathParam(id)}`
2707
+ ),
2708
+ method: "PUT",
2709
+ headers: _headers,
2710
+ contentType: "application/json",
2711
+ queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
2712
+ requestType: "json",
2713
+ body: _body,
2714
+ timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
2715
+ maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
2716
+ abortSignal: requestOptions?.abortSignal,
2717
+ fetchFn: this._options?.fetch,
2718
+ logging: this._options.logging
2719
+ });
2720
+ if (_response.ok) {
2721
+ return { data: _response.body, rawResponse: _response.rawResponse };
2722
+ }
2723
+ if (_response.error.reason === "status-code") {
2724
+ switch (_response.error.statusCode) {
2725
+ case 400:
2726
+ throw new BadRequestError(_response.error.body, _response.rawResponse);
2727
+ case 403:
2728
+ throw new ForbiddenError(_response.error.body, _response.rawResponse);
2729
+ case 404:
2730
+ throw new NotFoundError(_response.error.body, _response.rawResponse);
2731
+ case 409:
2732
+ throw new ConflictError(_response.error.body, _response.rawResponse);
2733
+ case 429:
2734
+ throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
2735
+ default:
2736
+ throw new VectrosError({
2737
+ statusCode: _response.error.statusCode,
2738
+ body: _response.error.body,
2739
+ rawResponse: _response.rawResponse
2740
+ });
2741
+ }
2742
+ }
2743
+ return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/v1/agents/{id}");
2744
+ }
2745
+ /**
2746
+ * Retires the agent: its versions stay readable, it leaves the default listing, and it can be restored. With `?purge=true` a retired agent's content (every version's prompt, description, tools, model, limits and budget) is erased, leaving its id, name, version numbers and timestamps; its change history keeps only a one-way hash of the prompt, description and tools. If the agent is restored while a purge is running, the purge stops (409) and versions it already erased stay erased; purge it again after retiring it. With `?purge=true&version=N` one version that is not the latest is erased. The name of a purged agent stays reserved. Requires the `agents:d` scope.
2747
+ *
2748
+ * @param {Vectros.DeleteAgentRequest} request
2749
+ * @param {AgentsClient.RequestOptions} requestOptions - Request-specific configuration.
2750
+ *
2751
+ * @throws {@link Vectros.BadRequestError}
2752
+ * @throws {@link Vectros.ForbiddenError}
2753
+ * @throws {@link Vectros.NotFoundError}
2754
+ * @throws {@link Vectros.ConflictError}
2755
+ * @throws {@link Vectros.TooManyRequestsError}
2756
+ *
2757
+ * @example
2758
+ * await client.agents.deleteAgent({
2759
+ * id: "id"
2760
+ * })
2761
+ */
2762
+ deleteAgent(request, requestOptions) {
2763
+ return HttpResponsePromise.fromPromise(this.__deleteAgent(request, requestOptions));
2764
+ }
2765
+ async __deleteAgent(request, requestOptions) {
2766
+ const { id, purge, version, contextId } = request;
2767
+ const _queryParams = {
2768
+ purge,
2769
+ version,
2770
+ contextId
2771
+ };
2772
+ const _authRequest = await this._options.authProvider.getAuthRequest();
2773
+ const _headers = mergeHeaders(
2774
+ _authRequest.headers,
2775
+ this._options?.headers,
2776
+ requestOptions?.headers
2777
+ );
2778
+ const _response = await fetcher({
2779
+ url: url_exports.join(
2780
+ await Supplier.get(this._options.baseUrl) ?? await Supplier.get(this._options.environment),
2781
+ `v1/agents/${url_exports.encodePathParam(id)}`
2782
+ ),
2783
+ method: "DELETE",
2784
+ headers: _headers,
2785
+ queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
2786
+ timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
2787
+ maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
2788
+ abortSignal: requestOptions?.abortSignal,
2789
+ fetchFn: this._options?.fetch,
2790
+ logging: this._options.logging
2791
+ });
2792
+ if (_response.ok) {
2793
+ return { data: _response.body, rawResponse: _response.rawResponse };
2794
+ }
2795
+ if (_response.error.reason === "status-code") {
2796
+ switch (_response.error.statusCode) {
2797
+ case 400:
2798
+ throw new BadRequestError(_response.error.body, _response.rawResponse);
2799
+ case 403:
2800
+ throw new ForbiddenError(_response.error.body, _response.rawResponse);
2801
+ case 404:
2802
+ throw new NotFoundError(_response.error.body, _response.rawResponse);
2803
+ case 409:
2804
+ throw new ConflictError(_response.error.body, _response.rawResponse);
2805
+ case 429:
2806
+ throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
2807
+ default:
2808
+ throw new VectrosError({
2809
+ statusCode: _response.error.statusCode,
2810
+ body: _response.error.body,
2811
+ rawResponse: _response.rawResponse
2812
+ });
2813
+ }
2814
+ }
2815
+ return handleNonStatusCodeError(_response.error, _response.rawResponse, "DELETE", "/v1/agents/{id}");
2816
+ }
2817
+ /**
2818
+ * Returns a page of the agent's versions, newest first: who wrote each and when, and whether it was purged. With `includeContent=true` each entry carries its full definition. Requires the `agents:r` scope.
2819
+ *
2820
+ * @param {Vectros.ListAgentVersionsRequest} request
2821
+ * @param {AgentsClient.RequestOptions} requestOptions - Request-specific configuration.
2822
+ *
2823
+ * @throws {@link Vectros.ForbiddenError}
2824
+ * @throws {@link Vectros.NotFoundError}
2825
+ *
2826
+ * @example
2827
+ * await client.agents.listAgentVersions({
2828
+ * id: "id"
2829
+ * })
2830
+ */
2831
+ listAgentVersions(request, requestOptions) {
2832
+ return HttpResponsePromise.fromPromise(this.__listAgentVersions(request, requestOptions));
2833
+ }
2834
+ async __listAgentVersions(request, requestOptions) {
2835
+ const { id, contextId, includeContent, startFrom, limit } = request;
2836
+ const _queryParams = {
2837
+ contextId,
2838
+ includeContent,
2839
+ startFrom,
2840
+ limit
2841
+ };
2842
+ const _authRequest = await this._options.authProvider.getAuthRequest();
2843
+ const _headers = mergeHeaders(
2844
+ _authRequest.headers,
2845
+ this._options?.headers,
2846
+ requestOptions?.headers
2847
+ );
2848
+ const _response = await fetcher({
2849
+ url: url_exports.join(
2850
+ await Supplier.get(this._options.baseUrl) ?? await Supplier.get(this._options.environment),
2851
+ `v1/agents/${url_exports.encodePathParam(id)}/versions`
2852
+ ),
2853
+ method: "GET",
2854
+ headers: _headers,
2855
+ queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
2856
+ timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
2857
+ maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
2858
+ abortSignal: requestOptions?.abortSignal,
2859
+ fetchFn: this._options?.fetch,
2860
+ logging: this._options.logging
2861
+ });
2862
+ if (_response.ok) {
2863
+ return { data: _response.body, rawResponse: _response.rawResponse };
2864
+ }
2865
+ if (_response.error.reason === "status-code") {
2866
+ switch (_response.error.statusCode) {
2867
+ case 403:
2868
+ throw new ForbiddenError(_response.error.body, _response.rawResponse);
2869
+ case 404:
2870
+ throw new NotFoundError(_response.error.body, _response.rawResponse);
2871
+ default:
2872
+ throw new VectrosError({
2873
+ statusCode: _response.error.statusCode,
2874
+ body: _response.error.body,
2875
+ rawResponse: _response.rawResponse
2876
+ });
2877
+ }
2878
+ }
2879
+ return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/agents/{id}/versions");
2880
+ }
2881
+ /**
2882
+ * Stops a run. The answer is `202` with the run still `RUNNING`: read it with `GET /v1/runs/{id}` to see it end. A model call in progress is cut within a few seconds; a tool call in progress is first allowed to finish, for up to about 30 seconds, so its change is recorded and billed, and the run stops as soon as it has. The run then ends `CANCELLED` with `errorCode` `CANCELLED_BY_CALLER`, unless it had already completed, in which case it stays `COMPLETED`. Its reservation is settled once, like any run's: a model call in progress when it stopped is charged up to its maximum. A run that has ended (or that had not started, which this cancel ends) answers `200` with its final state, so a cancel can always be retried. A run can be cancelled by the credential that started it (for a token bound to a user, by that user) and by your root key. Requires the `runs:c` scope; with `runs:r` as well the answer is the whole run, otherwise only its `id`, `status`, `terminal`, `reason` and `errorCode`. Takes no body.
2883
+ *
2884
+ * @param {Vectros.CancelRunRequest} request
2885
+ * @param {AgentsClient.RequestOptions} requestOptions - Request-specific configuration.
2886
+ *
2887
+ * @throws {@link Vectros.BadRequestError}
2888
+ * @throws {@link Vectros.UnauthorizedError}
2889
+ * @throws {@link Vectros.ForbiddenError}
2890
+ * @throws {@link Vectros.NotFoundError}
2891
+ * @throws {@link Vectros.TooManyRequestsError}
2892
+ *
2893
+ * @example
2894
+ * await client.agents.cancelRun({
2895
+ * id: "id"
2896
+ * })
2897
+ */
2898
+ cancelRun(request, requestOptions) {
2899
+ return HttpResponsePromise.fromPromise(this.__cancelRun(request, requestOptions));
2900
+ }
2901
+ async __cancelRun(request, requestOptions) {
2902
+ const { id, contextId } = request;
2903
+ const _queryParams = {
2904
+ contextId
2905
+ };
2906
+ const _authRequest = await this._options.authProvider.getAuthRequest();
2907
+ const _headers = mergeHeaders(
2908
+ _authRequest.headers,
2909
+ this._options?.headers,
2910
+ requestOptions?.headers
2911
+ );
2912
+ const _response = await fetcher({
2913
+ url: url_exports.join(
2914
+ await Supplier.get(this._options.baseUrl) ?? await Supplier.get(this._options.environment),
2915
+ `v1/runs/${url_exports.encodePathParam(id)}/cancel`
2916
+ ),
2917
+ method: "POST",
2918
+ headers: _headers,
2919
+ queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
2920
+ timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
2921
+ maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
2922
+ abortSignal: requestOptions?.abortSignal,
2923
+ fetchFn: this._options?.fetch,
2924
+ logging: this._options.logging
2925
+ });
2926
+ if (_response.ok) {
2927
+ return { data: _response.body, rawResponse: _response.rawResponse };
2928
+ }
2929
+ if (_response.error.reason === "status-code") {
2930
+ switch (_response.error.statusCode) {
2931
+ case 400:
2932
+ throw new BadRequestError(_response.error.body, _response.rawResponse);
2933
+ case 401:
2934
+ throw new UnauthorizedError(_response.error.body, _response.rawResponse);
2935
+ case 403:
2936
+ throw new ForbiddenError(_response.error.body, _response.rawResponse);
2937
+ case 404:
2938
+ throw new NotFoundError(_response.error.body, _response.rawResponse);
2939
+ case 429:
2940
+ throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
2941
+ default:
2942
+ throw new VectrosError({
2943
+ statusCode: _response.error.statusCode,
2944
+ body: _response.error.body,
2945
+ rawResponse: _response.rawResponse
2946
+ });
2947
+ }
2948
+ }
2949
+ return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/runs/{id}/cancel");
2950
+ }
2951
+ /**
2952
+ * Returns the status and metadata of one run: the agent version that ran, how it ended, its tool calls (never their arguments or results) and what it was charged. It never returns text: the answer is delivered only in the run's stream. A run is visible to the credential that started it (for a token bound to a user, to that user) and to your root key, and is kept for 30 days after it ends. Requires the `runs:r` scope.
2953
+ *
2954
+ * @param {Vectros.GetRunRequest} request
2955
+ * @param {AgentsClient.RequestOptions} requestOptions - Request-specific configuration.
2956
+ *
2957
+ * @throws {@link Vectros.BadRequestError}
2958
+ * @throws {@link Vectros.UnauthorizedError}
2959
+ * @throws {@link Vectros.ForbiddenError}
2960
+ * @throws {@link Vectros.NotFoundError}
2961
+ *
2962
+ * @example
2963
+ * await client.agents.getRun({
2964
+ * id: "id"
2965
+ * })
2966
+ */
2967
+ getRun(request, requestOptions) {
2968
+ return HttpResponsePromise.fromPromise(this.__getRun(request, requestOptions));
2969
+ }
2970
+ async __getRun(request, requestOptions) {
2971
+ const { id, contextId } = request;
2972
+ const _queryParams = {
2973
+ contextId
2974
+ };
2975
+ const _authRequest = await this._options.authProvider.getAuthRequest();
2976
+ const _headers = mergeHeaders(
2977
+ _authRequest.headers,
2978
+ this._options?.headers,
2979
+ requestOptions?.headers
2980
+ );
2981
+ const _response = await fetcher({
2982
+ url: url_exports.join(
2983
+ await Supplier.get(this._options.baseUrl) ?? await Supplier.get(this._options.environment),
2984
+ `v1/runs/${url_exports.encodePathParam(id)}`
2985
+ ),
2986
+ method: "GET",
2987
+ headers: _headers,
2988
+ queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
2989
+ timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
2990
+ maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
2991
+ abortSignal: requestOptions?.abortSignal,
2992
+ fetchFn: this._options?.fetch,
2993
+ logging: this._options.logging
2994
+ });
2995
+ if (_response.ok) {
2996
+ return { data: _response.body, rawResponse: _response.rawResponse };
2997
+ }
2998
+ if (_response.error.reason === "status-code") {
2999
+ switch (_response.error.statusCode) {
3000
+ case 400:
3001
+ throw new BadRequestError(_response.error.body, _response.rawResponse);
3002
+ case 401:
3003
+ throw new UnauthorizedError(_response.error.body, _response.rawResponse);
3004
+ case 403:
3005
+ throw new ForbiddenError(_response.error.body, _response.rawResponse);
3006
+ case 404:
3007
+ throw new NotFoundError(_response.error.body, _response.rawResponse);
3008
+ default:
3009
+ throw new VectrosError({
3010
+ statusCode: _response.error.statusCode,
3011
+ body: _response.error.body,
3012
+ rawResponse: _response.rawResponse
3013
+ });
3014
+ }
3015
+ }
3016
+ return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/runs/{id}");
3017
+ }
3018
+ /**
3019
+ * Starts a run of a stored agent and streams what it does as Server-Sent Events. The response is always `text/event-stream`, whatever `Accept` you send; its `X-Vectros-Run-Id` header names the run. The run's tool calls act as you, with your own permissions, and only through the tools the agent lists. On a prepaid account the run reserves its budget from your inference balance when it starts and returns what its model calls did not use when it ends; a fixed fee per run, tool calls and run time are billed separately, against your plan. Read each event's `type`; ignore types and fields you do not know; stop at the event with `terminal: true`. A stream that ends without one: read the run with `GET /v1/runs/{id}`. Closing the stream stops the run only on a best-effort basis. Requires the `runs:c` scope, and agent runs switched on for your tenant by an account owner.
3020
+ */
3021
+ createRun(request, requestOptions) {
3022
+ return HttpResponsePromise.fromPromise(this.__createRun(request, requestOptions));
3023
+ }
3024
+ async __createRun(request, requestOptions) {
3025
+ const { "Idempotency-Key": idempotencyKey, ..._body } = request;
3026
+ const _authRequest = await this._options.authProvider.getAuthRequest();
3027
+ const _headers = mergeHeaders(
3028
+ _authRequest.headers,
3029
+ this._options?.headers,
3030
+ mergeOnlyDefinedHeaders({ "Idempotency-Key": idempotencyKey }),
3031
+ requestOptions?.headers
3032
+ );
3033
+ const _response = await fetcher({
3034
+ url: url_exports.join(
3035
+ await Supplier.get(this._options.baseUrl) ?? await Supplier.get(this._options.environment),
3036
+ "v1/runs"
3037
+ ),
3038
+ method: "POST",
3039
+ headers: _headers,
3040
+ contentType: "application/json",
3041
+ queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
3042
+ requestType: "json",
3043
+ body: _body,
3044
+ responseType: "sse",
3045
+ timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
3046
+ maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
3047
+ abortSignal: requestOptions?.abortSignal,
3048
+ fetchFn: this._options?.fetch,
3049
+ logging: this._options.logging
3050
+ });
3051
+ if (_response.ok) {
3052
+ return {
3053
+ data: new Stream({
3054
+ stream: _response.body,
3055
+ parse: (data) => data,
3056
+ signal: requestOptions?.abortSignal,
3057
+ eventShape: {
3058
+ type: "sse"
3059
+ }
3060
+ }),
3061
+ rawResponse: _response.rawResponse
3062
+ };
3063
+ }
3064
+ if (_response.error.reason === "status-code") {
3065
+ switch (_response.error.statusCode) {
3066
+ case 400:
3067
+ throw new BadRequestError(_response.error.body, _response.rawResponse);
3068
+ case 401:
3069
+ throw new UnauthorizedError(_response.error.body, _response.rawResponse);
3070
+ case 402:
3071
+ throw new PaymentRequiredError(_response.error.body, _response.rawResponse);
3072
+ case 403:
3073
+ throw new ForbiddenError(_response.error.body, _response.rawResponse);
3074
+ case 404:
3075
+ throw new NotFoundError(_response.error.body, _response.rawResponse);
3076
+ case 409:
3077
+ throw new ConflictError(_response.error.body, _response.rawResponse);
3078
+ case 413:
3079
+ throw new ContentTooLargeError(_response.error.body, _response.rawResponse);
3080
+ case 422:
3081
+ throw new UnprocessableEntityError(_response.error.body, _response.rawResponse);
3082
+ case 429:
3083
+ throw new TooManyRequestsError(_response.error.body, _response.rawResponse);
3084
+ case 503:
3085
+ throw new ServiceUnavailableError(_response.error.body, _response.rawResponse);
3086
+ default:
3087
+ throw new VectrosError({
3088
+ statusCode: _response.error.statusCode,
3089
+ body: _response.error.body,
3090
+ rawResponse: _response.rawResponse
3091
+ });
3092
+ }
3093
+ }
3094
+ return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/runs");
2453
3095
  }
2454
- }
3096
+ };
2455
3097
  var AuthClient = class {
2456
3098
  constructor(options) {
2457
3099
  this._options = normalizeClientOptionsWithAuth(options);
@@ -2999,9 +3641,7 @@ var AuthClient = class {
2999
3641
  * @example
3000
3642
  * await client.auth.createAccessProfile({
3001
3643
  * contextId: "contextId",
3002
- * body: {
3003
- * principalId: "usr_6ba7b810-9dad-11d1-80b4-00c04fd430c8"
3004
- * }
3644
+ * body: {}
3005
3645
  * })
3006
3646
  */
3007
3647
  createAccessProfile(request, requestOptions) {
@@ -3137,10 +3777,7 @@ var AuthClient = class {
3137
3777
  *
3138
3778
  * @example
3139
3779
  * await client.auth.createAppContext({
3140
- * body: {
3141
- * contextId: "myapp",
3142
- * name: "My Internal App"
3143
- * }
3780
+ * body: {}
3144
3781
  * })
3145
3782
  */
3146
3783
  createAppContext(request, requestOptions) {
@@ -3283,13 +3920,7 @@ var AuthClient = class {
3283
3920
  * @example
3284
3921
  * await client.auth.createRole({
3285
3922
  * contextId: "contextId",
3286
- * body: {
3287
- * roleId: "engineering-member",
3288
- * name: "Engineering Team Member",
3289
- * scopes: [{
3290
- * allowed_actions: ["records:cru", "search:r"]
3291
- * }]
3292
- * }
3923
+ * body: {}
3293
3924
  * })
3294
3925
  */
3295
3926
  createRole(request, requestOptions) {
@@ -3433,9 +4064,7 @@ var AuthClient = class {
3433
4064
  * await client.auth.updateAccessProfile({
3434
4065
  * contextId: "contextId",
3435
4066
  * principalId: "principalId",
3436
- * body: {
3437
- * principalId: "usr_6ba7b810-9dad-11d1-80b4-00c04fd430c8"
3438
- * }
4067
+ * body: {}
3439
4068
  * })
3440
4069
  */
3441
4070
  updateAccessProfile(request, requestOptions) {
@@ -3638,10 +4267,7 @@ var AuthClient = class {
3638
4267
  * @example
3639
4268
  * await client.auth.updateAppContext({
3640
4269
  * contextId: "contextId",
3641
- * body: {
3642
- * contextId: "myapp",
3643
- * name: "My Internal App"
3644
- * }
4270
+ * body: {}
3645
4271
  * })
3646
4272
  */
3647
4273
  updateAppContext(request, requestOptions) {
@@ -3846,13 +4472,7 @@ var AuthClient = class {
3846
4472
  * await client.auth.updateRole({
3847
4473
  * contextId: "contextId",
3848
4474
  * roleId: "roleId",
3849
- * body: {
3850
- * roleId: "engineering-member",
3851
- * name: "Engineering Team Member",
3852
- * scopes: [{
3853
- * allowed_actions: ["records:cru", "search:r"]
3854
- * }]
3855
- * }
4475
+ * body: {}
3856
4476
  * })
3857
4477
  */
3858
4478
  updateRole(request, requestOptions) {
@@ -5304,7 +5924,7 @@ var DocumentsClient = class {
5304
5924
  this._options = normalizeClientOptionsWithAuth(options);
5305
5925
  }
5306
5926
  /**
5307
- * Returns a paginated list of your documents, optionally filtered by folder (`folderId`) and/or owner (`userId` or `scope`). The response is a `{data, nextCursor}` envelope; pass `nextCursor` back as `startFrom` to fetch the next page. Requires the `documents:r` scope.
5927
+ * Returns a paginated list of your documents, optionally filtered by folder (`folderId`), owner (`userId` or `scope`) and/or type (`type`). The response is a `{data, nextCursor}` envelope; pass `nextCursor` back as `startFrom` to fetch the next page. A page can hold fewer documents than `limit`, or none, while `nextCursor` is still set: keep paging until `nextCursor` is null. Requires the `documents:r` scope.
5308
5928
  *
5309
5929
  * @param {Vectros.ListDocumentsRequest} request
5310
5930
  * @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
@@ -5314,6 +5934,7 @@ var DocumentsClient = class {
5314
5934
  * userId: "550e8400-e29b-41d4-a716-446655440000",
5315
5935
  * scope: "group:eng-team",
5316
5936
  * folderId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
5937
+ * type: "investigation_report",
5317
5938
  * startFrom: "b3BhcXVlLWN1cnNvci1mcm9tLXRoZS1wcmV2aW91cy1wYWdl"
5318
5939
  * })
5319
5940
  */
@@ -5321,11 +5942,12 @@ var DocumentsClient = class {
5321
5942
  return HttpResponsePromise.fromPromise(this.__listDocuments(request, requestOptions));
5322
5943
  }
5323
5944
  async __listDocuments(request = {}, requestOptions) {
5324
- const { userId, scope, folderId, startFrom, limit } = request;
5945
+ const { userId, scope, folderId, type: type_, startFrom, limit } = request;
5325
5946
  const _queryParams = {
5326
5947
  userId,
5327
5948
  scope,
5328
5949
  folderId,
5950
+ type: type_,
5329
5951
  startFrom,
5330
5952
  limit
5331
5953
  };
@@ -5375,9 +5997,7 @@ var DocumentsClient = class {
5375
5997
  *
5376
5998
  * @example
5377
5999
  * await client.documents.ingestDocument({
5378
- * body: {
5379
- * title: "Patient Intake Form \u2014 Jane Doe"
5380
- * }
6000
+ * body: {}
5381
6001
  * })
5382
6002
  */
5383
6003
  ingestDocument(request, requestOptions) {
@@ -5507,9 +6127,7 @@ var DocumentsClient = class {
5507
6127
  * @example
5508
6128
  * await client.documents.updateDocument({
5509
6129
  * id: "id",
5510
- * body: {
5511
- * title: "Patient Intake Form \u2014 Jane Doe"
5512
- * }
6130
+ * body: {}
5513
6131
  * })
5514
6132
  */
5515
6133
  updateDocument(request, requestOptions) {
@@ -5907,11 +6525,12 @@ var DocumentsClient = class {
5907
6525
  return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/documents/{id}/download");
5908
6526
  }
5909
6527
  /**
5910
- * Returns the document's full text body when it is retained: retained for text-ingested documents, and for file-uploaded documents unless they were uploaded with `storeText=false` (which discards the extracted text once indexing completes — the original file remains available via `GET /v1/documents/{id}/download`). Returns 404 when the document does not exist, its text was not retained, or extraction has not yet completed. Requires the `documents:r` scope.
6528
+ * Returns the document's full text body when it is retained: retained for text-ingested documents, and for file-uploaded documents unless they were uploaded with `storeText=false` (which discards the extracted text once indexing completes — the original file remains available via `GET /v1/documents/{id}/download`). Returns 404 when the document does not exist, its text was not retained, extraction has not yet completed, or no text could be extracted from its file (see the document's `extraction`). To read a long text in parts, pass `limit` (and `offset` from the second part on): offsets and lengths count Unicode characters (code points), `totalLength` is the length of the whole text, and `nextOffset` is where the next part starts, absent once the end is reached. Requires the `documents:r` scope.
5911
6529
  *
5912
6530
  * @param {Vectros.GetDocumentTextRequest} request
5913
6531
  * @param {DocumentsClient.RequestOptions} requestOptions - Request-specific configuration.
5914
6532
  *
6533
+ * @throws {@link Vectros.BadRequestError}
5915
6534
  * @throws {@link Vectros.NotFoundError}
5916
6535
  *
5917
6536
  * @example
@@ -5923,7 +6542,11 @@ var DocumentsClient = class {
5923
6542
  return HttpResponsePromise.fromPromise(this.__getDocumentText(request, requestOptions));
5924
6543
  }
5925
6544
  async __getDocumentText(request, requestOptions) {
5926
- const { id } = request;
6545
+ const { id, offset, limit } = request;
6546
+ const _queryParams = {
6547
+ offset,
6548
+ limit
6549
+ };
5927
6550
  const _authRequest = await this._options.authProvider.getAuthRequest();
5928
6551
  const _headers = mergeHeaders(
5929
6552
  _authRequest.headers,
@@ -5937,7 +6560,7 @@ var DocumentsClient = class {
5937
6560
  ),
5938
6561
  method: "GET",
5939
6562
  headers: _headers,
5940
- queryString: url_exports.queryBuilder().mergeAdditional(requestOptions?.queryParams).build(),
6563
+ queryString: url_exports.queryBuilder().addMany(_queryParams).mergeAdditional(requestOptions?.queryParams).build(),
5941
6564
  timeoutMs: (requestOptions?.timeoutInSeconds ?? this._options?.timeoutInSeconds ?? 60) * 1e3,
5942
6565
  maxRetries: requestOptions?.maxRetries ?? this._options?.maxRetries,
5943
6566
  abortSignal: requestOptions?.abortSignal,
@@ -5949,6 +6572,8 @@ var DocumentsClient = class {
5949
6572
  }
5950
6573
  if (_response.error.reason === "status-code") {
5951
6574
  switch (_response.error.statusCode) {
6575
+ case 400:
6576
+ throw new BadRequestError(_response.error.body, _response.rawResponse);
5952
6577
  case 404:
5953
6578
  throw new NotFoundError(_response.error.body, _response.rawResponse);
5954
6579
  default:
@@ -6168,9 +6793,7 @@ var FoldersClient = class {
6168
6793
  *
6169
6794
  * @example
6170
6795
  * await client.folders.createFolder({
6171
- * body: {
6172
- * name: "Patient Records 2024"
6173
- * }
6796
+ * body: {}
6174
6797
  * })
6175
6798
  */
6176
6799
  createFolder(request, requestOptions) {
@@ -6298,9 +6921,7 @@ var FoldersClient = class {
6298
6921
  * @example
6299
6922
  * await client.folders.updateFolder({
6300
6923
  * id: "id",
6301
- * body: {
6302
- * name: "Patient Records 2024"
6303
- * }
6924
+ * body: {}
6304
6925
  * })
6305
6926
  */
6306
6927
  updateFolder(request, requestOptions) {
@@ -6433,9 +7054,7 @@ var FoldersClient = class {
6433
7054
  * @example
6434
7055
  * await client.folders.patchFolder({
6435
7056
  * id: "id",
6436
- * body: {
6437
- * name: "Patient Records 2024"
6438
- * }
7057
+ * body: {}
6439
7058
  * })
6440
7059
  */
6441
7060
  patchFolder(request, requestOptions) {
@@ -6650,9 +7269,7 @@ var IdentityClient = class {
6650
7269
  * await client.identity.createEntity({
6651
7270
  * namespace: "team",
6652
7271
  * contextId: "myapp",
6653
- * body: {
6654
- * externalId: "team_eng_platform"
6655
- * }
7272
+ * body: {}
6656
7273
  * })
6657
7274
  */
6658
7275
  createEntity(request, requestOptions) {
@@ -6788,9 +7405,7 @@ var IdentityClient = class {
6788
7405
  * namespace: "team",
6789
7406
  * id: "id",
6790
7407
  * contextId: "myapp",
6791
- * body: {
6792
- * externalId: "team_eng_platform"
6793
- * }
7408
+ * body: {}
6794
7409
  * })
6795
7410
  */
6796
7411
  updateEntity(request, requestOptions) {
@@ -7130,10 +7745,7 @@ var IdentityClient = class {
7130
7745
  * @example
7131
7746
  * await client.identity.updateNamespace({
7132
7747
  * namespace: "team",
7133
- * body: {
7134
- * namespace: "team",
7135
- * specificityRank: 1500
7136
- * }
7748
+ * body: {}
7137
7749
  * })
7138
7750
  */
7139
7751
  updateNamespace(request, requestOptions) {
@@ -7320,10 +7932,7 @@ var IdentityClient = class {
7320
7932
  *
7321
7933
  * @example
7322
7934
  * await client.identity.registerNamespace({
7323
- * body: {
7324
- * namespace: "team",
7325
- * specificityRank: 1500
7326
- * }
7935
+ * body: {}
7327
7936
  * })
7328
7937
  */
7329
7938
  registerNamespace(request, requestOptions) {
@@ -7456,9 +8065,7 @@ var IdentityClient = class {
7456
8065
  *
7457
8066
  * @example
7458
8067
  * await client.identity.createUser({
7459
- * body: {
7460
- * externalId: "usr_12345"
7461
- * }
8068
+ * body: {}
7462
8069
  * })
7463
8070
  */
7464
8071
  createUser(request, requestOptions) {
@@ -7577,6 +8184,7 @@ var IdentityClient = class {
7577
8184
  * @param {IdentityClient.RequestOptions} requestOptions - Request-specific configuration.
7578
8185
  *
7579
8186
  * @throws {@link Vectros.BadRequestError}
8187
+ * @throws {@link Vectros.ForbiddenError}
7580
8188
  * @throws {@link Vectros.NotFoundError}
7581
8189
  * @throws {@link Vectros.ConflictError}
7582
8190
  * @throws {@link Vectros.TooManyRequestsError}
@@ -7584,9 +8192,7 @@ var IdentityClient = class {
7584
8192
  * @example
7585
8193
  * await client.identity.updateUser({
7586
8194
  * id: "550e8400-e29b-41d4-a716-446655440000",
7587
- * body: {
7588
- * externalId: "usr_12345"
7589
- * }
8195
+ * body: {}
7590
8196
  * })
7591
8197
  */
7592
8198
  updateUser(request, requestOptions) {
@@ -7624,6 +8230,8 @@ var IdentityClient = class {
7624
8230
  switch (_response.error.statusCode) {
7625
8231
  case 400:
7626
8232
  throw new BadRequestError(_response.error.body, _response.rawResponse);
8233
+ case 403:
8234
+ throw new ForbiddenError(_response.error.body, _response.rawResponse);
7627
8235
  case 404:
7628
8236
  throw new NotFoundError(_response.error.body, _response.rawResponse);
7629
8237
  case 409:
@@ -7646,6 +8254,7 @@ var IdentityClient = class {
7646
8254
  * @param {Vectros.DeleteUserRequest} request
7647
8255
  * @param {IdentityClient.RequestOptions} requestOptions - Request-specific configuration.
7648
8256
  *
8257
+ * @throws {@link Vectros.ForbiddenError}
7649
8258
  * @throws {@link Vectros.NotFoundError}
7650
8259
  * @throws {@link Vectros.ConflictError}
7651
8260
  * @throws {@link Vectros.TooManyRequestsError}
@@ -7685,6 +8294,8 @@ var IdentityClient = class {
7685
8294
  }
7686
8295
  if (_response.error.reason === "status-code") {
7687
8296
  switch (_response.error.statusCode) {
8297
+ case 403:
8298
+ throw new ForbiddenError(_response.error.body, _response.rawResponse);
7688
8299
  case 404:
7689
8300
  throw new NotFoundError(_response.error.body, _response.rawResponse);
7690
8301
  case 409:
@@ -7889,7 +8500,7 @@ var InferenceClient = class {
7889
8500
  this._options = normalizeClientOptionsWithAuth(options);
7890
8501
  }
7891
8502
  /**
7892
- * Returns the whole platform model catalog, whatever your plan: `availableOn` names each model's plan tiers. Each entry also carries the context window and the credit rates per 1K input and output tokens: `regionPricing.base` for global-region requests, `regionPricing.us` for default requests, and `longContext` for prompts over its threshold. A model reached through a `providerAlias` is not listed. Use this to populate model pickers and to validate a request before calling `/v1/chat`, `/v1/rag`, or `/v1/documents/{id}/ask`.
8503
+ * Returns the whole platform model catalog, whatever your plan: `availableOn` names each model's plan tiers. Each entry also carries the context window and the credit rates per 1K input and output tokens: `regionPricing.base` for global-region requests, `regionPricing.us` for default requests, `longContext` for prompts over its threshold, and `cachePricing` for prompt-cache tokens on a model that has cache prices. A model reached through a `providerAlias` is not listed. Use this to populate model pickers and to validate a request before calling `/v1/chat`, `/v1/rag`, or `/v1/documents/{id}/ask`.
7893
8504
  *
7894
8505
  * @param {InferenceClient.RequestOptions} requestOptions - Request-specific configuration.
7895
8506
  *
@@ -7933,7 +8544,7 @@ var InferenceClient = class {
7933
8544
  return handleNonStatusCodeError(_response.error, _response.rawResponse, "GET", "/v1/models");
7934
8545
  }
7935
8546
  /**
7936
- * Streams a model response as Server-Sent Events (SSE). Send the full conversation history in the `messages` array; a message with role `system` is extracted and used as the system prompt. On a pre-paid account, token cost is debited from your inference balance; usage-billed accounts accrue it for post-billing. A request routed through your own `providerAlias` incurs no Vectros token cost. A small per-call flat fee is charged to your monthly credit allowance, except when the call fails on the server side. Requires the `inference:r` scope.
8547
+ * Streams a model response as Server-Sent Events (SSE). Send the full conversation history in the `messages` array; a message with role `system` is extracted and used as the system prompt. On a pre-paid account, token cost is debited from your inference balance; usage-billed accounts accrue it for post-billing. A request routed through your own `providerAlias` incurs no Vectros token cost. A per-call platform fee is charged to your monthly credit allowance, except when the call fails on the server side: 0.2 credits for a call of up to 60 seconds, then 1 credit per 5 minutes of the call's duration. Requires the `inference:r` scope.
7937
8548
  */
7938
8549
  chatInference(request, requestOptions) {
7939
8550
  return HttpResponsePromise.fromPromise(this.__chatInference(request, requestOptions));
@@ -8005,7 +8616,7 @@ var InferenceClient = class {
8005
8616
  return handleNonStatusCodeError(_response.error, _response.rawResponse, "POST", "/v1/chat");
8006
8617
  }
8007
8618
  /**
8008
- * Loads a single document's extracted text, supplies it as context, and streams a model answer about it. The document must be fully indexed. If the document exceeds the 32K-token cap, the call returns 413 with no credits charged — use `POST /v1/rag` instead to answer over larger or multi-document collections. Requires the `inference:r` scope.
8619
+ * Loads a single document's extracted text, supplies it as context, and streams a model answer about it. The document must have finished processing and have text. If the document exceeds the 32K-token cap, the call returns 413 with no credits charged — use `POST /v1/rag` instead to answer over larger or multi-document collections. Requires the `inference:r` scope.
8009
8620
  */
8010
8621
  documentAsk(request, requestOptions) {
8011
8622
  return HttpResponsePromise.fromPromise(this.__documentAsk(request, requestOptions));
@@ -9140,8 +9751,7 @@ var SchemasClient = class {
9140
9751
  * }],
9141
9752
  * capabilities: {
9142
9753
  * "auditHistory": true
9143
- * },
9144
- * allowedSurfaces: ["record"]
9754
+ * }
9145
9755
  * }
9146
9756
  * })
9147
9757
  */
@@ -9271,11 +9881,7 @@ var SchemasClient = class {
9271
9881
  * @example
9272
9882
  * await client.schemas.updateSchema({
9273
9883
  * id: "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
9274
- * body: {
9275
- * typeName: "intake_form",
9276
- * displayName: "Client Intake Form",
9277
- * allowedSurfaces: ["record"]
9278
- * }
9884
+ * body: {}
9279
9885
  * })
9280
9886
  */
9281
9887
  updateSchema(request, requestOptions) {
@@ -9815,7 +10421,7 @@ var ScriptsClient = class {
9815
10421
  return handleNonStatusCodeError(_response.error, _response.rawResponse, "PUT", "/v1/scripts/{id}");
9816
10422
  }
9817
10423
  /**
9818
- * Permanently deletes one script version by ID. Deleting one version does not affect any other version of the same name. Requires the `scripts:d` scope. Refused with 409 if a live trigger rule still references this version — either directly (a pinned `scriptRef`), or via `"latest"` when this IS the newest version of its name. Delete or re-point those rules first.
10424
+ * Permanently deletes one script version by ID. Deleting one version does not affect any other version of the same name. Requires the `scripts:d` scope. Refused with 409 if a live trigger rule still references this version — either directly (a pinned `scriptRef`), or via `"latest"` when this IS the newest version of its name. Delete or re-point those rules first. Also refused with a retryable 409, deleting nothing, when it cannot be told yet whether this is the newest version (for example just after several of this name's newest versions were deleted, or while its first version is gone and the listing has not caught up) and a `"latest"` rule depends on the script: try again shortly.
9819
10425
  *
9820
10426
  * @param {Vectros.DeleteScriptRequest} request
9821
10427
  * @param {ScriptsClient.RequestOptions} requestOptions - Request-specific configuration.
@@ -10079,17 +10685,7 @@ var TriggersClient = class {
10079
10685
  *
10080
10686
  * @example
10081
10687
  * await client.triggers.createTrigger({
10082
- * body: {
10083
- * name: "on-intake-create",
10084
- * firingSource: {
10085
- * schemaId: "sch_9f8e7d6c",
10086
- * event: "CREATE"
10087
- * },
10088
- * scriptRef: {
10089
- * name: "notify-assignee",
10090
- * version: "3"
10091
- * }
10092
- * }
10688
+ * body: {}
10093
10689
  * })
10094
10690
  */
10095
10691
  createTrigger(request, requestOptions) {
@@ -10218,17 +10814,7 @@ var TriggersClient = class {
10218
10814
  * @example
10219
10815
  * await client.triggers.updateTrigger({
10220
10816
  * id: "id",
10221
- * body: {
10222
- * name: "on-intake-create",
10223
- * firingSource: {
10224
- * schemaId: "sch_9f8e7d6c",
10225
- * event: "CREATE"
10226
- * },
10227
- * scriptRef: {
10228
- * name: "notify-assignee",
10229
- * version: "3"
10230
- * }
10231
- * }
10817
+ * body: {}
10232
10818
  * })
10233
10819
  */
10234
10820
  updateTrigger(request, requestOptions) {
@@ -10353,6 +10939,9 @@ var VectrosClient = class {
10353
10939
  get auth() {
10354
10940
  return this._auth ?? (this._auth = new AuthClient(this._options));
10355
10941
  }
10942
+ get agents() {
10943
+ return this._agents ?? (this._agents = new AgentsClient(this._options));
10944
+ }
10356
10945
  get documents() {
10357
10946
  return this._documents ?? (this._documents = new DocumentsClient(this._options));
10358
10947
  }
@@ -10418,7 +11007,7 @@ var logging;
10418
11007
 
10419
11008
  // src/server.ts
10420
11009
  var import_types3 = require("@modelcontextprotocol/sdk/types.js");
10421
- var import_zod25 = require("zod");
11010
+ var import_zod28 = require("zod");
10422
11011
 
10423
11012
  // src/auth.ts
10424
11013
  var KEY_RE = /^(sk|ssk|st)_(live|test)_/;
@@ -10462,8 +11051,8 @@ function warnOnSuboptimalKey(key, log) {
10462
11051
 
10463
11052
  // src/build-info.ts
10464
11053
  var BUILD_INFO = {
10465
- mcpServer: true ? "0.18.2" : "dev",
10466
- sdk: true ? "0.48.0" : "dev"
11054
+ mcpServer: true ? "0.19.0" : "dev",
11055
+ sdk: true ? "0.49.0" : "dev"
10467
11056
  };
10468
11057
 
10469
11058
  // src/log.ts
@@ -10719,6 +11308,11 @@ ${detail}${trailer}`
10719
11308
  };
10720
11309
  }
10721
11310
 
11311
+ // src/tools/limits.ts
11312
+ var MAX_OWNERSHIP_SCOPES = 2;
11313
+ var MAX_SCOPE_FILTERS = 16;
11314
+ var MAX_CONTENT_TYPES = 2;
11315
+
10722
11316
  // src/tools/hybrid_search.ts
10723
11317
  var MCP_DEFAULT_LIMIT = 3;
10724
11318
  var MCP_MAX_LIMIT = 50;
@@ -10761,11 +11355,11 @@ var inputSchema = {
10761
11355
  folderId: import_zod.z.string().optional().describe("Scope to content in this EXACT folder (Vectros folder id)."),
10762
11356
  rootFolderId: import_zod.z.string().optional().describe("Scope to content under this folder AND all its descendants (subtree-root folder id)."),
10763
11357
  // Content narrowing.
10764
- contentTypes: import_zod.z.array(import_zod.z.enum(["documents", "records"])).optional().describe('Narrow to content types. ["documents"] or ["records"]; omit for unified (both).'),
11358
+ contentTypes: import_zod.z.array(import_zod.z.enum(["documents", "records"])).max(MAX_CONTENT_TYPES, `contentTypes accepts at most ${MAX_CONTENT_TYPES} entries`).optional().describe('Narrow to content types. ["documents"] or ["records"]; omit for unified (both).'),
10765
11359
  typeName: import_zod.z.string().optional().describe(
10766
11360
  'Restrict hits to this schema type (e.g. "patient", "runbook"). Applies to BOTH documents and records \u2014 combine with contentTypes to scope within one content type. Untyped content never matches.'
10767
11361
  ),
10768
- filters: import_zod.z.record(import_zod.z.unknown()).optional().describe(
11362
+ filters: import_zod.z.record(import_zod.z.string(), import_zod.z.unknown()).optional().describe(
10769
11363
  'Field-level metadata filters (AND-combined). Value = scalar (equality), array (OR-set), or operator map: $eq/$ne/$gt/$gte/$lt/$lte (scalar) or $in/$nin (array). e.g. {"status":"open"} or {"price":{"$gte":100,"$lte":500}}.'
10770
11364
  ),
10771
11365
  // Ownership.
@@ -10773,7 +11367,7 @@ var inputSchema = {
10773
11367
  scope: import_zod.z.string().optional().describe(
10774
11368
  'Restrict to content carrying this scope value, in `namespace:value` form (e.g. "org:<uuid>", "client:<uuid>", "group:eng-team"). `org` and `client` are reserved namespace names, registered like any other; others are namespaces you registered yourself. Mutually exclusive with `scopeFilters` \u2014 use this for ONE dimension.'
10775
11369
  ),
10776
- scopeFilters: import_zod.z.array(import_zod.z.string()).min(1, "scopeFilters must name at least one dimension when supplied").optional().describe(
11370
+ scopeFilters: import_zod.z.array(import_zod.z.string()).min(1, "scopeFilters must name at least one dimension when supplied").max(MAX_SCOPE_FILTERS, `scopeFilters accepts at most ${MAX_SCOPE_FILTERS} entries`).optional().describe(
10777
11371
  'Restrict to content matching ALL of these scope values \u2014 one per namespace \u2014 for a credential whose access spans more than one ownership dimension. e.g. ["org:<uuid>", "client:<uuid>"] narrows to one specific client WITHIN one specific org, which `scope` alone cannot express. Same `namespace:value` form as `scope`; naming the same namespace twice is rejected. Mutually exclusive with `scope` \u2014 use `scope` for a single dimension.'
10778
11372
  ),
10779
11373
  // Date window.
@@ -11294,7 +11888,7 @@ var inputSchema5 = {
11294
11888
  status: import_zod5.z.string().optional().describe("Record lifecycle status. Defaults to ACTIVE server-side."),
11295
11889
  folderId: import_zod5.z.string().optional().describe("Group this record into a folder."),
11296
11890
  userId: import_zod5.z.string().optional().describe("Owning user id."),
11297
- scopes: import_zod5.z.array(import_zod5.z.string()).optional().describe(
11891
+ scopes: import_zod5.z.array(import_zod5.z.string()).max(MAX_OWNERSHIP_SCOPES, `scopes accepts at most ${MAX_OWNERSHIP_SCOPES} entries`).optional().describe(
11298
11892
  'Scope ownership as `namespace:value` entries, at most 2 (e.g. ["org:<uuid>", "group:eng-team"]). `org` and `client` are reserved namespace names, registered like any other; others are namespaces you registered yourself. This is the record\'s COMPLETE scope declaration, and each entry must fall inside the `data_scope` of a single clause of your credential that also grants this write \u2014 your identity supplies the DEFAULT when you state none, it does not limit which value you may state. An empty array `[]` creates a PRIVATE record owned by the calling user alone. Omit to stamp the credential\'s full identity \u2014 the default.'
11299
11893
  )
11300
11894
  };
@@ -11352,7 +11946,7 @@ var itemSchema = import_zod6.z.strictObject({
11352
11946
  status: import_zod6.z.string().optional().describe("Record lifecycle status. Defaults to ACTIVE server-side."),
11353
11947
  folderId: import_zod6.z.string().optional().describe("Group this record into a folder."),
11354
11948
  userId: import_zod6.z.string().optional().describe("Owning user id."),
11355
- scopes: import_zod6.z.array(import_zod6.z.string()).optional().describe(
11949
+ scopes: import_zod6.z.array(import_zod6.z.string()).max(MAX_OWNERSHIP_SCOPES, `scopes accepts at most ${MAX_OWNERSHIP_SCOPES} entries`).optional().describe(
11356
11950
  "Scope ownership as `namespace:value` entries, at most 2 namespaces. When supplied this is the record's COMPLETE scope declaration, and each entry must fall inside the `data_scope` of a single clause of your credential that also grants this write \u2014 your identity supplies the DEFAULT when you state none, it does not limit which value you may state. `[]` creates a PRIVATE record owned by the calling user alone. Omit to inherit the credential's full identity."
11357
11951
  )
11358
11952
  });
@@ -11509,7 +12103,7 @@ var import_zod10 = require("zod");
11509
12103
  // src/tools/provider-alias.ts
11510
12104
  var import_zod9 = require("zod");
11511
12105
  var providerAliasInput = import_zod9.z.string().min(1, "providerAlias must be non-empty when supplied").optional().describe(
11512
- "Route THIS call through your own model provider instead of the platform-hosted default, by the alias of a provider config your account has activated. It works only after your account has set up that provider and signed the platform's risk-acceptance waiver; without both, the platform refuses the call with a 403, which is returned to you as written. When the waiver is the cause, the 403 carries an `errorCode` that says which case it is: `WAIVER_NOT_SIGNED` (the waiver has not been signed), `WAIVER_TEXT_CHANGED` (it was signed, but its text changed afterwards, so the earlier signature no longer counts), or `WAIVER_REVOKED` (an owner revoked it). An account owner resolves each in the developer portal (Model Providers); retrying the call does not help. If your account has accepted the Business Associate Agreement with Vectros in the developer portal, an account owner must also have attested that provider config in the developer portal (Model Providers); until then the platform refuses the call with a 403 that says it needs a BAA-holder attestation. A call served by your own provider leaves the Vectros perimeter. Omit it (the default) to use the platform-hosted path. When set, `model` names the model on your provider's own id space, and falls back to that provider config's default model when omitted."
12106
+ "Route THIS call through your own model provider instead of the platform-hosted default, by the alias of a provider config your account has activated. It works only after your account has set up that provider and signed the platform's risk-acceptance waiver; without both, the platform refuses the call with a 403, which is returned to you as written. When the waiver is the cause, the 403 carries an `errorCode` that says which case it is: `WAIVER_NOT_SIGNED` (the waiver has not been signed), `WAIVER_TEXT_CHANGED` (Vectros now requires a newer version of the waiver than the one in force for your account), or `WAIVER_REVOKED` (an owner revoked it). An account owner resolves each in the developer portal (Model Providers); retrying the call does not help. If your account has accepted the Business Associate Agreement with Vectros in the developer portal, an account owner must also have attested that provider config in the developer portal (Model Providers); until then the platform refuses the call with a 403 that says it needs a BAA-holder attestation. A call served by your own provider leaves the Vectros perimeter. Omit it (the default) to use the platform-hosted path. When set, `model` names the model on your provider's own id space, and falls back to that provider config's default model when omitted."
11513
12107
  );
11514
12108
 
11515
12109
  // src/tools/rag_ask.ts
@@ -11536,14 +12130,14 @@ var inputSchema9 = {
11536
12130
  scope: import_zod10.z.string().optional().describe(
11537
12131
  'Restrict retrieval to content carrying this scope value, in `namespace:value` form (e.g. "org:<uuid>", "client:<uuid>", "group:eng-team"). `org` and `client` are reserved namespace names, registered like any other; others are namespaces you registered yourself. Mutually exclusive with `scopeFilters` \u2014 use this for ONE dimension.'
11538
12132
  ),
11539
- scopeFilters: import_zod10.z.array(import_zod10.z.string()).min(1, "scopeFilters must name at least one dimension when supplied").optional().describe(
12133
+ scopeFilters: import_zod10.z.array(import_zod10.z.string()).min(1, "scopeFilters must name at least one dimension when supplied").max(MAX_SCOPE_FILTERS, `scopeFilters accepts at most ${MAX_SCOPE_FILTERS} entries`).optional().describe(
11540
12134
  'Restrict retrieval to content matching ALL of these scope values \u2014 one per namespace \u2014 for a credential whose access spans more than one ownership dimension. e.g. ["org:<uuid>", "client:<uuid>"] grounds the answer on one specific client WITHIN one specific org, which `scope` alone cannot express. Naming the same namespace twice is rejected. Mutually exclusive with `scope`.'
11541
12135
  ),
11542
12136
  folderId: import_zod10.z.string().optional().describe("Restrict retrieval to this exact folder."),
11543
12137
  rootFolderId: import_zod10.z.string().optional().describe("Restrict retrieval to this folder and all its descendants."),
11544
12138
  typeName: import_zod10.z.string().optional().describe("Restrict record retrieval to this schema type."),
11545
- contentTypes: import_zod10.z.array(import_zod10.z.enum(["documents", "records"])).optional().describe('Narrow retrieval to content types. ["documents"] or ["records"]; omit for both.'),
11546
- filters: import_zod10.z.record(import_zod10.z.unknown()).optional().describe(
12139
+ contentTypes: import_zod10.z.array(import_zod10.z.enum(["documents", "records"])).max(MAX_CONTENT_TYPES, `contentTypes accepts at most ${MAX_CONTENT_TYPES} entries`).optional().describe('Narrow retrieval to content types. ["documents"] or ["records"]; omit for both.'),
12140
+ filters: import_zod10.z.record(import_zod10.z.string(), import_zod10.z.unknown()).optional().describe(
11547
12141
  'Field-level metadata filters (AND-combined). Value = scalar (equality), array (OR-set), or operator map ($eq/$ne/$gt/$gte/$lt/$lte, $in/$nin). e.g. {"status":"open"}.'
11548
12142
  ),
11549
12143
  createdAfter: import_zod10.z.string().optional().describe(
@@ -11818,7 +12412,7 @@ function isNotAvailable(err) {
11818
12412
  var documentGet = ({ client, log }) => ({
11819
12413
  name: "document_get",
11820
12414
  title: "Get document metadata (and optionally text)",
11821
- description: "Fetch metadata for a single document \u2014 select it by its Vectros `documentId` OR by your own `externalId` + `type` (resolved for you). Pass `includeText: true` to also fetch the text body (capped at ~8K tokens; `truncated: true` flag if cut). Text is available for every text-ingested document and for file-ingested documents unless they were uploaded with `storeText: false` (extracted text discarded after indexing) \u2014 then `textAvailable: false` is set; the original file is still retrievable via `includeDownloadUrl`. Pass `includeDownloadUrl: true` to also get a short-lived presigned `downloadUrl` for the original file (file-backed documents only). Returns the full document metadata, including its lifecycle `status` (ACTIVE, or ARCHIVED when soft-retracted from search \u2014 restorable via document_update) and its processing `indexStatus`. When `indexStatus` is FAILED the response also carries `indexFailure` with a machine-readable `code` telling you whether the content is still findable \u2014 e.g. TEXT_INDEX_FAILED / VECTOR_LIMIT_EXCEEDED (one leg still serves it) vs INDEXING_FAILED (not findable by search at all). Branch on `code`, not on the message.",
12415
+ description: "Fetch metadata for a single document \u2014 select it by its Vectros `documentId` OR by your own `externalId` + `type` (resolved for you). Pass `includeText: true` to also fetch the text body (capped at ~8K tokens; `truncated: true` flag if cut). Text is available for every text-ingested document and for file-ingested documents unless they were uploaded with `storeText: false` (extracted text discarded after indexing) or no text could be extracted from the file (`extraction.outcome` other than OK/TRUNCATED, e.g. an image or a scanned page) \u2014 then `textAvailable: false` is set; the original file is still retrievable via `includeDownloadUrl`. Pass `includeDownloadUrl: true` to also get a short-lived presigned `downloadUrl` for the original file (file-backed documents only). Returns the full document metadata, including its lifecycle `status` (ACTIVE, or ARCHIVED when soft-retracted from search \u2014 restorable via document_update) and its processing `indexStatus`. When `indexStatus` is FAILED the response also carries `indexFailure` with a machine-readable `code` telling you whether the content is still findable \u2014 e.g. TEXT_INDEX_FAILED / VECTOR_LIMIT_EXCEEDED (one leg still serves it) vs INDEXING_FAILED (not findable by search at all). Branch on `code`, not on the message.",
11822
12416
  inputSchema: inputSchema12,
11823
12417
  // A pure read — never creates, modifies, or deletes anything server-side.
11824
12418
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
@@ -11844,7 +12438,7 @@ var documentGet = ({ client, log }) => ({
11844
12438
  if (isNotAvailable(textErr)) {
11845
12439
  log.debug(
11846
12440
  { tool: "document_get", id },
11847
- "document_get text unavailable (not retained \u2014 storeText:false upload)"
12441
+ "document_get text unavailable (not retained, or no text could be extracted from the file)"
11848
12442
  );
11849
12443
  extra = { ...extra, textAvailable: false };
11850
12444
  } else {
@@ -11897,10 +12491,12 @@ var inputSchema13 = {
11897
12491
  to: import_zod14.z.string().optional().describe("Lookup mode (range): inclusive upper bound; requires `from`."),
11898
12492
  prefix: import_zod14.z.string().optional().describe("Lookup mode (prefix): match values starting with this. Range-enabled string fields only."),
11899
12493
  order: import_zod14.z.enum(["asc", "desc"]).optional().describe(
11900
- "Lookup mode: sort direction by the looked-up field. `asc` (default) or `desc` \u2014 use `desc` with a range/prefix lookup to get the most-recent / highest values first (e.g. latest-N). Ignored in list mode."
12494
+ "Lookup mode: sort direction by the looked-up field. `asc` (default) or `desc` \u2014 use `desc` with a range/prefix lookup to get the most-recent / highest values first (e.g. latest-N). Refused without `field`."
11901
12495
  ),
11902
- type: import_zod14.z.string().optional().describe("Lookup mode: the document type (the bound schema's type). Required with `field`."),
11903
- // List-mode args:
12496
+ type: import_zod14.z.string().min(1, "type must be non-empty: omit it to list every type").optional().describe(
12497
+ "The document type (the bound schema's type). Required with `field` (lookup mode); in list mode it narrows the list to documents of this type. The list filter applies page by page: a page can hold fewer documents than `limit`, or none, while `nextCursor` is set, so keep paging until `nextCursor` is null before concluding there are none. For a large collection, hybrid_search with `typeName` finds documents of one type directly."
12498
+ ),
12499
+ // List-mode args (`type` above applies to both modes):
11904
12500
  folderId: import_zod14.z.string().optional().describe("List mode: only documents in this folder (Vectros folder id)."),
11905
12501
  userId: import_zod14.z.string().optional().describe("List mode: scope to documents owned by this user."),
11906
12502
  scope: import_zod14.z.string().optional().describe(
@@ -11914,7 +12510,7 @@ var inputSchema13 = {
11914
12510
  var documentQuery = ({ client, log }) => ({
11915
12511
  name: "document_query",
11916
12512
  title: "Document query (list or lookup)",
11917
- description: "Query document metadata. Two modes:\n \u2022 Lookup on a lookup-indexed field: pass `type` and `field` plus exactly one of `value` (exact), `from`+`to` (range), or `prefix`. Works on sensitive fields (equality only there); range/prefix need a range-enabled field.\n \u2022 List: omit `field`; optionally filter by `folderId`/`userId`/`scope` (`namespace:value`).\nReturns a `{ data, nextCursor }` page: `data` holds up to `limit` documents (default 3, max 100 \u2014 raise it in one call when you can accept the payload), and a non-null `nextCursor` means more remain \u2014 pass it back as `startFrom` to page. Mode is auto-detected. Use document_get for the full text/download URL of one document.",
12513
+ description: "Query document metadata. Two modes:\n \u2022 Lookup on a lookup-indexed field: pass `type` and `field` plus exactly one of `value` (exact), `from`+`to` (range), or `prefix`. Works on sensitive fields (equality only there); range/prefix need a range-enabled field.\n \u2022 List: omit `field`; optionally filter by `type`/`folderId`/`userId`/`scope` (`namespace:value`). The `type` filter is applied page by page, so a page can be short or empty while `nextCursor` is set: keep paging before concluding there are none.\nReturns a `{ data, nextCursor }` page: `data` holds up to `limit` documents (default 3, max 100 \u2014 raise it in one call when you can accept the payload), and a non-null `nextCursor` means more remain \u2014 pass it back as `startFrom` to page. Mode is auto-detected. Use document_get for the full text/download URL of one document.",
11918
12514
  inputSchema: inputSchema13,
11919
12515
  // A pure read — never creates, modifies, or deletes anything server-side.
11920
12516
  annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
@@ -11973,7 +12569,19 @@ var documentQuery = ({ client, log }) => ({
11973
12569
  "document_query lookup ok"
11974
12570
  );
11975
12571
  } else {
12572
+ const strayLookupArgs = ["value", "from", "to", "prefix", "order"].filter(
12573
+ (k) => args[k] !== void 0
12574
+ );
12575
+ if (strayLookupArgs.length > 0) {
12576
+ return toolError(
12577
+ "document_query",
12578
+ new Error(
12579
+ `Lookup arguments need 'field' (and 'type'): ${strayLookupArgs.map((k) => `'${k}'`).join(", ")}. Without 'field' the call lists documents: filter the list with 'type', 'folderId', 'userId' or 'scope'.`
12580
+ )
12581
+ );
12582
+ }
11976
12583
  page = await client.documents.listDocuments({
12584
+ type,
11977
12585
  folderId: args.folderId,
11978
12586
  userId: args.userId,
11979
12587
  scope: args.scope,
@@ -11981,7 +12589,7 @@ var documentQuery = ({ client, log }) => ({
11981
12589
  limit
11982
12590
  });
11983
12591
  log.debug(
11984
- { tool: "document_query", mode: "list", limit, returned: pageItems(page).length },
12592
+ { tool: "document_query", mode: "list", type, limit, returned: pageItems(page).length },
11985
12593
  "document_query list ok"
11986
12594
  );
11987
12595
  }
@@ -12016,7 +12624,7 @@ var inputSchema14 = {
12016
12624
  "Lifecycle status. `ARCHIVED` soft-retracts the document \u2014 it is pulled from search and recall but kept and recoverable; set it back to `ACTIVE` to re-index and restore it. The way to retire superseded content when the credential has no delete authority. Omit to leave unchanged."
12017
12625
  ),
12018
12626
  userId: import_zod15.z.string().optional().describe("Reassign the owning user (Vectros UUID)."),
12019
- scopes: import_zod15.z.array(import_zod15.z.string()).optional().describe(
12627
+ scopes: import_zod15.z.array(import_zod15.z.string()).max(MAX_OWNERSHIP_SCOPES, `scopes accepts at most ${MAX_OWNERSHIP_SCOPES} entries`).optional().describe(
12020
12628
  'Reassign scope ownership as `namespace:value` entries, at most 2 (e.g. ["org:<uuid>", "group:eng-team"]) \u2014 the COMPLETE new parent set. `org`/`client` are reserved namespace names, registered like any other. Omit to leave ownership unchanged; `[]` clears it (private to the owning user).'
12021
12629
  ),
12022
12630
  expectedVersion: import_zod15.z.number().int().optional().describe(
@@ -12160,7 +12768,7 @@ var inputSchema17 = {
12160
12768
  "Optional stable slug (sibling-unique; idempotency key). Lowercase letters/digits/hyphens. SUPPLY THIS to make a retry safe \u2014 if omitted, a derived slug is only ever a freshly-proved-free one, so a redelivered call creates a new sibling folder every time rather than matching the first."
12161
12769
  ),
12162
12770
  userId: import_zod18.z.string().optional().describe("Owning user (Vectros UUID)."),
12163
- scopes: import_zod18.z.array(import_zod18.z.string()).optional().describe(
12771
+ scopes: import_zod18.z.array(import_zod18.z.string()).max(MAX_OWNERSHIP_SCOPES, `scopes accepts at most ${MAX_OWNERSHIP_SCOPES} entries`).optional().describe(
12164
12772
  'Scope ownership as `namespace:value` entries, at most 2 (e.g. ["org:<uuid>", "group:eng-team"]). `org` and `client` are reserved namespace names, registered like any other; others are namespaces you registered yourself. This is the folder\'s COMPLETE scope declaration and values must come from the credential\'s own identity. An empty array `[]` creates a PRIVATE folder owned by the calling user alone. Omit to stamp the credential\'s full identity \u2014 the default.'
12165
12773
  )
12166
12774
  };
@@ -12206,7 +12814,7 @@ var inputSchema18 = {
12206
12814
  name: import_zod19.z.string().optional().describe("New folder name. Omit to keep the current name."),
12207
12815
  description: import_zod19.z.string().optional().describe("New description. Omit to leave unchanged."),
12208
12816
  userId: import_zod19.z.string().optional().describe("Reassign the owning user (Vectros UUID)."),
12209
- scopes: import_zod19.z.array(import_zod19.z.string()).optional().describe(
12817
+ scopes: import_zod19.z.array(import_zod19.z.string()).max(MAX_OWNERSHIP_SCOPES, `scopes accepts at most ${MAX_OWNERSHIP_SCOPES} entries`).optional().describe(
12210
12818
  'Reassign scope ownership as `namespace:value` entries, at most 2 (e.g. ["org:<uuid>", "group:eng-team"]) \u2014 the COMPLETE new parent set. `org`/`client` are reserved namespace names, registered like any other. Omit to leave ownership unchanged; `[]` clears it (private to the owning user).'
12211
12819
  ),
12212
12820
  expectedVersion: import_zod19.z.number().int().optional().describe(
@@ -12255,7 +12863,7 @@ var inputSchema19 = {
12255
12863
  var folderDelete = ({ client, log }) => ({
12256
12864
  name: "folder_delete",
12257
12865
  title: "Delete a folder",
12258
- description: 'Permanently delete a folder by id. The folder must be EMPTY first \u2014 it must contain no documents, no records, and no sub-folders \u2014 and a non-empty folder is refused, which is the most common way this call fails. Use record_query with this folderId, and document_query, to see what is in it. Clearing it differs by content type: a document can be moved out (document_update with a different folderId) or deleted. A RECORD can also be re-filed into another folder \u2014 the API supports it \u2014 but THIS SERVER does not expose that yet: record_update has no folderId argument, so from here your only option is record_delete (needs records:d). Do NOT read that as "the record cannot be moved" and delete the record on that basis: if the record should be kept, tell the user to move it with the Vectros API/SDK directly rather than deleting it here. A sub-folder cannot be re-parented at all, so nested folders have to be emptied and deleted innermost-first. Note the emptiness check counts everything in the folder, INCLUDING items your credential cannot read \u2014 so a refusal you cannot account for from what you can list is expected behaviour, not a bug, and retrying will not clear it. Requires the key to allow folders:d \u2014 a key without it gets a permission error. Protected folders (e.g. a context root) cannot be deleted at all, however empty.',
12866
+ description: 'Permanently delete a folder by id. The folder must be EMPTY first \u2014 it must contain no documents, no records, and no sub-folders \u2014 and a non-empty folder is refused, which is the most common way this call fails. Use record_query with this folderId, and document_query, to see what is in it. Clearing it differs by content type: a document can be moved out (document_update with a different folderId) or deleted. A RECORD can also be re-filed into another folder \u2014 the API supports it \u2014 but THIS SERVER does not expose that: record_update has no folderId argument, so from here your only option is record_delete (needs records:d). Do NOT read that as "the record cannot be moved" and delete the record on that basis: if the record should be kept, tell the user to move it with the Vectros API/SDK directly rather than deleting it here. A sub-folder cannot be re-parented at all, so nested folders have to be emptied and deleted innermost-first. Note the emptiness check counts everything in the folder, INCLUDING items your credential cannot read \u2014 so a refusal you cannot account for from what you can list is expected behaviour, not a bug, and retrying will not clear it. Requires the key to allow folders:d \u2014 a key without it gets a permission error. Protected folders (e.g. a context root) cannot be deleted at all, however empty.',
12259
12867
  inputSchema: inputSchema19,
12260
12868
  // Deletes. Idempotent in the standard REST sense: the end state ("this id no longer
12261
12869
  // resolves") is the same whether this is the first delete or a repeat of one.
@@ -12408,14 +13016,14 @@ var inputSchema21 = {
12408
13016
  // Common (both modes):
12409
13017
  folderId: import_zod21.z.string().optional().describe("Optional folder to ingest into. Default: tenant root."),
12410
13018
  userId: import_zod21.z.string().optional().describe("Owning user ID. Optional."),
12411
- scopes: import_zod21.z.array(import_zod21.z.string()).optional().describe(
13019
+ scopes: import_zod21.z.array(import_zod21.z.string()).max(MAX_OWNERSHIP_SCOPES, `scopes accepts at most ${MAX_OWNERSHIP_SCOPES} entries`).optional().describe(
12412
13020
  "TEXT MODE ONLY. Scope ownership as `namespace:value` entries, at most 2 (e.g. [\"org:<uuid>\", \"group:eng-team\"]). `org` and `client` are reserved namespace names, registered like any other; others are namespaces you registered yourself. This is the document's COMPLETE scope declaration and values must come from the credential's own identity. An empty array `[]` creates a PRIVATE document owned by the calling user alone. Omit to stamp the credential's full identity \u2014 the default. Rejected with `filePath` (file uploads do not accept a scope declaration): a file-uploaded document is stamped with the credential's full identity."
12413
13021
  )
12414
13022
  };
12415
13023
  var documentIngest = ({ client, log, transport, ingestRoot }) => ({
12416
13024
  name: "document_ingest",
12417
13025
  title: "Ingest a new document (text body or file upload)",
12418
- description: "Create a new document in your Vectros tenant. Two modes:\n \u2022 Text mode: pass `text` (string body). Single-call. Use for crawled content, notes, generated text.\n \u2022 File mode: pass `filePath` (local path on the MCP server's host machine). STDIO-TRANSPORT ONLY \u2014 rejected on HTTP transport. MCP server reads the bytes, requests a presigned upload URL, and PUTs them. Returns when the upload is accepted (indexStatus: PENDING_INDEX); poll `document_get` until indexStatus is INDEXED. EXCEPTION \u2014 re-ingesting file mode against an externalId that already has a document (response `created:false`): the file body is replaced by ANY such re-ingest, whether or not `upsert` was set (`upsert` there only additionally applies `payload`/`title` to the matched document). The document keeps serving the OLD file's bytes until the new one is adopted, and indexStatus already reads INDEXED left over from that old file, so polling it returns immediately without waiting for the new content. Poll `document_get` and watch `fileItemId` instead \u2014 once it differs from the value it held before this call, the new file has been adopted. Re-request any download URL after that: one minted before adoption can stop working even within its stated `downloadExpires`.\nEither `text` or `filePath` must be present; both is an error. Idempotent by `externalId` (re-ingest returns the existing document, not a duplicate \u2014 requires documents:r in addition to documents:c, since being handed the existing document is a read of it; in FILE MODE a re-ingest against an existing externalId needs documents:u). TEXT MODE: without `upsert`, the existing document's content is genuinely unchanged. FILE MODE: as noted above, a re-ingest against an existing externalId always replaces the file body regardless of `upsert` \u2014 only the `payload`/`title` metadata overwrite is gated by it. To UPDATE an existing document instead \u2014 re-index an edited body \u2014 pass `upsert:true` with the same `externalId` (and the same `schemaId`); this is the re-sync primitive for keeping a knowledge base current. Pass `schemaId` + `payload` for a typed, lookup-queryable document (records parity). An `externalId` with no `schemaId` is refused (400) unless you also pass `confirmUntyped:true` to confirm that an untyped document is intended. indexMode defaults to HYBRID for untyped documents (omit to inherit a bound schema's default). storeText is a FILE-MODE knob (default true): false discards the uploaded file's extracted text after indexing (search + file download keep working; text retrieval and document_ask do not). Text-mode bodies are always retained, so storeText is not accepted with `text`.",
13026
+ description: "Create a new document in your Vectros tenant. Two modes:\n \u2022 Text mode: pass `text` (string body). Single-call. Use for crawled content, notes, generated text.\n \u2022 File mode: pass `filePath` (local path on the MCP server's host machine). STDIO-TRANSPORT ONLY \u2014 rejected on HTTP transport. MCP server reads the bytes, requests a presigned upload URL, and PUTs them. Returns when the upload is accepted (indexStatus: PENDING_INDEX); poll `document_get` until indexStatus is terminal: INDEXED, SKIPPED (no indexable text: `extraction.outcome` says why, e.g. NO_TEXT for an image or a scanned page), STORED (indexMode NONE) or FAILED. EXCEPTION \u2014 re-ingesting file mode against an externalId that already has a document (response `created:false`): the file body is replaced by ANY such re-ingest, whether or not `upsert` was set (`upsert` there only additionally applies `payload`/`title` to the matched document). The document keeps serving the OLD file's bytes until the new one is adopted, and indexStatus already reads INDEXED left over from that old file, so polling it returns immediately without waiting for the new content. Poll `document_get` and watch `fileItemId` instead \u2014 once it differs from the value it held before this call, the new file has been adopted. Re-request any download URL after that: one minted before adoption can stop working even within its stated `downloadExpires`.\nEither `text` or `filePath` must be present; both is an error. Idempotent by `externalId` (re-ingest returns the existing document, not a duplicate \u2014 requires documents:r in addition to documents:c, since being handed the existing document is a read of it; in FILE MODE a re-ingest against an existing externalId needs documents:u). TEXT MODE: without `upsert`, the existing document's content is genuinely unchanged. FILE MODE: as noted above, a re-ingest against an existing externalId always replaces the file body regardless of `upsert` \u2014 only the `payload`/`title` metadata overwrite is gated by it. To UPDATE an existing document instead \u2014 re-index an edited body \u2014 pass `upsert:true` with the same `externalId` (and the same `schemaId`); this is the re-sync primitive for keeping a knowledge base current. Pass `schemaId` + `payload` for a typed, lookup-queryable document (records parity). An `externalId` with no `schemaId` is refused (400) unless you also pass `confirmUntyped:true` to confirm that an untyped document is intended. indexMode defaults to HYBRID for untyped documents (omit to inherit a bound schema's default). storeText is a FILE-MODE knob (default true): false discards the uploaded file's extracted text after indexing (search + file download keep working; text retrieval and document_ask do not). Text-mode bodies are always retained, so storeText is not accepted with `text`.",
12419
13027
  inputSchema: inputSchema21,
12420
13028
  // Creates by default, idempotent ONLY when the caller supplies `externalId` (optional,
12421
13029
  // per the doc comment above) — re-ingest then returns the existing document rather than
@@ -12557,7 +13165,7 @@ var documentIngest = ({ client, log, transport, ingestRoot }) => ({
12557
13165
  "document_ingest file ok"
12558
13166
  );
12559
13167
  const uploadCreated = upload?.created;
12560
- const note = uploadCreated === false ? "File replaced: this re-ingest matched an existing externalId, which always replaces that document's file body (upsert or not \u2014 upsert only additionally applies payload/title to it). The next document_get call may already show indexStatus: INDEXED \u2014 that is left over from the PREVIOUS file, not confirmation the new one is ready, and the document keeps serving the old file's bytes until adoption completes. Poll document_get(id) and watch fileItemId instead: once it differs from the value it held before this call, the new file has been adopted. Any download URL already held for this document was minted for the old file and can stop working before its stated downloadExpires once adoption happens \u2014 request a fresh one after fileItemId changes." : "File uploaded; indexing is asynchronous. Poll document_get(id) until indexStatus is INDEXED.";
13168
+ const note = uploadCreated === false ? "File replaced: this re-ingest matched an existing externalId, which always replaces that document's file body (upsert or not \u2014 upsert only additionally applies payload/title to it). The next document_get call may already show indexStatus: INDEXED \u2014 that is left over from the PREVIOUS file, not confirmation the new one is ready, and the document keeps serving the old file's bytes until adoption completes. Poll document_get(id) and watch fileItemId instead: once it differs from the value it held before this call, the new file has been adopted. Any download URL already held for this document was minted for the old file and can stop working before its stated downloadExpires once adoption happens \u2014 request a fresh one after fileItemId changes." : "File uploaded; indexing is asynchronous. Poll document_get(id) until indexStatus is INDEXED, SKIPPED (no indexable text; see extraction.outcome), STORED or FAILED.";
12561
13169
  return {
12562
13170
  content: [
12563
13171
  {
@@ -12730,6 +13338,365 @@ var versionHistory = ({ client, log }) => ({
12730
13338
  });
12731
13339
  var version_history_default = versionHistory;
12732
13340
 
13341
+ // src/tools/agent_list.ts
13342
+ var import_zod24 = require("zod");
13343
+ var MCP_DEFAULT_LIMIT6 = 10;
13344
+ var MCP_MAX_LIMIT6 = 100;
13345
+ function toAgentSummary(agent) {
13346
+ return {
13347
+ id: agent.id,
13348
+ name: agent.name,
13349
+ description: agent.description,
13350
+ latestVersion: agent.latestVersion,
13351
+ status: agent.status
13352
+ };
13353
+ }
13354
+ var inputSchema24 = {
13355
+ includeRetired: import_zod24.z.boolean().optional().describe("Also list retired and purged agents (neither can be run). Default false."),
13356
+ startFrom: import_zod24.z.string().optional().describe("Pagination cursor: pass the `nextCursor` from the previous page to fetch the next."),
13357
+ limit: import_zod24.z.number().int().min(1).max(MCP_MAX_LIMIT6).optional().describe(`Max agents per page. Default ${MCP_DEFAULT_LIMIT6}, max ${MCP_MAX_LIMIT6}.`)
13358
+ };
13359
+ var agentList = ({ client, log }) => ({
13360
+ name: "agent_list",
13361
+ title: "List agents",
13362
+ description: "List the stored agents you can run with agent_run, newest first. Each is returned as its `id`, `name`, `description`, `latestVersion` and `status`; only an `ACTIVE` agent can be run. Retired and purged agents are left out unless `includeRetired` is true. Returns a `{ data, nextCursor }` page: a non-null `nextCursor` means more remain, pass it back as `startFrom`. Needs the `runs:c` or `agents:r` scope; running a listed agent needs `runs:c`, so a credential with only `agents:r` can list agents but not run them.",
13363
+ inputSchema: inputSchema24,
13364
+ // A pure read: it lists definitions and changes nothing.
13365
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
13366
+ handler: async (args) => {
13367
+ const limit = args.limit ?? MCP_DEFAULT_LIMIT6;
13368
+ try {
13369
+ const page = await client.agents.listAgents({
13370
+ includeRetired: args.includeRetired,
13371
+ startFrom: args.startFrom,
13372
+ limit
13373
+ });
13374
+ const data = (page.data ?? []).map(toAgentSummary);
13375
+ const nextCursor = page.nextCursor ?? null;
13376
+ log.debug({ tool: "agent_list", limit, returned: data.length }, "agent_list ok");
13377
+ return { content: [{ type: "text", text: JSON.stringify({ data, nextCursor }, null, 2) }] };
13378
+ } catch (err) {
13379
+ log.warn({ tool: "agent_list", err: String(err) }, "agent_list failed");
13380
+ return toolError("agent_list", err);
13381
+ }
13382
+ }
13383
+ });
13384
+ var agent_list_default = agentList;
13385
+
13386
+ // src/tools/agent_run.ts
13387
+ var import_node_crypto = require("crypto");
13388
+ var import_zod25 = require("zod");
13389
+
13390
+ // src/run-stream.ts
13391
+ var TEXT_PROGRESS_CHARS = 256;
13392
+ var HEARTBEAT_MS = 15e3;
13393
+ var TERMINAL_STATUS = {
13394
+ "run.completed": "COMPLETED",
13395
+ "run.failed": "FAILED",
13396
+ "run.cancelled": "CANCELLED"
13397
+ };
13398
+ function str(v) {
13399
+ return typeof v === "string" && v.length > 0 ? v : void 0;
13400
+ }
13401
+ function num(v) {
13402
+ return typeof v === "number" && Number.isFinite(v) ? v : void 0;
13403
+ }
13404
+ async function consumeRunStream(stream, initialRunId, onProgress, heartbeatMs = HEARTBEAT_MS) {
13405
+ const result = {
13406
+ runId: initialRunId,
13407
+ terminal: false,
13408
+ status: "UNKNOWN",
13409
+ text: "",
13410
+ toolCalls: []
13411
+ };
13412
+ const blocks = [];
13413
+ let pending = "";
13414
+ let unsentChars = 0;
13415
+ const emit = async (message) => {
13416
+ if (!onProgress) return;
13417
+ try {
13418
+ await onProgress(message);
13419
+ } catch {
13420
+ }
13421
+ };
13422
+ let heartbeat;
13423
+ let lastEventAt = Date.now();
13424
+ if (onProgress) {
13425
+ heartbeat = setInterval(() => {
13426
+ if (Date.now() - lastEventAt >= heartbeatMs) {
13427
+ lastEventAt = Date.now();
13428
+ void emit("run still working");
13429
+ }
13430
+ }, Math.max(1, Math.floor(heartbeatMs / 3)));
13431
+ heartbeat.unref?.();
13432
+ }
13433
+ try {
13434
+ for await (const raw of stream) {
13435
+ lastEventAt = Date.now();
13436
+ if (!raw || typeof raw !== "object") continue;
13437
+ const ev = raw;
13438
+ result.runId ??= str(ev.runId);
13439
+ const type = typeof ev.type === "string" ? ev.type : "";
13440
+ switch (type) {
13441
+ case "run.started":
13442
+ result.agentId = str(ev.agentId) ?? result.agentId;
13443
+ result.agentVersion = num(ev.agentVersion) ?? result.agentVersion;
13444
+ await emit("run started");
13445
+ break;
13446
+ case "message.delta": {
13447
+ const piece = typeof ev.text === "string" ? ev.text : "";
13448
+ pending += piece;
13449
+ unsentChars += piece.length;
13450
+ if (unsentChars >= TEXT_PROGRESS_CHARS) {
13451
+ unsentChars = 0;
13452
+ await emit(`answer: ${blocks.join("\n\n").length + pending.length} characters so far`);
13453
+ }
13454
+ break;
13455
+ }
13456
+ case "message.completed": {
13457
+ const full = str(ev.text) ?? pending;
13458
+ if (full.length > 0) blocks.push(full);
13459
+ pending = "";
13460
+ unsentChars = 0;
13461
+ await emit("model call completed");
13462
+ break;
13463
+ }
13464
+ case "tool.completed": {
13465
+ const call = {
13466
+ tool: str(ev.tool),
13467
+ outcome: str(ev.outcome),
13468
+ code: str(ev.code)
13469
+ };
13470
+ result.toolCalls.push(call);
13471
+ await emit(`tool ${call.tool ?? "call"}: ${call.outcome ?? "ended"}`);
13472
+ break;
13473
+ }
13474
+ default:
13475
+ break;
13476
+ }
13477
+ if (ev.terminal === true || Object.hasOwn(TERMINAL_STATUS, type)) {
13478
+ result.terminal = true;
13479
+ result.status = str(ev.status) ?? TERMINAL_STATUS[type] ?? "ENDED";
13480
+ result.reason = str(ev.reason);
13481
+ result.errorCode = str(ev.errorCode);
13482
+ if (typeof ev.settled === "boolean" || ev.inferenceBalanceCentsChargedMilli !== void 0 || ev.inferenceBalanceCentsRefundedMilli !== void 0) {
13483
+ result.charged = {
13484
+ settled: typeof ev.settled === "boolean" ? ev.settled : void 0,
13485
+ inferenceBalanceCentsChargedMilli: num(ev.inferenceBalanceCentsChargedMilli),
13486
+ inferenceBalanceCentsRefundedMilli: num(ev.inferenceBalanceCentsRefundedMilli)
13487
+ };
13488
+ }
13489
+ break;
13490
+ }
13491
+ }
13492
+ } catch (err) {
13493
+ result.streamError = err instanceof Error ? err.message : String(err);
13494
+ } finally {
13495
+ if (heartbeat) clearInterval(heartbeat);
13496
+ }
13497
+ if (pending.length > 0) blocks.push(pending);
13498
+ result.text = blocks.join("\n\n");
13499
+ return result;
13500
+ }
13501
+
13502
+ // src/tools/agent_run.ts
13503
+ var RUN_ID_HEADER = "x-vectros-run-id";
13504
+ var MAX_INPUT_CHARS = 1e5;
13505
+ var MAX_INPUT_BYTES = 24e4;
13506
+ function encodedInputBytes(v) {
13507
+ return Buffer.byteLength(JSON.stringify(v), "utf8");
13508
+ }
13509
+ var TERMINAL_EVENT_TYPES = /* @__PURE__ */ new Set(["run.completed", "run.failed", "run.cancelled"]);
13510
+ var ENDED_STATUSES = /* @__PURE__ */ new Set(["COMPLETED", "FAILED", "CANCELLED", "EXPIRED"]);
13511
+ var inputSchema25 = {
13512
+ agentId: import_zod25.z.string().min(1, "agentId must be non-empty").describe("The agent to run: an `id` from agent_list."),
13513
+ input: import_zod25.z.string().min(1, "input must be non-empty").max(MAX_INPUT_CHARS, `input accepts at most ${MAX_INPUT_CHARS} characters`).refine(
13514
+ (v) => encodedInputBytes(v) <= MAX_INPUT_BYTES,
13515
+ `input accepts at most ${MAX_INPUT_BYTES} bytes as sent (a JSON string in UTF-8)`
13516
+ ).describe(
13517
+ `What the agent is asked, 1 to ${MAX_INPUT_CHARS} characters and at most ${MAX_INPUT_BYTES} bytes as sent (a JSON string in UTF-8; most control characters other than newline and tab count six bytes).`
13518
+ ),
13519
+ agentVersion: import_zod25.z.number().int().min(1).optional().describe("The agent version to run. Default: its latest version."),
13520
+ maxInferenceCents: import_zod25.z.number().int().min(1).optional().describe(
13521
+ "The most this run's model calls may cost, in US cents. It can only lower the agent's own budget (a higher value is refused); a value below the platform minimum is refused with errorCode `BUDGET_TOO_LOW`, whose message names the minimum. Default: the agent's budget. A run that reaches it ends `FAILED` with errorCode `BUDGET_EXCEEDED`."
13522
+ ),
13523
+ readOnly: import_zod25.z.boolean().optional().describe("When true, every tool call that would change data is refused in this run. Default false."),
13524
+ idempotencyKey: import_zod25.z.string().regex(/^[A-Za-z0-9._:-]{1,128}$/, "idempotencyKey is 1 to 128 letters, digits, `.`, `_`, `:` or `-`").optional().describe(
13525
+ "Makes a retried call safe: the same key with the same arguments never starts a second run; the retry returns that run's id and status (`alreadyStarted: true`) instead. Its answer text is not returned again; read how it ended with run_get. Default: a new key per call."
13526
+ )
13527
+ };
13528
+ var agentRun = ({ client, log }) => ({
13529
+ name: "agent_run",
13530
+ title: "Run an agent",
13531
+ description: "Run a stored agent (from agent_list) on one `input` and return its answer. The agent's tool calls act as you, with your own permissions, and only through the tools the agent lists; set `readOnly` to refuse every tool call that would change data. Cap the run's model cost with `maxInferenceCents` (it can only lower the agent's own budget). The run is billed: model calls against your inference balance, and a fee per run, tool calls and run time against your plan. An agent can search the web or fetch a page when its definition lists that web tool (web fetch also needs your tenant's owner to switch it on, and a run of an agent that lists `web_search` is refused while the platform has web search disabled). Returns `{ runId, agentId, agentVersion, status, terminal, reason, errorCode, text, toolCalls, charged }`; `charged` carries the run's charge and refund in milli-cents (1 cent = 1000). Progress notifications keep the call alive while the run works for a client that resets its timeout on progress; cancelling the call cancels the run once the run is named (almost at once). If `terminal` is false the result arrived before the run ended: read it with run_get, which needs `runs:r` as well. Calling again without `idempotencyKey` starts a new, billed run. Needs the `runs:c` scope.",
13532
+ inputSchema: inputSchema25,
13533
+ // Not a read: a run is billed, and its tool calls may create, change or delete data with the
13534
+ // caller's permissions, so a host should confirm it. Not idempotent: each call is a new paid run
13535
+ // unless the same idempotencyKey is passed again.
13536
+ annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
13537
+ handler: async (args, extra) => {
13538
+ const agentId = args.agentId;
13539
+ const maxInferenceCents = args.maxInferenceCents;
13540
+ const idempotencyKey = args.idempotencyKey ?? (0, import_node_crypto.randomUUID)();
13541
+ const request = {
13542
+ "Idempotency-Key": idempotencyKey,
13543
+ agentId,
13544
+ agentVersion: args.agentVersion,
13545
+ input: { text: args.input },
13546
+ budget: maxInferenceCents !== void 0 ? { maxInferenceCents } : void 0,
13547
+ readOnly: args.readOnly
13548
+ };
13549
+ const progressToken = extra._meta?.progressToken;
13550
+ let progressCount = 0;
13551
+ const onProgress = progressToken !== void 0 && extra.sendNotification ? async (message) => {
13552
+ progressCount += 1;
13553
+ await extra.sendNotification({
13554
+ method: "notifications/progress",
13555
+ params: { progressToken, progress: progressCount, message }
13556
+ });
13557
+ } : void 0;
13558
+ const streamAbort = new AbortController();
13559
+ const callSignal = extra.signal;
13560
+ let knownRunId;
13561
+ let headersReceived = false;
13562
+ let cancelRequested = false;
13563
+ let cancelSent = false;
13564
+ let runEnded = false;
13565
+ const sendCancel = () => {
13566
+ if (!cancelRequested || cancelSent || !knownRunId || runEnded) return;
13567
+ cancelSent = true;
13568
+ const id = knownRunId;
13569
+ client.agents.cancelRun({ id }).then(
13570
+ () => log.info({ tool: "agent_run", runId: id }, "agent_run cancelled by the client"),
13571
+ (err) => log.warn({ tool: "agent_run", runId: id, err: String(err) }, "agent_run cancel failed")
13572
+ );
13573
+ };
13574
+ const cancelIfNamed = () => {
13575
+ if (!cancelRequested || !headersReceived || !knownRunId) return;
13576
+ sendCancel();
13577
+ streamAbort.abort();
13578
+ };
13579
+ const onCancel = () => {
13580
+ cancelRequested = true;
13581
+ cancelIfNamed();
13582
+ };
13583
+ if (callSignal?.aborted) {
13584
+ return toolError("agent_run", new Error("The call was cancelled before the run started."));
13585
+ }
13586
+ callSignal?.addEventListener("abort", onCancel, { once: true });
13587
+ try {
13588
+ const { data: stream, rawResponse } = await client.agents.createRun(request, { abortSignal: streamAbort.signal }).withRawResponse();
13589
+ headersReceived = true;
13590
+ knownRunId = rawResponse.headers.get(RUN_ID_HEADER) ?? void 0;
13591
+ cancelIfNamed();
13592
+ const naming = async function* () {
13593
+ for await (const ev of stream) {
13594
+ if (!knownRunId && ev && typeof ev === "object") {
13595
+ const id = ev.runId;
13596
+ if (typeof id === "string" && id.length > 0) {
13597
+ knownRunId = id;
13598
+ cancelIfNamed();
13599
+ }
13600
+ }
13601
+ if (ev && typeof ev === "object") {
13602
+ const e = ev;
13603
+ if (e.terminal === true || typeof e.type === "string" && TERMINAL_EVENT_TYPES.has(e.type)) runEnded = true;
13604
+ }
13605
+ yield ev;
13606
+ }
13607
+ };
13608
+ const run = await consumeRunStream(naming(), knownRunId, onProgress);
13609
+ knownRunId ??= run.runId;
13610
+ if (run.terminal) runEnded = true;
13611
+ sendCancel();
13612
+ log.debug(
13613
+ {
13614
+ tool: "agent_run",
13615
+ runId: run.runId,
13616
+ status: run.status,
13617
+ terminal: run.terminal,
13618
+ toolCalls: run.toolCalls.length,
13619
+ textLength: run.text.length
13620
+ },
13621
+ "agent_run ok"
13622
+ );
13623
+ const body = {
13624
+ runId: run.runId,
13625
+ agentId: run.agentId ?? agentId,
13626
+ agentVersion: run.agentVersion,
13627
+ status: run.status,
13628
+ terminal: run.terminal,
13629
+ reason: run.reason,
13630
+ errorCode: run.errorCode,
13631
+ text: run.text,
13632
+ toolCalls: run.toolCalls,
13633
+ charged: run.charged
13634
+ };
13635
+ if (!run.terminal) {
13636
+ body.note = cancelRequested ? "The call was cancelled and the run was asked to stop. Read it with run_get (needs the `runs:r` scope) to see how it ended." : "The stream ended before the run did" + (run.streamError ? ` (${run.streamError})` : "") + ". The run may still be working: read it with run_get until `terminal` is true (run_get needs the `runs:r` scope).";
13637
+ }
13638
+ return { content: [{ type: "text", text: JSON.stringify(body, null, 2) }] };
13639
+ } catch (err) {
13640
+ const e = err;
13641
+ if (e?.body?.errorCode === "IDEMPOTENCY_IN_PROGRESS") {
13642
+ const runId = (typeof e.body.runId === "string" ? e.body.runId : void 0) ?? e.rawResponse?.headers?.get?.(RUN_ID_HEADER) ?? void 0;
13643
+ if (runId) {
13644
+ const status = typeof e.body.status === "string" ? e.body.status : void 0;
13645
+ log.info({ tool: "agent_run", runId }, "agent_run: the idempotency key already started a run");
13646
+ const body = {
13647
+ runId,
13648
+ agentId,
13649
+ status,
13650
+ // The 409 names the run's status at the time; an ended status is final.
13651
+ terminal: status !== void 0 && ENDED_STATUSES.has(status),
13652
+ alreadyStarted: true,
13653
+ note: "This idempotency key already started this run, so no new run was started. Its answer text is not returned again and cannot be read back: run_get (needs the `runs:r` scope) shows how the run ended and what it was charged, never its text. To start another run, pass a new idempotencyKey."
13654
+ };
13655
+ return { content: [{ type: "text", text: JSON.stringify(body, null, 2) }] };
13656
+ }
13657
+ }
13658
+ log.warn({ tool: "agent_run", agentId, err: String(err) }, "agent_run failed");
13659
+ const result = toolError("agent_run", err);
13660
+ const statusCode = err?.statusCode;
13661
+ if (typeof statusCode !== "number" || statusCode === 408 || statusCode >= 500) {
13662
+ result.content[0].text += `
13663
+ idempotencyKey: ${idempotencyKey}
13664
+ A run may have started before this error. Call agent_run again with the same arguments and this idempotencyKey: if a run started, the call names it; if not, it starts one.` + (cancelRequested ? " (The call was cancelled; a run that started could not be cancelled.)" : "");
13665
+ }
13666
+ return result;
13667
+ } finally {
13668
+ callSignal?.removeEventListener("abort", onCancel);
13669
+ }
13670
+ }
13671
+ });
13672
+ var agent_run_default = agentRun;
13673
+
13674
+ // src/tools/run_get.ts
13675
+ var import_zod26 = require("zod");
13676
+ var inputSchema26 = {
13677
+ runId: import_zod26.z.string().min(1, "runId must be non-empty").describe("The run id agent_run returned (`runId`).")
13678
+ };
13679
+ var runGet = ({ client, log }) => ({
13680
+ name: "run_get",
13681
+ title: "Get an agent run",
13682
+ description: "Read one agent run by id: `status` (`RUNNING`, `COMPLETED`, `FAILED`, `CANCELLED` or `EXPIRED`), `terminal` (whether it has ended; poll until true), `reason`, `errorCode`, `steps`, its `toolCalls` (each tool and how it ended, never its arguments or result) and `usage` (what it was charged, in milli-cents). It does not return the answer text: agent_run returns that. Use it when agent_run returned `terminal: false`. Needs the `runs:r` scope, which starting a run (`runs:c`) does not include: a credential with only `runs:c` gets `403` here, and learns how its runs end only from agent_run's own result.",
13683
+ inputSchema: inputSchema26,
13684
+ // A pure read: polling a run does not change it.
13685
+ annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
13686
+ handler: async (args) => {
13687
+ const runId = args.runId;
13688
+ try {
13689
+ const run = await client.agents.getRun({ id: runId });
13690
+ log.debug({ tool: "run_get", runId, status: run.status, terminal: run.terminal }, "run_get ok");
13691
+ return { content: [{ type: "text", text: JSON.stringify(run, null, 2) }] };
13692
+ } catch (err) {
13693
+ log.warn({ tool: "run_get", runId, err: String(err) }, "run_get failed");
13694
+ return toolError("run_get", err);
13695
+ }
13696
+ }
13697
+ });
13698
+ var run_get_default = runGet;
13699
+
12733
13700
  // src/tools/types.ts
12734
13701
  var TOOL_NAMES = [
12735
13702
  "hybrid_search",
@@ -12754,7 +13721,10 @@ var TOOL_NAMES = [
12754
13721
  "current_identity",
12755
13722
  "document_ingest",
12756
13723
  "lookup_principal",
12757
- "version_history"
13724
+ "version_history",
13725
+ "agent_list",
13726
+ "agent_run",
13727
+ "run_get"
12758
13728
  ];
12759
13729
 
12760
13730
  // src/tools/index.ts
@@ -12781,7 +13751,10 @@ var ALL_TOOL_FACTORIES = {
12781
13751
  current_identity: current_identity_default,
12782
13752
  document_ingest: document_ingest_default,
12783
13753
  lookup_principal: lookup_principal_default,
12784
- version_history: version_history_default
13754
+ version_history: version_history_default,
13755
+ agent_list: agent_list_default,
13756
+ agent_run: agent_run_default,
13757
+ run_get: run_get_default
12785
13758
  };
12786
13759
 
12787
13760
  // src/resources/schemas.ts
@@ -12824,11 +13797,12 @@ var ALL_RESOURCE_FACTORIES = {
12824
13797
  };
12825
13798
 
12826
13799
  // src/zod-to-json-schema.ts
12827
- var import_zod24 = require("zod");
13800
+ var import_zod27 = require("zod");
12828
13801
  function zodShapeToJsonSchema(shape) {
12829
13802
  const properties = {};
12830
13803
  const required = [];
12831
- for (const [name, schema] of Object.entries(shape)) {
13804
+ for (const [name, entry] of Object.entries(shape)) {
13805
+ const schema = entry;
12832
13806
  properties[name] = zodTypeToJsonSchema(schema);
12833
13807
  if (!schema.isOptional()) required.push(name);
12834
13808
  }
@@ -12837,43 +13811,45 @@ function zodShapeToJsonSchema(shape) {
12837
13811
  return out;
12838
13812
  }
12839
13813
  function zodTypeToJsonSchema(schema) {
12840
- const description = schema.description;
13814
+ const descriptionOf = (s) => s.description;
13815
+ let description = descriptionOf(schema);
12841
13816
  let current = schema;
12842
- while (current instanceof import_zod24.z.ZodOptional || current instanceof import_zod24.z.ZodDefault || current instanceof import_zod24.z.ZodNullable) {
13817
+ while (current instanceof import_zod27.z.ZodOptional || current instanceof import_zod27.z.ZodDefault || current instanceof import_zod27.z.ZodNullable) {
12843
13818
  current = current._def.innerType;
13819
+ description ??= descriptionOf(current);
12844
13820
  }
12845
13821
  const base = description ? { description } : {};
12846
- if (current instanceof import_zod24.z.ZodString) {
13822
+ if (current instanceof import_zod27.z.ZodString) {
12847
13823
  return { ...base, type: "string" };
12848
13824
  }
12849
- if (current instanceof import_zod24.z.ZodNumber) {
13825
+ if (current instanceof import_zod27.z.ZodNumber) {
12850
13826
  return { ...base, type: "number" };
12851
13827
  }
12852
- if (current instanceof import_zod24.z.ZodBoolean) {
13828
+ if (current instanceof import_zod27.z.ZodBoolean) {
12853
13829
  return { ...base, type: "boolean" };
12854
13830
  }
12855
- if (current instanceof import_zod24.z.ZodEnum) {
13831
+ if (current instanceof import_zod27.z.ZodEnum) {
12856
13832
  return {
12857
13833
  ...base,
12858
13834
  type: "string",
12859
13835
  enum: current.options
12860
13836
  };
12861
13837
  }
12862
- if (current instanceof import_zod24.z.ZodArray) {
13838
+ if (current instanceof import_zod27.z.ZodArray) {
12863
13839
  return {
12864
13840
  ...base,
12865
13841
  type: "array",
12866
13842
  items: zodTypeToJsonSchema(current.element)
12867
13843
  };
12868
13844
  }
12869
- if (current instanceof import_zod24.z.ZodObject) {
13845
+ if (current instanceof import_zod27.z.ZodObject) {
12870
13846
  return {
12871
13847
  ...base,
12872
13848
  ...zodShapeToJsonSchema(current.shape)
12873
13849
  };
12874
13850
  }
12875
- if (current instanceof import_zod24.z.ZodRecord) {
12876
- const valueType = current._def.valueType;
13851
+ if (current instanceof import_zod27.z.ZodRecord) {
13852
+ const valueType = current.valueType;
12877
13853
  return {
12878
13854
  ...base,
12879
13855
  type: "object",
@@ -13001,7 +13977,7 @@ var VectrosMCPServer = class {
13001
13977
  if (!tool) {
13002
13978
  result = toolError(req.params.name ?? "unknown", new Error("No such tool."));
13003
13979
  } else {
13004
- const parsed = import_zod25.z.object(tool.inputSchema).strict().safeParse(req.params.arguments ?? {});
13980
+ const parsed = import_zod28.z.object(tool.inputSchema).strict().safeParse(req.params.arguments ?? {});
13005
13981
  if (!parsed.success) {
13006
13982
  result = toolError(tool.name, new Error(formatArgError(tool, parsed.error)));
13007
13983
  } else {
@@ -13064,7 +14040,7 @@ var VectrosMCPServer = class {
13064
14040
  *
13065
14041
  * If `validateOnStart` (default true) is enabled, performs a
13066
14042
  * GET /v1/ping check BEFORE wiring the transport. On failure,
13067
- * throws — the partner's MCP client sees a startup error instead
14043
+ * throws — your MCP client sees a startup error instead
13068
14044
  * of a confusing "tools work but the first call 401s" experience.
13069
14045
  */
13070
14046
  async connect(transport) {