@kindgi/client 0.1.4-rc.5 → 0.1.4

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/README.md CHANGED
@@ -72,7 +72,7 @@ try {
72
72
 
73
73
  ## Exports
74
74
 
75
- - **`createClient(options: ClientOptions)`** — returns a `KindgiClient` with one resource client per property (see [Resources](#resources)). `ClientOptions`: `apiUrl` (no trailing slash), `auth` (`{ kind: 'apiToken', token }` or `{ kind: 'oauth', accessToken, refresh? }`), and an optional `fetch`. Creating a client opens no connections.
75
+ - **`createClient(options: ClientOptions)`** — returns a `KindgiClient` with one resource client per property (see [Resources](#resources)). `ClientOptions`: `apiUrl` (no trailing slash), `auth` (`{ kind: 'apiToken', token }` or `{ kind: 'oauth', accessToken, refresh? }`), an optional `fetch`, and an optional `timeoutMs` (below). Creating a client opens no connections.
76
76
  - **Errors** — every method throws **`KindgiApiError`**, whose `error` is a **`KindgiError`** discriminated on `code`: `network`, `auth`, `rate-limited`, `not-found`, `conflict`, `invalid-request`, `guardrail-violation`, `server`, `not-implemented-in-preview`, `not-yet-wired`. **`fromWire(body)`** maps an API error (`{ code, message, details? }`) onto that union; wire codes it does not recognize become `server`, with the original code in `serverCode`. **`notYetWired`** and **`notImplementedInPreview`** build the two preview variants.
77
77
  - **Streaming** — **`readSse`** and **`unwrapSseData`** read a `text/event-stream` response as an `AsyncIterable`, reconnecting with exponential backoff and `Last-Event-Id`. `runs.stream`, `evalRuns.events`, `adapters.prepare` and the `secrets` rotation event stream are built on them.
78
78
  - **Types** — the input, filter, page and record types of every resource; branded ids and `Filter` / `Page` re-exported from [`@kindgi/types`](../../packages/types/); `DefineAgentSpec` and `RunStatus`.
@@ -80,6 +80,8 @@ try {
80
80
 
81
81
  The transport makes one attempt per call and does not retry. Mutating calls accept an `idempotencyKey`, sent as the `Idempotency-Key` header, so a caller's own retries are safe (see [`docs/API-ROUTE-CONVENTIONS.md`](../../docs/API-ROUTE-CONVENTIONS.md)).
82
82
 
83
+ **Timeouts.** One request may take `timeoutMs` (30 000 ms unless `ClientOptions.timeoutMs` says otherwise); then it fails with a `network` error whose `timeoutMs` is set. Streams aren't bound by it. A waited `runs.start` answers only when the run ends, so it's bound by it too, and takes its own `timeoutMs`. When the timeout runs out there, the run may still be going and its id never arrived. Start a run that can take longer with `options: { wait: false }`, whose answer carries the run's id at once, and follow it with `runs.stream(runId)`.
84
+
83
85
  ## JSDoc tags
84
86
 
85
87
  - `@wire` — the method or type mirrors a route or schema in the API's `openapi.json`.
package/dist/index.cjs CHANGED
@@ -2894,7 +2894,157 @@ function subscribeToRun(options) {
2894
2894
  });
2895
2895
  }
2896
2896
 
2897
+ // src/transport.ts
2898
+ var DEFAULT_TIMEOUT_MS = 3e4;
2899
+ var MUTATING = /* @__PURE__ */ new Set([
2900
+ "POST",
2901
+ "PUT",
2902
+ "PATCH",
2903
+ "DELETE"
2904
+ ]);
2905
+ function createTransport(options) {
2906
+ const apiUrl = options.apiUrl.replace(/\/+$/u, "");
2907
+ const fetchImpl = options.fetch ?? fetch;
2908
+ const clientTimeoutMs = checkedTimeoutMs(
2909
+ options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
2910
+ "ClientOptions.timeoutMs"
2911
+ );
2912
+ return {
2913
+ apiUrl,
2914
+ fetchImpl,
2915
+ authHeaders() {
2916
+ return { Authorization: `Bearer ${authTokenFor(options.auth)}` };
2917
+ },
2918
+ async request(input) {
2919
+ const url = buildUrl(apiUrl, input.path, input.query);
2920
+ const headers = buildHeaders(input, options.auth);
2921
+ const timeoutMs = input.timeoutMs === void 0 ? clientTimeoutMs : checkedTimeoutMs(input.timeoutMs, "timeoutMs");
2922
+ const ac = new AbortController();
2923
+ let timedOut = false;
2924
+ const timer = setTimeout(() => {
2925
+ timedOut = true;
2926
+ ac.abort(new Error("timeout"));
2927
+ }, timeoutMs);
2928
+ let response;
2929
+ try {
2930
+ response = await fetchImpl(url, {
2931
+ method: input.method,
2932
+ headers,
2933
+ ...input.body !== void 0 && { body: JSON.stringify(input.body) },
2934
+ signal: ac.signal
2935
+ });
2936
+ } catch (cause) {
2937
+ clearTimeout(timer);
2938
+ const err = timedOut ? {
2939
+ code: "network",
2940
+ message: `No answer within ${seconds(timeoutMs)}, the client's timeout (timeoutMs).`,
2941
+ cause,
2942
+ timeoutMs
2943
+ } : {
2944
+ code: "network",
2945
+ message: cause instanceof Error ? cause.message : "network request failed",
2946
+ cause
2947
+ };
2948
+ throw new KindgiApiError(err);
2949
+ }
2950
+ clearTimeout(timer);
2951
+ if (response.ok) {
2952
+ if (input.discardResponse === true || response.status === 204) {
2953
+ try {
2954
+ await response.arrayBuffer();
2955
+ } catch {
2956
+ }
2957
+ return void 0;
2958
+ }
2959
+ try {
2960
+ return await response.json();
2961
+ } catch (cause) {
2962
+ const err = {
2963
+ code: "network",
2964
+ message: "Response body was not valid JSON",
2965
+ cause
2966
+ };
2967
+ throw new KindgiApiError(err);
2968
+ }
2969
+ }
2970
+ let body;
2971
+ try {
2972
+ body = await response.json();
2973
+ } catch {
2974
+ body = void 0;
2975
+ }
2976
+ throw new KindgiApiError(
2977
+ fromWire(unwrapErrorEnvelope(body, response.status), response.status)
2978
+ );
2979
+ }
2980
+ };
2981
+ }
2982
+ function checkedTimeoutMs(value, name) {
2983
+ if (!Number.isFinite(value) || value <= 0) {
2984
+ throw new TypeError(`${name} must be a positive number of milliseconds. Got ${String(value)}.`);
2985
+ }
2986
+ return value;
2987
+ }
2988
+ function seconds(ms) {
2989
+ return `${ms / 1e3} s`;
2990
+ }
2991
+ function buildUrl(apiUrl, path, query) {
2992
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
2993
+ const base = `${apiUrl}${normalizedPath}`;
2994
+ if (query === void 0) return base;
2995
+ const params = [];
2996
+ for (const key of Object.keys(query)) {
2997
+ const value = query[key];
2998
+ if (value === void 0) continue;
2999
+ const values = typeof value === "object" ? value : [String(value)];
3000
+ for (const v of values) params.push(`${encodeURIComponent(key)}=${encodeURIComponent(v)}`);
3001
+ }
3002
+ return params.length === 0 ? base : `${base}?${params.join("&")}`;
3003
+ }
3004
+ function buildHeaders(input, auth) {
3005
+ const headers = {
3006
+ Accept: "application/json",
3007
+ Authorization: `Bearer ${authTokenFor(auth)}`
3008
+ };
3009
+ if (input.body !== void 0) {
3010
+ headers["Content-Type"] = "application/json; charset=utf-8";
3011
+ }
3012
+ if (input.idempotencyKey !== void 0 && MUTATING.has(input.method)) {
3013
+ headers["Idempotency-Key"] = input.idempotencyKey;
3014
+ }
3015
+ if (input.headers !== void 0) {
3016
+ for (const key of Object.keys(input.headers)) {
3017
+ headers[key] = input.headers[key];
3018
+ }
3019
+ }
3020
+ return headers;
3021
+ }
3022
+ function authTokenFor(auth) {
3023
+ if (auth.kind === "apiToken") return auth.token;
3024
+ return auth.accessToken;
3025
+ }
3026
+ function unwrapErrorEnvelope(body, status) {
3027
+ if (body !== null && typeof body === "object" && !Array.isArray(body)) {
3028
+ const inner = body.error;
3029
+ if (inner !== null && typeof inner === "object" && !Array.isArray(inner)) {
3030
+ return inner;
3031
+ }
3032
+ }
3033
+ return { code: "unknown", message: `HTTP ${status} without recognizable error envelope` };
3034
+ }
3035
+
2897
3036
  // src/resources/runs.ts
3037
+ function waitedStartTimeout(e) {
3038
+ if (!(e instanceof KindgiApiError) || e.error.code !== "network") return e;
3039
+ const { timeoutMs } = e.error;
3040
+ if (timeoutMs === void 0) return e;
3041
+ return new KindgiApiError({
3042
+ code: "network",
3043
+ message: `The run didn't end within ${seconds(timeoutMs)}, the client's timeout (timeoutMs). A waited start answers only when the run ends, so the run may still be going, and its id didn't arrive. Start a run that can take longer with \`options: { wait: false }\`: the answer carries its id at once. Then follow it with \`runs.stream(runId)\` or \`runs.get(runId)\`. Or raise \`timeoutMs\`.`,
3044
+ cause: e.error.cause,
3045
+ timeoutMs
3046
+ });
3047
+ }
2898
3048
  function makeRunsClient(transport) {
2899
3049
  return {
2900
3050
  async start(input) {
@@ -2913,14 +3063,19 @@ function makeRunsClient(transport) {
2913
3063
  input: input.input,
2914
3064
  ...input.options !== void 0 && { options: input.options }
2915
3065
  };
2916
- return transport.request({
2917
- method: "POST",
2918
- path: "/v1/runs",
2919
- body,
2920
- ...input.idempotencyKey !== void 0 && {
2921
- idempotencyKey: input.idempotencyKey
2922
- }
2923
- });
3066
+ try {
3067
+ return await transport.request({
3068
+ method: "POST",
3069
+ path: "/v1/runs",
3070
+ body,
3071
+ ...input.idempotencyKey !== void 0 && {
3072
+ idempotencyKey: input.idempotencyKey
3073
+ },
3074
+ ...input.timeoutMs !== void 0 && { timeoutMs: input.timeoutMs }
3075
+ });
3076
+ } catch (e) {
3077
+ throw input.options?.wait === false ? e : waitedStartTimeout(e);
3078
+ }
2924
3079
  },
2925
3080
  async dryRun(_input) {
2926
3081
  throw new KindgiApiError(
@@ -4050,127 +4205,6 @@ function makeWebhooksClient(transport) {
4050
4205
  };
4051
4206
  }
4052
4207
 
4053
- // src/transport.ts
4054
- var DEFAULT_TIMEOUT_MS = 3e4;
4055
- var MUTATING = /* @__PURE__ */ new Set([
4056
- "POST",
4057
- "PUT",
4058
- "PATCH",
4059
- "DELETE"
4060
- ]);
4061
- function createTransport(options) {
4062
- const apiUrl = options.apiUrl.replace(/\/+$/u, "");
4063
- const fetchImpl = options.fetch ?? fetch;
4064
- const clientTimeoutMs = DEFAULT_TIMEOUT_MS;
4065
- return {
4066
- apiUrl,
4067
- fetchImpl,
4068
- authHeaders() {
4069
- return { Authorization: `Bearer ${authTokenFor(options.auth)}` };
4070
- },
4071
- async request(input) {
4072
- const url = buildUrl(apiUrl, input.path, input.query);
4073
- const headers = buildHeaders(input, options.auth);
4074
- const timeoutMs = input.timeoutMs ?? clientTimeoutMs;
4075
- const ac = new AbortController();
4076
- const timer = setTimeout(
4077
- () => ac.abort(new Error("timeout")),
4078
- timeoutMs
4079
- );
4080
- let response;
4081
- try {
4082
- response = await fetchImpl(url, {
4083
- method: input.method,
4084
- headers,
4085
- ...input.body !== void 0 && { body: JSON.stringify(input.body) },
4086
- signal: ac.signal
4087
- });
4088
- } catch (cause) {
4089
- clearTimeout(timer);
4090
- const err = {
4091
- code: "network",
4092
- message: cause instanceof Error ? cause.message : "network request failed",
4093
- cause
4094
- };
4095
- throw new KindgiApiError(err);
4096
- }
4097
- clearTimeout(timer);
4098
- if (response.ok) {
4099
- if (input.discardResponse === true || response.status === 204) {
4100
- try {
4101
- await response.arrayBuffer();
4102
- } catch {
4103
- }
4104
- return void 0;
4105
- }
4106
- try {
4107
- return await response.json();
4108
- } catch (cause) {
4109
- const err = {
4110
- code: "network",
4111
- message: "Response body was not valid JSON",
4112
- cause
4113
- };
4114
- throw new KindgiApiError(err);
4115
- }
4116
- }
4117
- let body;
4118
- try {
4119
- body = await response.json();
4120
- } catch {
4121
- body = void 0;
4122
- }
4123
- throw new KindgiApiError(
4124
- fromWire(unwrapErrorEnvelope(body, response.status), response.status)
4125
- );
4126
- }
4127
- };
4128
- }
4129
- function buildUrl(apiUrl, path, query) {
4130
- const normalizedPath = path.startsWith("/") ? path : `/${path}`;
4131
- const base = `${apiUrl}${normalizedPath}`;
4132
- if (query === void 0) return base;
4133
- const params = [];
4134
- for (const key of Object.keys(query)) {
4135
- const value = query[key];
4136
- if (value === void 0) continue;
4137
- const values = typeof value === "object" ? value : [String(value)];
4138
- for (const v of values) params.push(`${encodeURIComponent(key)}=${encodeURIComponent(v)}`);
4139
- }
4140
- return params.length === 0 ? base : `${base}?${params.join("&")}`;
4141
- }
4142
- function buildHeaders(input, auth) {
4143
- const headers = {
4144
- Accept: "application/json",
4145
- Authorization: `Bearer ${authTokenFor(auth)}`
4146
- };
4147
- if (input.body !== void 0) {
4148
- headers["Content-Type"] = "application/json; charset=utf-8";
4149
- }
4150
- if (input.idempotencyKey !== void 0 && MUTATING.has(input.method)) {
4151
- headers["Idempotency-Key"] = input.idempotencyKey;
4152
- }
4153
- if (input.headers !== void 0) {
4154
- for (const key of Object.keys(input.headers)) {
4155
- headers[key] = input.headers[key];
4156
- }
4157
- }
4158
- return headers;
4159
- }
4160
- function authTokenFor(auth) {
4161
- if (auth.kind === "apiToken") return auth.token;
4162
- return auth.accessToken;
4163
- }
4164
- function unwrapErrorEnvelope(body, status) {
4165
- if (body !== null && typeof body === "object" && !Array.isArray(body)) {
4166
- const inner = body.error;
4167
- if (inner !== null && typeof inner === "object" && !Array.isArray(inner)) {
4168
- return inner;
4169
- }
4170
- }
4171
- return { code: "unknown", message: `HTTP ${status} without recognizable error envelope` };
4172
- }
4173
-
4174
4208
  // src/client.ts
4175
4209
  function createClient(options) {
4176
4210
  const transport = createTransport(options);
package/dist/index.d.cts CHANGED
@@ -1775,7 +1775,9 @@ export interface DefineAgentSpec {
1775
1775
  * Required capabilities the agent needs from a `ModelProvider`.
1776
1776
  * Typically one entry: `[{ needs: [{ feature: 'tool-use' }] }]`
1777
1777
  * for a tool-calling agent, `[{ needs: [{ feature: 'structured-output' }] }]`
1778
- * for an agent that returns schema-constrained JSON. The router uses
1778
+ * for an agent that should run on a model that can follow a JSON schema
1779
+ * natively (its typed `output` is still checked by parse and repair, on
1780
+ * every model). The router uses
1779
1781
  * the first entry to pick a compatible provider from the tenant's
1780
1782
  * `ProviderRegistry`.
1781
1783
  */
@@ -3997,6 +3999,12 @@ export interface ClientOptions {
3997
3999
  readonly auth: AuthConfig;
3998
4000
  /** Overridable fetch impl for testing. Defaults to global `fetch`. */
3999
4001
  readonly fetch?: typeof fetch;
4002
+ /**
4003
+ * How long one request may take, in milliseconds, before it fails with
4004
+ * a `network` error. Default 30 000. Streams (`runs.stream` and the
4005
+ * like) aren't bound by it. `runs.start` also takes its own.
4006
+ */
4007
+ readonly timeoutMs?: number;
4000
4008
  }
4001
4009
  /** The verdict of a judgment. */
4002
4010
  export type Verdict = "yes" | "no";
@@ -6909,6 +6917,19 @@ declare namespace Schemas {
6909
6917
  nextCursor?: string;
6910
6918
  hasMore: boolean;
6911
6919
  };
6920
+ /**
6921
+ * How the model thinks before it answers, so a call that wants as little as it allows (a judge's) gets it. Absent: it doesn't think, or nothing is known.
6922
+ */
6923
+ export type ModelThinking = {
6924
+ /**
6925
+ * `adaptive`: on unless turned down. `always`: on, and it can only be lowered.
6926
+ */
6927
+ mode: "adaptive" | "always";
6928
+ /**
6929
+ * The vendor's own setting for the least thinking: for Anthropic `disabled`, `between_tools` or an effort (`low`); for Gemini a thinking level (`low`, `minimal`); for OpenAI a reasoning effort (`low`, `none`).
6930
+ */
6931
+ lowest: string;
6932
+ };
6912
6933
  /**
6913
6934
  * USD per 1K tokens. An adapter may take more rate fields (see the adapter's README).
6914
6935
  */
@@ -6938,6 +6959,11 @@ declare namespace Schemas {
6938
6959
  * Fallback cap on output tokens. Adapters that require `max_tokens` on every request (e.g. Anthropic) use this when `ModelCallInput.maxOutputTokens` is unset.
6939
6960
  */
6940
6961
  maxOutputTokens?: number;
6962
+ /**
6963
+ * Whether the model takes sampling settings (`temperature`). `false`: its API rejects a non-default value, so the call goes without one and the answer's `warnings` say so (`sampling-unsupported`). Absent: it takes them.
6964
+ */
6965
+ sampling?: boolean;
6966
+ thinking?: ModelThinking;
6941
6967
  /**
6942
6968
  * Short per-model description surfaced in logs.
6943
6969
  */
@@ -6953,6 +6979,10 @@ declare namespace Schemas {
6953
6979
  * Models this connection exposes. Non-empty. `models[i].name` must be unique within the list.
6954
6980
  */
6955
6981
  models: Array<ModelInfo>;
6982
+ /**
6983
+ * The model to use when an agent doesn't choose: one of `models[].name`. When candidates rank equally, it comes before the provider's other models; without it, ties break by model name. A preset sets it. A runtime before 0.1.4 ignores it.
6984
+ */
6985
+ defaultModel?: string;
6956
6986
  /**
6957
6987
  * Soft attributes for preference-ranking (`local`, `lower-cost`, `higher-accuracy`, ...). Matched by string equality against `Preference.feature`.
6958
6988
  */
@@ -10091,8 +10121,17 @@ declare const ProviderMetadata: z.ZodObject<{
10091
10121
  }, z.core.$catchall<z.ZodUnknown>>;
10092
10122
  p95LatencyMs: z.ZodOptional<z.ZodNumber>;
10093
10123
  maxOutputTokens: z.ZodOptional<z.ZodNumber>;
10124
+ sampling: z.ZodOptional<z.ZodBoolean>;
10125
+ thinking: z.ZodOptional<z.ZodObject<{
10126
+ mode: z.ZodEnum<{
10127
+ always: "always";
10128
+ adaptive: "adaptive";
10129
+ }>;
10130
+ lowest: z.ZodString;
10131
+ }, z.core.$strict>>;
10094
10132
  description: z.ZodOptional<z.ZodString>;
10095
10133
  }, z.core.$strict>>;
10134
+ defaultModel: z.ZodOptional<z.ZodString>;
10096
10135
  attributes: z.ZodOptional<z.ZodArray<z.ZodString>>;
10097
10136
  description: z.ZodOptional<z.ZodString>;
10098
10137
  capabilityKind: z.ZodOptional<z.ZodString>;
@@ -10128,8 +10167,17 @@ declare const ProviderCollectionPage: z.ZodObject<{
10128
10167
  }, z.core.$catchall<z.ZodUnknown>>;
10129
10168
  p95LatencyMs: z.ZodOptional<z.ZodNumber>;
10130
10169
  maxOutputTokens: z.ZodOptional<z.ZodNumber>;
10170
+ sampling: z.ZodOptional<z.ZodBoolean>;
10171
+ thinking: z.ZodOptional<z.ZodObject<{
10172
+ mode: z.ZodEnum<{
10173
+ always: "always";
10174
+ adaptive: "adaptive";
10175
+ }>;
10176
+ lowest: z.ZodString;
10177
+ }, z.core.$strict>>;
10131
10178
  description: z.ZodOptional<z.ZodString>;
10132
10179
  }, z.core.$strict>>;
10180
+ defaultModel: z.ZodOptional<z.ZodString>;
10133
10181
  attributes: z.ZodOptional<z.ZodArray<z.ZodString>>;
10134
10182
  description: z.ZodOptional<z.ZodString>;
10135
10183
  capabilityKind: z.ZodOptional<z.ZodString>;
@@ -10168,8 +10216,17 @@ declare const RegisterProviderBody: z.ZodObject<{
10168
10216
  }, z.core.$catchall<z.ZodUnknown>>;
10169
10217
  p95LatencyMs: z.ZodOptional<z.ZodNumber>;
10170
10218
  maxOutputTokens: z.ZodOptional<z.ZodNumber>;
10219
+ sampling: z.ZodOptional<z.ZodBoolean>;
10220
+ thinking: z.ZodOptional<z.ZodObject<{
10221
+ mode: z.ZodEnum<{
10222
+ always: "always";
10223
+ adaptive: "adaptive";
10224
+ }>;
10225
+ lowest: z.ZodString;
10226
+ }, z.core.$strict>>;
10171
10227
  description: z.ZodOptional<z.ZodString>;
10172
10228
  }, z.core.$strict>>;
10229
+ defaultModel: z.ZodOptional<z.ZodString>;
10173
10230
  attributes: z.ZodOptional<z.ZodArray<z.ZodString>>;
10174
10231
  description: z.ZodOptional<z.ZodString>;
10175
10232
  capabilityKind: z.ZodOptional<z.ZodString>;
@@ -14696,6 +14753,11 @@ export interface RunsClient {
14696
14753
  * with `options.wait: false` as soon as it exists (202) — poll
14697
14754
  * `get(runId)` until it finishes.
14698
14755
  *
14756
+ * A waited start is bound by the client's timeout (`timeoutMs`, 30 s by
14757
+ * default; this call can set its own). When it runs out, the run may
14758
+ * still be going and its id never arrived: the `network` error says so.
14759
+ * Start a run that can take longer with `options.wait: false`.
14760
+ *
14699
14761
  * `idempotencyKey` makes retries safe: two calls with the same key
14700
14762
  * within the server's retention window return the same `Run`.
14701
14763
  *
@@ -14860,6 +14922,13 @@ export type StartRunInput = {
14860
14922
  readonly input: unknown;
14861
14923
  readonly options?: StartRunOptions;
14862
14924
  readonly idempotencyKey?: string;
14925
+ /**
14926
+ * How long to wait for the answer, in milliseconds: this call's
14927
+ * `ClientOptions.timeoutMs`. A waited start answers only when the run
14928
+ * ends, so a run that can take longer is better started with
14929
+ * `options: { wait: false }` and followed.
14930
+ */
14931
+ readonly timeoutMs?: number;
14863
14932
  } | {
14864
14933
  readonly flow: FlowId | string;
14865
14934
  readonly flowVersion?: string;
@@ -14870,6 +14939,13 @@ export type StartRunInput = {
14870
14939
  readonly input: unknown;
14871
14940
  readonly options?: StartRunOptions;
14872
14941
  readonly idempotencyKey?: string;
14942
+ /**
14943
+ * How long to wait for the answer, in milliseconds: this call's
14944
+ * `ClientOptions.timeoutMs`. A waited start answers only when the run
14945
+ * ends, so a run that can take longer is better started with
14946
+ * `options: { wait: false }` and followed.
14947
+ */
14948
+ readonly timeoutMs?: number;
14873
14949
  };
14874
14950
  export interface StartRunOptions {
14875
14951
  /** Run with side-effects mocked; the row is marked `dryRun: true`. */
@@ -16054,6 +16130,8 @@ export interface NetworkError {
16054
16130
  readonly code: "network";
16055
16131
  readonly message: string;
16056
16132
  readonly cause?: unknown;
16133
+ /** Set when the client's own timeout ended the request: that timeout, in milliseconds. */
16134
+ readonly timeoutMs?: number;
16057
16135
  }
16058
16136
  export interface AuthError {
16059
16137
  readonly code: "auth";
package/dist/index.d.ts CHANGED
@@ -1775,7 +1775,9 @@ export interface DefineAgentSpec {
1775
1775
  * Required capabilities the agent needs from a `ModelProvider`.
1776
1776
  * Typically one entry: `[{ needs: [{ feature: 'tool-use' }] }]`
1777
1777
  * for a tool-calling agent, `[{ needs: [{ feature: 'structured-output' }] }]`
1778
- * for an agent that returns schema-constrained JSON. The router uses
1778
+ * for an agent that should run on a model that can follow a JSON schema
1779
+ * natively (its typed `output` is still checked by parse and repair, on
1780
+ * every model). The router uses
1779
1781
  * the first entry to pick a compatible provider from the tenant's
1780
1782
  * `ProviderRegistry`.
1781
1783
  */
@@ -3997,6 +3999,12 @@ export interface ClientOptions {
3997
3999
  readonly auth: AuthConfig;
3998
4000
  /** Overridable fetch impl for testing. Defaults to global `fetch`. */
3999
4001
  readonly fetch?: typeof fetch;
4002
+ /**
4003
+ * How long one request may take, in milliseconds, before it fails with
4004
+ * a `network` error. Default 30 000. Streams (`runs.stream` and the
4005
+ * like) aren't bound by it. `runs.start` also takes its own.
4006
+ */
4007
+ readonly timeoutMs?: number;
4000
4008
  }
4001
4009
  /** The verdict of a judgment. */
4002
4010
  export type Verdict = "yes" | "no";
@@ -6909,6 +6917,19 @@ declare namespace Schemas {
6909
6917
  nextCursor?: string;
6910
6918
  hasMore: boolean;
6911
6919
  };
6920
+ /**
6921
+ * How the model thinks before it answers, so a call that wants as little as it allows (a judge's) gets it. Absent: it doesn't think, or nothing is known.
6922
+ */
6923
+ export type ModelThinking = {
6924
+ /**
6925
+ * `adaptive`: on unless turned down. `always`: on, and it can only be lowered.
6926
+ */
6927
+ mode: "adaptive" | "always";
6928
+ /**
6929
+ * The vendor's own setting for the least thinking: for Anthropic `disabled`, `between_tools` or an effort (`low`); for Gemini a thinking level (`low`, `minimal`); for OpenAI a reasoning effort (`low`, `none`).
6930
+ */
6931
+ lowest: string;
6932
+ };
6912
6933
  /**
6913
6934
  * USD per 1K tokens. An adapter may take more rate fields (see the adapter's README).
6914
6935
  */
@@ -6938,6 +6959,11 @@ declare namespace Schemas {
6938
6959
  * Fallback cap on output tokens. Adapters that require `max_tokens` on every request (e.g. Anthropic) use this when `ModelCallInput.maxOutputTokens` is unset.
6939
6960
  */
6940
6961
  maxOutputTokens?: number;
6962
+ /**
6963
+ * Whether the model takes sampling settings (`temperature`). `false`: its API rejects a non-default value, so the call goes without one and the answer's `warnings` say so (`sampling-unsupported`). Absent: it takes them.
6964
+ */
6965
+ sampling?: boolean;
6966
+ thinking?: ModelThinking;
6941
6967
  /**
6942
6968
  * Short per-model description surfaced in logs.
6943
6969
  */
@@ -6953,6 +6979,10 @@ declare namespace Schemas {
6953
6979
  * Models this connection exposes. Non-empty. `models[i].name` must be unique within the list.
6954
6980
  */
6955
6981
  models: Array<ModelInfo>;
6982
+ /**
6983
+ * The model to use when an agent doesn't choose: one of `models[].name`. When candidates rank equally, it comes before the provider's other models; without it, ties break by model name. A preset sets it. A runtime before 0.1.4 ignores it.
6984
+ */
6985
+ defaultModel?: string;
6956
6986
  /**
6957
6987
  * Soft attributes for preference-ranking (`local`, `lower-cost`, `higher-accuracy`, ...). Matched by string equality against `Preference.feature`.
6958
6988
  */
@@ -10091,8 +10121,17 @@ declare const ProviderMetadata: z.ZodObject<{
10091
10121
  }, z.core.$catchall<z.ZodUnknown>>;
10092
10122
  p95LatencyMs: z.ZodOptional<z.ZodNumber>;
10093
10123
  maxOutputTokens: z.ZodOptional<z.ZodNumber>;
10124
+ sampling: z.ZodOptional<z.ZodBoolean>;
10125
+ thinking: z.ZodOptional<z.ZodObject<{
10126
+ mode: z.ZodEnum<{
10127
+ always: "always";
10128
+ adaptive: "adaptive";
10129
+ }>;
10130
+ lowest: z.ZodString;
10131
+ }, z.core.$strict>>;
10094
10132
  description: z.ZodOptional<z.ZodString>;
10095
10133
  }, z.core.$strict>>;
10134
+ defaultModel: z.ZodOptional<z.ZodString>;
10096
10135
  attributes: z.ZodOptional<z.ZodArray<z.ZodString>>;
10097
10136
  description: z.ZodOptional<z.ZodString>;
10098
10137
  capabilityKind: z.ZodOptional<z.ZodString>;
@@ -10128,8 +10167,17 @@ declare const ProviderCollectionPage: z.ZodObject<{
10128
10167
  }, z.core.$catchall<z.ZodUnknown>>;
10129
10168
  p95LatencyMs: z.ZodOptional<z.ZodNumber>;
10130
10169
  maxOutputTokens: z.ZodOptional<z.ZodNumber>;
10170
+ sampling: z.ZodOptional<z.ZodBoolean>;
10171
+ thinking: z.ZodOptional<z.ZodObject<{
10172
+ mode: z.ZodEnum<{
10173
+ always: "always";
10174
+ adaptive: "adaptive";
10175
+ }>;
10176
+ lowest: z.ZodString;
10177
+ }, z.core.$strict>>;
10131
10178
  description: z.ZodOptional<z.ZodString>;
10132
10179
  }, z.core.$strict>>;
10180
+ defaultModel: z.ZodOptional<z.ZodString>;
10133
10181
  attributes: z.ZodOptional<z.ZodArray<z.ZodString>>;
10134
10182
  description: z.ZodOptional<z.ZodString>;
10135
10183
  capabilityKind: z.ZodOptional<z.ZodString>;
@@ -10168,8 +10216,17 @@ declare const RegisterProviderBody: z.ZodObject<{
10168
10216
  }, z.core.$catchall<z.ZodUnknown>>;
10169
10217
  p95LatencyMs: z.ZodOptional<z.ZodNumber>;
10170
10218
  maxOutputTokens: z.ZodOptional<z.ZodNumber>;
10219
+ sampling: z.ZodOptional<z.ZodBoolean>;
10220
+ thinking: z.ZodOptional<z.ZodObject<{
10221
+ mode: z.ZodEnum<{
10222
+ always: "always";
10223
+ adaptive: "adaptive";
10224
+ }>;
10225
+ lowest: z.ZodString;
10226
+ }, z.core.$strict>>;
10171
10227
  description: z.ZodOptional<z.ZodString>;
10172
10228
  }, z.core.$strict>>;
10229
+ defaultModel: z.ZodOptional<z.ZodString>;
10173
10230
  attributes: z.ZodOptional<z.ZodArray<z.ZodString>>;
10174
10231
  description: z.ZodOptional<z.ZodString>;
10175
10232
  capabilityKind: z.ZodOptional<z.ZodString>;
@@ -14696,6 +14753,11 @@ export interface RunsClient {
14696
14753
  * with `options.wait: false` as soon as it exists (202) — poll
14697
14754
  * `get(runId)` until it finishes.
14698
14755
  *
14756
+ * A waited start is bound by the client's timeout (`timeoutMs`, 30 s by
14757
+ * default; this call can set its own). When it runs out, the run may
14758
+ * still be going and its id never arrived: the `network` error says so.
14759
+ * Start a run that can take longer with `options.wait: false`.
14760
+ *
14699
14761
  * `idempotencyKey` makes retries safe: two calls with the same key
14700
14762
  * within the server's retention window return the same `Run`.
14701
14763
  *
@@ -14860,6 +14922,13 @@ export type StartRunInput = {
14860
14922
  readonly input: unknown;
14861
14923
  readonly options?: StartRunOptions;
14862
14924
  readonly idempotencyKey?: string;
14925
+ /**
14926
+ * How long to wait for the answer, in milliseconds: this call's
14927
+ * `ClientOptions.timeoutMs`. A waited start answers only when the run
14928
+ * ends, so a run that can take longer is better started with
14929
+ * `options: { wait: false }` and followed.
14930
+ */
14931
+ readonly timeoutMs?: number;
14863
14932
  } | {
14864
14933
  readonly flow: FlowId | string;
14865
14934
  readonly flowVersion?: string;
@@ -14870,6 +14939,13 @@ export type StartRunInput = {
14870
14939
  readonly input: unknown;
14871
14940
  readonly options?: StartRunOptions;
14872
14941
  readonly idempotencyKey?: string;
14942
+ /**
14943
+ * How long to wait for the answer, in milliseconds: this call's
14944
+ * `ClientOptions.timeoutMs`. A waited start answers only when the run
14945
+ * ends, so a run that can take longer is better started with
14946
+ * `options: { wait: false }` and followed.
14947
+ */
14948
+ readonly timeoutMs?: number;
14873
14949
  };
14874
14950
  export interface StartRunOptions {
14875
14951
  /** Run with side-effects mocked; the row is marked `dryRun: true`. */
@@ -16054,6 +16130,8 @@ export interface NetworkError {
16054
16130
  readonly code: "network";
16055
16131
  readonly message: string;
16056
16132
  readonly cause?: unknown;
16133
+ /** Set when the client's own timeout ended the request: that timeout, in milliseconds. */
16134
+ readonly timeoutMs?: number;
16057
16135
  }
16058
16136
  export interface AuthError {
16059
16137
  readonly code: "auth";
package/dist/index.js CHANGED
@@ -2892,7 +2892,157 @@ function subscribeToRun(options) {
2892
2892
  });
2893
2893
  }
2894
2894
 
2895
+ // src/transport.ts
2896
+ var DEFAULT_TIMEOUT_MS = 3e4;
2897
+ var MUTATING = /* @__PURE__ */ new Set([
2898
+ "POST",
2899
+ "PUT",
2900
+ "PATCH",
2901
+ "DELETE"
2902
+ ]);
2903
+ function createTransport(options) {
2904
+ const apiUrl = options.apiUrl.replace(/\/+$/u, "");
2905
+ const fetchImpl = options.fetch ?? fetch;
2906
+ const clientTimeoutMs = checkedTimeoutMs(
2907
+ options.timeoutMs ?? DEFAULT_TIMEOUT_MS,
2908
+ "ClientOptions.timeoutMs"
2909
+ );
2910
+ return {
2911
+ apiUrl,
2912
+ fetchImpl,
2913
+ authHeaders() {
2914
+ return { Authorization: `Bearer ${authTokenFor(options.auth)}` };
2915
+ },
2916
+ async request(input) {
2917
+ const url = buildUrl(apiUrl, input.path, input.query);
2918
+ const headers = buildHeaders(input, options.auth);
2919
+ const timeoutMs = input.timeoutMs === void 0 ? clientTimeoutMs : checkedTimeoutMs(input.timeoutMs, "timeoutMs");
2920
+ const ac = new AbortController();
2921
+ let timedOut = false;
2922
+ const timer = setTimeout(() => {
2923
+ timedOut = true;
2924
+ ac.abort(new Error("timeout"));
2925
+ }, timeoutMs);
2926
+ let response;
2927
+ try {
2928
+ response = await fetchImpl(url, {
2929
+ method: input.method,
2930
+ headers,
2931
+ ...input.body !== void 0 && { body: JSON.stringify(input.body) },
2932
+ signal: ac.signal
2933
+ });
2934
+ } catch (cause) {
2935
+ clearTimeout(timer);
2936
+ const err = timedOut ? {
2937
+ code: "network",
2938
+ message: `No answer within ${seconds(timeoutMs)}, the client's timeout (timeoutMs).`,
2939
+ cause,
2940
+ timeoutMs
2941
+ } : {
2942
+ code: "network",
2943
+ message: cause instanceof Error ? cause.message : "network request failed",
2944
+ cause
2945
+ };
2946
+ throw new KindgiApiError(err);
2947
+ }
2948
+ clearTimeout(timer);
2949
+ if (response.ok) {
2950
+ if (input.discardResponse === true || response.status === 204) {
2951
+ try {
2952
+ await response.arrayBuffer();
2953
+ } catch {
2954
+ }
2955
+ return void 0;
2956
+ }
2957
+ try {
2958
+ return await response.json();
2959
+ } catch (cause) {
2960
+ const err = {
2961
+ code: "network",
2962
+ message: "Response body was not valid JSON",
2963
+ cause
2964
+ };
2965
+ throw new KindgiApiError(err);
2966
+ }
2967
+ }
2968
+ let body;
2969
+ try {
2970
+ body = await response.json();
2971
+ } catch {
2972
+ body = void 0;
2973
+ }
2974
+ throw new KindgiApiError(
2975
+ fromWire(unwrapErrorEnvelope(body, response.status), response.status)
2976
+ );
2977
+ }
2978
+ };
2979
+ }
2980
+ function checkedTimeoutMs(value, name) {
2981
+ if (!Number.isFinite(value) || value <= 0) {
2982
+ throw new TypeError(`${name} must be a positive number of milliseconds. Got ${String(value)}.`);
2983
+ }
2984
+ return value;
2985
+ }
2986
+ function seconds(ms) {
2987
+ return `${ms / 1e3} s`;
2988
+ }
2989
+ function buildUrl(apiUrl, path, query) {
2990
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
2991
+ const base = `${apiUrl}${normalizedPath}`;
2992
+ if (query === void 0) return base;
2993
+ const params = [];
2994
+ for (const key of Object.keys(query)) {
2995
+ const value = query[key];
2996
+ if (value === void 0) continue;
2997
+ const values = typeof value === "object" ? value : [String(value)];
2998
+ for (const v of values) params.push(`${encodeURIComponent(key)}=${encodeURIComponent(v)}`);
2999
+ }
3000
+ return params.length === 0 ? base : `${base}?${params.join("&")}`;
3001
+ }
3002
+ function buildHeaders(input, auth) {
3003
+ const headers = {
3004
+ Accept: "application/json",
3005
+ Authorization: `Bearer ${authTokenFor(auth)}`
3006
+ };
3007
+ if (input.body !== void 0) {
3008
+ headers["Content-Type"] = "application/json; charset=utf-8";
3009
+ }
3010
+ if (input.idempotencyKey !== void 0 && MUTATING.has(input.method)) {
3011
+ headers["Idempotency-Key"] = input.idempotencyKey;
3012
+ }
3013
+ if (input.headers !== void 0) {
3014
+ for (const key of Object.keys(input.headers)) {
3015
+ headers[key] = input.headers[key];
3016
+ }
3017
+ }
3018
+ return headers;
3019
+ }
3020
+ function authTokenFor(auth) {
3021
+ if (auth.kind === "apiToken") return auth.token;
3022
+ return auth.accessToken;
3023
+ }
3024
+ function unwrapErrorEnvelope(body, status) {
3025
+ if (body !== null && typeof body === "object" && !Array.isArray(body)) {
3026
+ const inner = body.error;
3027
+ if (inner !== null && typeof inner === "object" && !Array.isArray(inner)) {
3028
+ return inner;
3029
+ }
3030
+ }
3031
+ return { code: "unknown", message: `HTTP ${status} without recognizable error envelope` };
3032
+ }
3033
+
2895
3034
  // src/resources/runs.ts
3035
+ function waitedStartTimeout(e) {
3036
+ if (!(e instanceof KindgiApiError) || e.error.code !== "network") return e;
3037
+ const { timeoutMs } = e.error;
3038
+ if (timeoutMs === void 0) return e;
3039
+ return new KindgiApiError({
3040
+ code: "network",
3041
+ message: `The run didn't end within ${seconds(timeoutMs)}, the client's timeout (timeoutMs). A waited start answers only when the run ends, so the run may still be going, and its id didn't arrive. Start a run that can take longer with \`options: { wait: false }\`: the answer carries its id at once. Then follow it with \`runs.stream(runId)\` or \`runs.get(runId)\`. Or raise \`timeoutMs\`.`,
3042
+ cause: e.error.cause,
3043
+ timeoutMs
3044
+ });
3045
+ }
2896
3046
  function makeRunsClient(transport) {
2897
3047
  return {
2898
3048
  async start(input) {
@@ -2911,14 +3061,19 @@ function makeRunsClient(transport) {
2911
3061
  input: input.input,
2912
3062
  ...input.options !== void 0 && { options: input.options }
2913
3063
  };
2914
- return transport.request({
2915
- method: "POST",
2916
- path: "/v1/runs",
2917
- body,
2918
- ...input.idempotencyKey !== void 0 && {
2919
- idempotencyKey: input.idempotencyKey
2920
- }
2921
- });
3064
+ try {
3065
+ return await transport.request({
3066
+ method: "POST",
3067
+ path: "/v1/runs",
3068
+ body,
3069
+ ...input.idempotencyKey !== void 0 && {
3070
+ idempotencyKey: input.idempotencyKey
3071
+ },
3072
+ ...input.timeoutMs !== void 0 && { timeoutMs: input.timeoutMs }
3073
+ });
3074
+ } catch (e) {
3075
+ throw input.options?.wait === false ? e : waitedStartTimeout(e);
3076
+ }
2922
3077
  },
2923
3078
  async dryRun(_input) {
2924
3079
  throw new KindgiApiError(
@@ -4048,127 +4203,6 @@ function makeWebhooksClient(transport) {
4048
4203
  };
4049
4204
  }
4050
4205
 
4051
- // src/transport.ts
4052
- var DEFAULT_TIMEOUT_MS = 3e4;
4053
- var MUTATING = /* @__PURE__ */ new Set([
4054
- "POST",
4055
- "PUT",
4056
- "PATCH",
4057
- "DELETE"
4058
- ]);
4059
- function createTransport(options) {
4060
- const apiUrl = options.apiUrl.replace(/\/+$/u, "");
4061
- const fetchImpl = options.fetch ?? fetch;
4062
- const clientTimeoutMs = DEFAULT_TIMEOUT_MS;
4063
- return {
4064
- apiUrl,
4065
- fetchImpl,
4066
- authHeaders() {
4067
- return { Authorization: `Bearer ${authTokenFor(options.auth)}` };
4068
- },
4069
- async request(input) {
4070
- const url = buildUrl(apiUrl, input.path, input.query);
4071
- const headers = buildHeaders(input, options.auth);
4072
- const timeoutMs = input.timeoutMs ?? clientTimeoutMs;
4073
- const ac = new AbortController();
4074
- const timer = setTimeout(
4075
- () => ac.abort(new Error("timeout")),
4076
- timeoutMs
4077
- );
4078
- let response;
4079
- try {
4080
- response = await fetchImpl(url, {
4081
- method: input.method,
4082
- headers,
4083
- ...input.body !== void 0 && { body: JSON.stringify(input.body) },
4084
- signal: ac.signal
4085
- });
4086
- } catch (cause) {
4087
- clearTimeout(timer);
4088
- const err = {
4089
- code: "network",
4090
- message: cause instanceof Error ? cause.message : "network request failed",
4091
- cause
4092
- };
4093
- throw new KindgiApiError(err);
4094
- }
4095
- clearTimeout(timer);
4096
- if (response.ok) {
4097
- if (input.discardResponse === true || response.status === 204) {
4098
- try {
4099
- await response.arrayBuffer();
4100
- } catch {
4101
- }
4102
- return void 0;
4103
- }
4104
- try {
4105
- return await response.json();
4106
- } catch (cause) {
4107
- const err = {
4108
- code: "network",
4109
- message: "Response body was not valid JSON",
4110
- cause
4111
- };
4112
- throw new KindgiApiError(err);
4113
- }
4114
- }
4115
- let body;
4116
- try {
4117
- body = await response.json();
4118
- } catch {
4119
- body = void 0;
4120
- }
4121
- throw new KindgiApiError(
4122
- fromWire(unwrapErrorEnvelope(body, response.status), response.status)
4123
- );
4124
- }
4125
- };
4126
- }
4127
- function buildUrl(apiUrl, path, query) {
4128
- const normalizedPath = path.startsWith("/") ? path : `/${path}`;
4129
- const base = `${apiUrl}${normalizedPath}`;
4130
- if (query === void 0) return base;
4131
- const params = [];
4132
- for (const key of Object.keys(query)) {
4133
- const value = query[key];
4134
- if (value === void 0) continue;
4135
- const values = typeof value === "object" ? value : [String(value)];
4136
- for (const v of values) params.push(`${encodeURIComponent(key)}=${encodeURIComponent(v)}`);
4137
- }
4138
- return params.length === 0 ? base : `${base}?${params.join("&")}`;
4139
- }
4140
- function buildHeaders(input, auth) {
4141
- const headers = {
4142
- Accept: "application/json",
4143
- Authorization: `Bearer ${authTokenFor(auth)}`
4144
- };
4145
- if (input.body !== void 0) {
4146
- headers["Content-Type"] = "application/json; charset=utf-8";
4147
- }
4148
- if (input.idempotencyKey !== void 0 && MUTATING.has(input.method)) {
4149
- headers["Idempotency-Key"] = input.idempotencyKey;
4150
- }
4151
- if (input.headers !== void 0) {
4152
- for (const key of Object.keys(input.headers)) {
4153
- headers[key] = input.headers[key];
4154
- }
4155
- }
4156
- return headers;
4157
- }
4158
- function authTokenFor(auth) {
4159
- if (auth.kind === "apiToken") return auth.token;
4160
- return auth.accessToken;
4161
- }
4162
- function unwrapErrorEnvelope(body, status) {
4163
- if (body !== null && typeof body === "object" && !Array.isArray(body)) {
4164
- const inner = body.error;
4165
- if (inner !== null && typeof inner === "object" && !Array.isArray(inner)) {
4166
- return inner;
4167
- }
4168
- }
4169
- return { code: "unknown", message: `HTTP ${status} without recognizable error envelope` };
4170
- }
4171
-
4172
4206
  // src/client.ts
4173
4207
  function createClient(options) {
4174
4208
  const transport = createTransport(options);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kindgi/client",
3
- "version": "0.1.4-rc.5",
3
+ "version": "0.1.4",
4
4
  "description": "TypeScript client SDK for Kindgi™ — the sovereign AI OS. Preview: wire-generated from openapi.json via typed-openapi, ergonomic hand-authored resource clients (Transport, KindgiApiError, SSE with Last-Event-Id resume).",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -45,12 +45,12 @@
45
45
  "zod": "^4.6.5"
46
46
  },
47
47
  "devDependencies": {
48
- "@kindgi/agents": "0.1.4-rc.5",
49
- "@kindgi/api": "0.1.4-rc.5",
50
- "@kindgi/flow": "0.1.4-rc.5",
51
- "@kindgi/platform": "0.1.4-rc.5",
52
- "@kindgi/runtime": "0.1.4-rc.5",
53
- "@kindgi/types": "0.1.4-rc.5",
48
+ "@kindgi/agents": "0.1.4",
49
+ "@kindgi/api": "0.1.4",
50
+ "@kindgi/flow": "0.1.4",
51
+ "@kindgi/platform": "0.1.4",
52
+ "@kindgi/runtime": "0.1.4",
53
+ "@kindgi/types": "0.1.4",
54
54
  "@types/node": "^22.10.5",
55
55
  "dts-bundle-generator": "^9.5.1",
56
56
  "tsup": "^8.5.1",