@alvin0/ai-agent-sdk-provider-typesafe 0.1.10

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 alvin0 (chaulamdinhai) <chaulamdinhai@gmail.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,146 @@
1
+ # TypeSafe provider
2
+
3
+ Runtime: **Universal** (Node, browser, and Web-standard workers).
4
+
5
+ ```sh
6
+ pnpm add @alvin0/ai-agent-sdk-core @alvin0/ai-agent-sdk-decision-adapter @alvin0/ai-agent-sdk-provider-typesafe
7
+ ```
8
+
9
+ TypeSafe Jev adapter for typed decisions. Implements `POST /v1/systemone` and
10
+ `GET /v1/models`; it does not register Jev as a chat-generation model.
11
+
12
+ ```ts
13
+ import {
14
+ createDecisionRuntime, choiceQuestion, scoreQuestion, booleanQuestion,
15
+ } from '@alvin0/ai-agent-sdk-decision-adapter'
16
+ import { typesafePlugin } from '@alvin0/ai-agent-sdk-provider-typesafe'
17
+ import { envCredential } from '@alvin0/ai-agent-sdk-auth-node/env'
18
+
19
+ const runtime = createDecisionRuntime({ providers: [typesafePlugin({
20
+ apiKey: envCredential('TYPESAFE_API_KEY'),
21
+ })] })
22
+ try {
23
+ const result = await runtime.decisionModel({
24
+ provider: 'typesafe', model: 'jev-latest',
25
+ }).evaluate({
26
+ state: { message: 'My payouts have failed for three days. Please help today.' },
27
+ questions: {
28
+ department: choiceQuestion('Which department should handle this?', {
29
+ billing: 'Payments and refunds', support: 'Technical bugs',
30
+ }),
31
+ urgency: scoreQuestion('How urgent is this?', ['Routine', 'Urgent', 'Critical']),
32
+ needsAttention: booleanQuestion('Does this require prompt attention?'),
33
+ },
34
+ })
35
+ console.log(result.model, result.answers, result.usage)
36
+ } finally {
37
+ await runtime.close()
38
+ }
39
+ ```
40
+
41
+ Install `@alvin0/ai-agent-sdk-auth-node` separately for the Node environment-key
42
+ example. The universal provider does not read environment variables; it accepts
43
+ a string or the SDK's versioned `CredentialSource`, resolved once per attempt.
44
+ Never embed a server API key in client-side code. Use a server endpoint/proxy for
45
+ browser applications.
46
+
47
+ ## Configure tasks for different consumers
48
+
49
+ Use `createDecisionTask(runtime.decisionModel(target), { questions, timeoutMs })`
50
+ to inject a reusable typed task into a service handler, agent tool, CLI or batch
51
+ job. For request-scoped accounting, pass `ModelInvocationContext` to
52
+ `task.evaluate(state, options, context)` or `task.evaluateBatch(states, { context })`.
53
+ Independent questions share a request; different states use bounded client-side
54
+ batching. [Workflow examples](../decision-adapter/USE_CASES.md) cover routing,
55
+ verification, reranking and sequential decisions.
56
+
57
+ Separate accounts or endpoints can coexist with named routes:
58
+
59
+ ```ts
60
+ const runtime = createDecisionRuntime({ providers: [
61
+ typesafePlugin({
62
+ id: 'typesafe-primary', routes: ['primary'],
63
+ apiKey: envCredential('TYPESAFE_API_KEY'),
64
+ }),
65
+ typesafePlugin({
66
+ id: 'typesafe-secondary', routes: ['secondary'],
67
+ apiKey: envCredential('TYPESAFE_SECONDARY_API_KEY'),
68
+ }),
69
+ ] })
70
+ const primary = runtime.decisionModel({ provider: 'primary', model: 'jev-1.13.0' })
71
+ const secondary = runtime.decisionModel({ provider: 'secondary', model: 'jev-latest' })
72
+ // Application code chooses which task/handle to call, and when to fall back.
73
+ ```
74
+
75
+ For workers, inject the host's secret binding as `apiKey`; for tests, inject `fetch`.
76
+ Native Noul answers close to 0.5 are uncertain. Use
77
+ `gateBoolean(answer, { falseMax: 0.2, trueMin: 0.8 })` when the workflow needs an
78
+ explicit review interval; tune those example thresholds using labeled data.
79
+ Choice routing can use `gateChoice` with probability, margin and confidence
80
+ thresholds. Provider confidence describes a distribution; it does not prove
81
+ that the business decision is correct.
82
+
83
+ ## Mapping and options
84
+
85
+ | SDK question | TypeSafe wire type | Returned evidence |
86
+ | --- | --- | --- |
87
+ | `choice` with `options` | `choice` with `criteria` | choice, probabilities, confidence |
88
+ | `score` with `levels` | `score` with `criteria` | fractional score, probabilities, confidence |
89
+ | `boolean` | `noul` | probabilityTrue; value is probabilityTrue >= 0.5 |
90
+
91
+ All native evidence has `probabilitySource: 'provider'`. Noul does not supply
92
+ confidence; the adapter keeps it absent. The actual response model ID is retained
93
+ even when the request uses an alias. New/pinned model IDs need no SDK release.
94
+
95
+ `typesafeAdapter(options)` supports direct use and catalog discovery.
96
+ `typesafePlugin(options)` installs it into the decision runtime. Options include
97
+ `apiKey`, `baseUrl` (default `https://api.typesafe.ai/v1`), injected `fetch`, custom
98
+ headers, `requestTimeoutMs` (30 seconds), `maxRequestBytes` (2 MiB),
99
+ `maxResponseBytes` (4 MiB), and `retryPolicy`. Plugins also accept `id` and `routes`
100
+ (default `['typesafe']`). Local HTTP requires explicit `allowInsecureHttp: true`.
101
+ Connection options are captured at construction. Redirects are rejected, custom
102
+ headers cannot override authentication/transport headers, and errors omit raw
103
+ response bodies and credentials. Per-call `ModelInvocationContext.providerOptions.headers`
104
+ override custom route headers and are captured before preparation/credentials,
105
+ then reused through retries. Authentication and transport headers remain reserved.
106
+ Nonempty `providerOptions.body` overrides are rejected with `INVALID_REQUEST`;
107
+ configure native questions/state/model through the typed decision request.
108
+
109
+ When `retryPolicy` is omitted, the runtime policy applies; an explicit provider
110
+ policy overrides it. Direct calls perform one attempt and honor per-call
111
+ `timeoutMs`, bounded also by `requestTimeoutMs`, during credentials and transport.
112
+ Prepared calls reuse the captured JSON body across retries; tasks share compiled
113
+ rubrics without caching decisions, credentials or connections across calls.
114
+ Valid usage is retained in provider-attempt reports even if answer validation
115
+ fails. Invalid or overflowing usage counters fail validation and are not reported
116
+ as trustworthy billing evidence. Each physical attempt is closed at most once,
117
+ including when its accounting callback throws. Late credentials/admission do not
118
+ dispatch after cancellation; late fetch responses are cancelled without reporting
119
+ invented usage or closing the attempt again.
120
+
121
+ Choice accepts 2–255 options; Score accepts 2–10 ordered levels. Invalid requests
122
+ fail before network IO. Responses are byte-bounded and validated; score legends
123
+ must match the ordered rubric, including object/array descriptions. The adapter
124
+ performs one attempt; the runtime applies retries for transient rate limiting,
125
+ overload, timeout and transport errors. HTTP 401/403 map to `AUTH`, 422 to
126
+ `INVALID_REQUEST`, 429 to `RATE_LIMIT`, and 529 to `SERVER`; `Retry-After` is honored
127
+ within the retry policy's delay ceiling.
128
+
129
+ ## Verification
130
+
131
+ Offline: `pnpm --filter @alvin0/ai-agent-sdk-provider-typesafe test`.
132
+ Installed tarballs: `pnpm --filter @alvin0/ai-agent-sdk-provider-typesafe test:pack`.
133
+ Live acceptance from the workspace root: set `TYPESAFE_API_KEY` in `.env`, build
134
+ the packages, then run `pnpm human:decision`. The runner prints English/Vietnamese
135
+ decision results, usage, model IDs and latency; it never prints the API key.
136
+ Optionally set `TYPESAFE_MODEL` to pin a model ID.
137
+ Run `pnpm test:decision-live` from the workspace root for paid TypeSafe accounting
138
+ checks. They inject an invalid choice after a real response and an overload before
139
+ a real retry, verifying billed usage and reuse of the captured request. A structured
140
+ score case sends invocation headers to the real API, then rejects a reversed
141
+ legend injected into that response while retaining usage evidence. A terminal audit
142
+ rejection after a real billed response verifies one request and one attempt closure,
143
+ even when provider retries are enabled.
144
+
145
+ API references: [Evaluation](https://docs.typesafe.ai/api),
146
+ [Models](https://docs.typesafe.ai/models), [Confidence](https://docs.typesafe.ai/confidence).
@@ -0,0 +1,25 @@
1
+ import { CredentialInput, RetryPolicyConfig } from "@alvin0/ai-agent-sdk-core/provider";
2
+ import { DecisionAdapter, DecisionProviderPlugin } from "@alvin0/ai-agent-sdk-decision-adapter";
3
+ //#region src/adapter.d.ts
4
+ interface TypesafeAdapterOptions {
5
+ readonly apiKey: CredentialInput;
6
+ /** API root including /v1. Default: https://api.typesafe.ai/v1 */
7
+ readonly baseUrl?: string | URL;
8
+ readonly fetch?: typeof globalThis.fetch;
9
+ readonly headers?: Readonly<Record<string, string>>;
10
+ readonly requestTimeoutMs?: number;
11
+ readonly maxRequestBytes?: number;
12
+ readonly maxResponseBytes?: number;
13
+ readonly retryPolicy?: RetryPolicyConfig;
14
+ /** For explicitly configured development endpoints only. */
15
+ readonly allowInsecureHttp?: boolean;
16
+ }
17
+ interface TypesafePluginOptions extends TypesafeAdapterOptions {
18
+ readonly id?: string;
19
+ readonly routes?: readonly string[];
20
+ }
21
+ declare function typesafeAdapter(options: TypesafeAdapterOptions): DecisionAdapter;
22
+ declare function typesafePlugin(options: TypesafePluginOptions): DecisionProviderPlugin;
23
+ //#endregion
24
+ export { type TypesafeAdapterOptions, type TypesafePluginOptions, typesafeAdapter, typesafePlugin };
25
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/adapter.ts"],"mappings":";;;UAQiB;WACN,QAAQ;;WAER,mBAAmB;WACnB,eAAe,WAAW;WAC1B,UAAU,SAAS;WACnB;WACA;WACA;WACA,cAAc;;WAEd;;UAEM,8BAA8B;WACpC;WACA;;iBAiRK,gBAAgB,SAAS,yBAAyB;iBAClD,eAAe,SAAS,wBAAwB"}
package/dist/index.js ADDED
@@ -0,0 +1,434 @@
1
+ import { MODEL_ERROR_CODES, ModelError, ProviderRequestId, assertUsableApiKey, attributionHeaders, normalizeModelFailure, resolveRetryPolicy, validateUsageCounters } from "@alvin0/ai-agent-sdk-core";
2
+ import { CREDENTIAL_CAPABILITY_API_VERSION } from "@alvin0/ai-agent-sdk-core/provider";
3
+ import { DecisionAdapter, defineDecisionProviderPlugin, snapshotDecisionInput, validateDecisionResult } from "@alvin0/ai-agent-sdk-decision-adapter";
4
+ import { abortable, throwIfAborted } from "@alvin0/ai-agent-sdk-decision-adapter/transport";
5
+
6
+ //#region src/adapter.ts
7
+ const CAPABILITIES = Object.freeze({
8
+ questionTypes: Object.freeze([
9
+ "choice",
10
+ "score",
11
+ "boolean"
12
+ ]),
13
+ maxChoiceOptions: 255,
14
+ maxScoreLevels: 10
15
+ });
16
+ const NULL_LOGGER = Object.freeze({
17
+ child: () => NULL_LOGGER,
18
+ trace() {},
19
+ debug() {},
20
+ info() {},
21
+ warn() {},
22
+ error() {},
23
+ fatal() {}
24
+ });
25
+ function object(value) {
26
+ if (value === null || typeof value !== "object" || Array.isArray(value)) throw new ModelError("TypeSafe returned an invalid response object", MODEL_ERROR_CODES.MALFORMED_RESPONSE);
27
+ return value;
28
+ }
29
+ /** Compare JSON rubric values without depending on object property order. */
30
+ function sameDescription(expected, actual) {
31
+ if (expected === actual) return true;
32
+ if (expected === null || actual === null || typeof expected !== "object" || typeof actual !== "object") return false;
33
+ if (Array.isArray(expected)) return Array.isArray(actual) && expected.length === actual.length && expected.every((value, index) => sameDescription(value, actual[index]));
34
+ if (Array.isArray(actual)) return false;
35
+ const keys = Object.keys(expected);
36
+ return keys.length === Object.keys(actual).length && keys.every((key) => Object.hasOwn(actual, key) && sameDescription(expected[key], actual[key]));
37
+ }
38
+ function capturePublicHeaders(source) {
39
+ let headers;
40
+ try {
41
+ headers = Object.freeze(Object.fromEntries(new Headers(source).entries()));
42
+ } catch {
43
+ throw new ModelError("Invalid TypeSafe custom headers", MODEL_ERROR_CODES.INVALID_REQUEST);
44
+ }
45
+ for (const key of Object.keys(headers)) if ([
46
+ "authorization",
47
+ "content-type",
48
+ "accept",
49
+ "host",
50
+ "content-length",
51
+ "traceparent"
52
+ ].includes(key)) throw new ModelError("TypeSafe custom headers cannot override transport headers", MODEL_ERROR_CODES.INVALID_REQUEST);
53
+ return headers;
54
+ }
55
+ function captureInvocationHeaders(context) {
56
+ if (context?.providerOptions?.body !== void 0 && Object.keys(context.providerOptions.body).length) throw new ModelError("TypeSafe does not support providerOptions.body overrides", MODEL_ERROR_CODES.INVALID_REQUEST);
57
+ return capturePublicHeaders(context?.providerOptions?.headers);
58
+ }
59
+ function bound(value, fallback, name) {
60
+ const result = value ?? fallback;
61
+ if (!Number.isSafeInteger(result) || result <= 0 || result > 2147483647) throw new Error(`Invalid TypeSafe ${name}`);
62
+ return result;
63
+ }
64
+ function responseUsage(value) {
65
+ const raw = object(value).usage;
66
+ if (raw === void 0) return void 0;
67
+ const source = object(raw);
68
+ const counters = {
69
+ ...source.input_tokens === void 0 ? {} : { inputTokens: source.input_tokens },
70
+ ...source.output_tokens === void 0 ? {} : { outputTokens: source.output_tokens }
71
+ };
72
+ const validated = validateUsageCounters(counters);
73
+ if (validated.invalidFields.length || validated.overflow) throw new ModelError("Invalid TypeSafe usage counters", MODEL_ERROR_CODES.MALFORMED_RESPONSE);
74
+ return validated.reported;
75
+ }
76
+ function retryAfter(value) {
77
+ if (value === null) return void 0;
78
+ const seconds = Number(value);
79
+ const delay = Number.isFinite(seconds) ? seconds * 1e3 : Date.parse(value) - Date.now();
80
+ return Number.isFinite(delay) && delay > 0 ? delay : void 0;
81
+ }
82
+ function statusCode(status) {
83
+ if (status === 401 || status === 403) return MODEL_ERROR_CODES.AUTH;
84
+ if (status === 429) return MODEL_ERROR_CODES.RATE_LIMIT;
85
+ if (status >= 500) return MODEL_ERROR_CODES.SERVER;
86
+ return MODEL_ERROR_CODES.INVALID_REQUEST;
87
+ }
88
+ async function readJson(response, maxBytes, signal) {
89
+ if (Number(response.headers.get("content-length")) > maxBytes) {
90
+ response.body?.cancel().catch(() => {});
91
+ throw new ModelError("TypeSafe response exceeds byte limit", MODEL_ERROR_CODES.MALFORMED_RESPONSE);
92
+ }
93
+ if (!response.body) throw new ModelError("TypeSafe returned an empty response", MODEL_ERROR_CODES.MALFORMED_RESPONSE);
94
+ const reader = response.body.getReader();
95
+ const decoder = new TextDecoder("utf-8", { fatal: true });
96
+ let bytes = 0;
97
+ let text = "";
98
+ try {
99
+ for (;;) {
100
+ const item = await abortable(reader.read(), signal);
101
+ if (item.done) break;
102
+ bytes += item.value.byteLength;
103
+ if (bytes > maxBytes) throw new ModelError("TypeSafe response exceeds byte limit", MODEL_ERROR_CODES.MALFORMED_RESPONSE);
104
+ text += decoder.decode(item.value, { stream: true });
105
+ }
106
+ text += decoder.decode();
107
+ try {
108
+ return JSON.parse(text);
109
+ } catch {
110
+ throw new ModelError("TypeSafe returned invalid JSON", MODEL_ERROR_CODES.MALFORMED_RESPONSE);
111
+ }
112
+ } catch (error) {
113
+ throwIfAborted(signal);
114
+ if (error instanceof ModelError) throw error;
115
+ throw new ModelError("TypeSafe returned an unreadable JSON response", MODEL_ERROR_CODES.MALFORMED_RESPONSE);
116
+ } finally {
117
+ reader.cancel().catch(() => {});
118
+ }
119
+ }
120
+ var TypesafeDecisionAdapter = class extends DecisionAdapter {
121
+ #baseUrl;
122
+ #credential;
123
+ #fetch;
124
+ #headers;
125
+ #timeout;
126
+ #maxRequest;
127
+ #maxResponse;
128
+ #retry;
129
+ #rubrics = /* @__PURE__ */ new WeakMap();
130
+ constructor(options) {
131
+ super();
132
+ const url = new URL(options.baseUrl ?? "https://api.typesafe.ai/v1");
133
+ if (url.username || url.password || url.search || url.hash || url.protocol !== "https:" && !(url.protocol === "http:" && options.allowInsecureHttp === true)) throw new Error("TypeSafe requires an HTTPS API root without credentials, query or fragment");
134
+ this.#baseUrl = url.href.replace(/\/$/, "");
135
+ this.#fetch = options.fetch ?? globalThis.fetch;
136
+ this.#headers = capturePublicHeaders(options.headers);
137
+ this.#timeout = bound(options.requestTimeoutMs, 3e4, "requestTimeoutMs");
138
+ this.#maxRequest = bound(options.maxRequestBytes, 2097152, "maxRequestBytes");
139
+ this.#maxResponse = bound(options.maxResponseBytes, 4194304, "maxResponseBytes");
140
+ this.#retry = options.retryPolicy === void 0 ? void 0 : resolveRetryPolicy(options.retryPolicy, "typesafe.retryPolicy");
141
+ if (typeof options.apiKey === "string") this.#credential = assertUsableApiKey(options.apiKey, "TypeSafe", "apiKey");
142
+ else {
143
+ const source = options.apiKey;
144
+ if (source?.kind !== "credential-source" || source.apiVersion !== CREDENTIAL_CAPABILITY_API_VERSION || typeof source.resolve !== "function") throw new Error("TypeSafe requires a compatible credential source");
145
+ const resolve = source.resolve.bind(source);
146
+ this.#credential = async (signal, logger) => assertUsableApiKey(await resolve({
147
+ signal,
148
+ logger
149
+ }), "TypeSafe", "credential source");
150
+ }
151
+ }
152
+ providerRetryPolicy() {
153
+ return this.#retry;
154
+ }
155
+ async resolveModel(provider, model) {
156
+ return Object.freeze({
157
+ provider,
158
+ id: model,
159
+ name: model,
160
+ capabilities: CAPABILITIES
161
+ });
162
+ }
163
+ async prepareDecisionCall(provider, model, _signal, context) {
164
+ const headers = captureInvocationHeaders(context);
165
+ let captured;
166
+ return Object.freeze({
167
+ model: await this.resolveModel(provider, model),
168
+ evaluate: (request, invocation = context) => {
169
+ if (request.provider !== provider || request.model !== model) throw new ModelError("Prepared TypeSafe decision target does not match request", MODEL_ERROR_CODES.INVALID_REQUEST);
170
+ if (captured && captured.request !== request) throw new ModelError("Prepared TypeSafe decision call cannot dispatch a different request", MODEL_ERROR_CODES.INVALID_REQUEST);
171
+ captured ??= {
172
+ request,
173
+ value: this.#capture(request, invocation === context ? headers : captureInvocationHeaders(invocation))
174
+ };
175
+ return this.#evaluate(captured.value, invocation);
176
+ }
177
+ });
178
+ }
179
+ async listModels(provider, signal) {
180
+ const models = object((await this.#dispatch(provider, "catalog", "models", void 0, signal)).value).models;
181
+ if (!Array.isArray(models) || models.length > 1024) throw new ModelError("Invalid TypeSafe model catalog", MODEL_ERROR_CODES.MALFORMED_RESPONSE);
182
+ return Object.freeze(models.map((entry) => {
183
+ const info = object(entry);
184
+ if (typeof info.name !== "string" || !info.name.trim() || info.name.length > 256) throw new ModelError("Invalid TypeSafe model id", MODEL_ERROR_CODES.MALFORMED_RESPONSE);
185
+ return Object.freeze({
186
+ provider,
187
+ id: info.name,
188
+ name: info.name,
189
+ capabilities: CAPABILITIES
190
+ });
191
+ }));
192
+ }
193
+ async evaluate(request, context) {
194
+ return this.#evaluate(this.#capture(request, captureInvocationHeaders(context)), context);
195
+ }
196
+ #capture(request, headers) {
197
+ if (typeof request.provider !== "string" || !request.provider.trim() || typeof request.model !== "string" || !request.model.trim()) throw new ModelError("TypeSafe requires provider and model identifiers", MODEL_ERROR_CODES.INVALID_REQUEST);
198
+ const input = snapshotDecisionInput(request, CAPABILITIES);
199
+ const timeout = Math.min(this.#timeout, input.timeoutMs ?? this.#timeout);
200
+ let questions = this.#rubrics.get(input.questions);
201
+ if (questions === void 0) {
202
+ questions = JSON.stringify(Object.fromEntries(Object.entries(input.questions).map(([id, question]) => {
203
+ switch (question.type) {
204
+ case "choice": return [id, {
205
+ type: "choice",
206
+ instructions: question.instructions,
207
+ criteria: question.options
208
+ }];
209
+ case "score": return [id, {
210
+ type: "score",
211
+ instructions: question.instructions,
212
+ criteria: question.levels
213
+ }];
214
+ case "boolean": return [id, {
215
+ type: "noul",
216
+ instructions: question.instructions,
217
+ ...question.criteria === void 0 ? {} : { criteria: question.criteria }
218
+ }];
219
+ }
220
+ })));
221
+ this.#rubrics.set(input.questions, questions);
222
+ }
223
+ const wire = `{"questions":${questions},"model":${JSON.stringify(request.model)},"state":${JSON.stringify(input.state)}}`;
224
+ if (new TextEncoder().encode(wire).byteLength > this.#maxRequest) throw new ModelError("TypeSafe request exceeds byte limit", MODEL_ERROR_CODES.INVALID_REQUEST);
225
+ return {
226
+ input,
227
+ wire,
228
+ timeout,
229
+ provider: request.provider,
230
+ model: request.model,
231
+ headers
232
+ };
233
+ }
234
+ async #evaluate(captured, context) {
235
+ const { input, wire, timeout, provider, model } = captured;
236
+ const response = await this.#dispatch(provider, model, "systemone", wire, input.signal, context, (value) => {
237
+ const raw = object(value);
238
+ const rawAnswers = object(raw.answers);
239
+ if (Object.keys(rawAnswers).length !== Object.keys(input.questions).length) throw new ModelError("Unexpected TypeSafe answer ids", MODEL_ERROR_CODES.MALFORMED_RESPONSE);
240
+ const answers = Object.create(null);
241
+ for (const [id, question] of Object.entries(input.questions)) {
242
+ if (!Object.hasOwn(rawAnswers, id)) throw new ModelError("Missing TypeSafe answer", MODEL_ERROR_CODES.MALFORMED_RESPONSE);
243
+ const answer = object(rawAnswers[id]);
244
+ const evidence = {
245
+ probabilitySource: "provider",
246
+ ...answer.confidence === void 0 ? {} : { confidence: answer.confidence }
247
+ };
248
+ switch (question.type) {
249
+ case "choice":
250
+ if (answer.type !== "choice" || answer.probabilities === void 0 || answer.confidence === void 0) throw new ModelError("Invalid TypeSafe choice answer", MODEL_ERROR_CODES.MALFORMED_RESPONSE);
251
+ answers[id] = {
252
+ type: "choice",
253
+ choice: answer.choice,
254
+ probabilities: answer.probabilities,
255
+ ...evidence
256
+ };
257
+ break;
258
+ case "score": {
259
+ if (answer.type !== "score" || answer.probabilities === void 0 || answer.confidence === void 0) throw new ModelError("Invalid TypeSafe score answer", MODEL_ERROR_CODES.MALFORMED_RESPONSE);
260
+ const legend = object(answer.legend);
261
+ if (Object.keys(legend).length !== question.levels.length || question.levels.some((level, i) => !Object.hasOwn(legend, String(i)) || !sameDescription(level, legend[String(i)]))) throw new ModelError("Invalid TypeSafe score legend", MODEL_ERROR_CODES.MALFORMED_RESPONSE);
262
+ answers[id] = {
263
+ type: "score",
264
+ score: answer.score,
265
+ probabilities: answer.probabilities,
266
+ ...evidence
267
+ };
268
+ break;
269
+ }
270
+ case "boolean":
271
+ if (answer.type !== "noul") throw new ModelError("Invalid TypeSafe boolean answer", MODEL_ERROR_CODES.MALFORMED_RESPONSE);
272
+ answers[id] = {
273
+ type: "boolean",
274
+ value: answer.noul >= .5,
275
+ probabilityTrue: answer.noul,
276
+ probabilitySource: "provider"
277
+ };
278
+ }
279
+ }
280
+ const usage = responseUsage(raw);
281
+ return validateDecisionResult({
282
+ model: raw.model,
283
+ answers,
284
+ ...usage === void 0 ? {} : { usage }
285
+ }, input.questions);
286
+ }, timeout, captured.headers);
287
+ const result = response.value;
288
+ return response.requestId === void 0 ? result : validateDecisionResult({
289
+ ...result,
290
+ providerRequestId: response.requestId
291
+ }, input.questions);
292
+ }
293
+ async #dispatch(provider, model, path, body, callerSignal, context, decode, timeout = this.#timeout, publicHeaders = {}) {
294
+ const controller = new AbortController();
295
+ const forward = () => controller.abort(callerSignal?.reason ?? new ModelError("TypeSafe call aborted", MODEL_ERROR_CODES.ABORTED));
296
+ callerSignal?.addEventListener("abort", forward, { once: true });
297
+ if (callerSignal?.aborted) forward();
298
+ const timer = setTimeout(() => controller.abort(new ModelError("TypeSafe request timed out", MODEL_ERROR_CODES.TIMEOUT)), timeout);
299
+ const signal = controller.signal;
300
+ let attempt;
301
+ let response;
302
+ let sent = false;
303
+ let requestId;
304
+ let reportedUsage;
305
+ const endAttempt = (input) => {
306
+ const handle = attempt;
307
+ attempt = void 0;
308
+ handle?.end(input);
309
+ };
310
+ try {
311
+ throwIfAborted(signal);
312
+ const credential = this.#credential;
313
+ const key = typeof credential === "string" ? credential : await abortable(credential(signal, context?.logger ?? NULL_LOGGER), signal);
314
+ throwIfAborted(signal);
315
+ const url = `${this.#baseUrl}/${path}`;
316
+ context?.declareProviderAttemptAccounting?.();
317
+ attempt = await abortable(Promise.resolve(context?.startProviderAttempt?.({
318
+ provider,
319
+ model,
320
+ method: body === void 0 ? "GET" : "POST",
321
+ origin: new URL(url).origin
322
+ }, signal)).then((handle) => {
323
+ if (signal.aborted) handle?.end({
324
+ status: "aborted",
325
+ dispatchState: "not-sent"
326
+ });
327
+ return handle;
328
+ }), signal);
329
+ throwIfAborted(signal);
330
+ const headers = new Headers({
331
+ ...this.#headers,
332
+ ...publicHeaders,
333
+ ...attributionHeaders(),
334
+ authorization: `Bearer ${key}`,
335
+ accept: "application/json",
336
+ "content-type": "application/json"
337
+ });
338
+ if (attempt) headers.set("traceparent", attempt.traceparent);
339
+ response = await abortable(Promise.resolve().then(() => {
340
+ throwIfAborted(signal);
341
+ sent = true;
342
+ return this.#fetch(url, {
343
+ method: body === void 0 ? "GET" : "POST",
344
+ headers,
345
+ ...body === void 0 ? {} : { body },
346
+ signal,
347
+ redirect: "error"
348
+ });
349
+ }).then((result) => {
350
+ if (signal.aborted) result.body?.cancel().catch(() => {});
351
+ return result;
352
+ }).catch(() => {
353
+ throwIfAborted(signal);
354
+ throw new ModelError("TypeSafe transport failed", MODEL_ERROR_CODES.TRANSPORT);
355
+ }), signal);
356
+ const headerId = response.headers.get("x-request-id") ?? response.headers.get("request-id");
357
+ requestId = headerId && headerId.length <= 256 ? headerId : void 0;
358
+ if (!response.ok) {
359
+ response.body?.cancel().catch(() => {});
360
+ const delay = retryAfter(response.headers.get("retry-after"));
361
+ throw new ModelError(`TypeSafe HTTP request failed (${response.status})`, statusCode(response.status), {
362
+ status: response.status,
363
+ ...delay === void 0 ? {} : { providerRetryAfterMs: delay },
364
+ ...requestId === void 0 ? {} : { requestId: ProviderRequestId(requestId) }
365
+ });
366
+ }
367
+ const json = await readJson(response, this.#maxResponse, signal);
368
+ if (decode !== void 0) reportedUsage = responseUsage(json);
369
+ const value = decode ? decode(json) : json;
370
+ throwIfAborted(signal);
371
+ const usage = reportedUsage;
372
+ endAttempt({
373
+ status: "success",
374
+ dispatchState: "sent",
375
+ httpStatus: response.status,
376
+ ...requestId === void 0 ? {} : { providerRequestId: requestId },
377
+ ...usage === void 0 ? {} : {
378
+ reported: usage,
379
+ usageFinal: true
380
+ }
381
+ });
382
+ return {
383
+ value,
384
+ ...requestId === void 0 ? {} : { requestId }
385
+ };
386
+ } catch (error) {
387
+ let failure = error;
388
+ if (signal.aborted) try {
389
+ throwIfAborted(signal);
390
+ } catch (aborted) {
391
+ failure = aborted;
392
+ }
393
+ const normalized = normalizeModelFailure(failure);
394
+ endAttempt({
395
+ status: normalized.code === MODEL_ERROR_CODES.ABORTED ? "aborted" : "error",
396
+ dispatchState: sent ? "sent" : "not-sent",
397
+ ...response === void 0 ? {} : { httpStatus: response.status },
398
+ ...requestId === void 0 ? {} : { providerRequestId: requestId },
399
+ ...reportedUsage === void 0 ? {} : {
400
+ reported: reportedUsage,
401
+ usageFinal: true
402
+ },
403
+ error: {
404
+ type: "ModelError",
405
+ message: "TypeSafe request failed",
406
+ code: normalized.code
407
+ }
408
+ });
409
+ throw failure;
410
+ } finally {
411
+ clearTimeout(timer);
412
+ callerSignal?.removeEventListener("abort", forward);
413
+ }
414
+ }
415
+ };
416
+ function typesafeAdapter(options) {
417
+ return new TypesafeDecisionAdapter(options);
418
+ }
419
+ function typesafePlugin(options) {
420
+ const adapter = typesafeAdapter(options);
421
+ const routes = Object.freeze([...options.routes ?? ["typesafe"]]);
422
+ return defineDecisionProviderPlugin({
423
+ id: options.id ?? "typesafe",
424
+ routes,
425
+ setup(registrar) {
426
+ const registration = registrar.registerAdapter(routes, adapter);
427
+ return () => registration.dispose();
428
+ }
429
+ });
430
+ }
431
+
432
+ //#endregion
433
+ export { typesafeAdapter, typesafePlugin };
434
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/adapter.ts"],"sourcesContent":["import { assertUsableApiKey, attributionHeaders, ModelError, MODEL_ERROR_CODES, normalizeModelFailure, ProviderRequestId, resolveRetryPolicy, validateUsageCounters, type UsageCounters } from '@alvin0/ai-agent-sdk-core'\nimport { CREDENTIAL_CAPABILITY_API_VERSION, type CredentialInput, type EndProviderAttemptInput, type ModelInvocationContext, type ProviderAttemptHandle, type RetryPolicyConfig, type SdkLogger } from '@alvin0/ai-agent-sdk-core/provider'\nimport { DecisionAdapter, defineDecisionProviderPlugin, snapshotDecisionInput, validateDecisionResult, type DecisionAnswer, type DecisionInput, type DecisionModelInfo, type DecisionProviderPlugin, type DecisionRequest, type DecisionResult, type PreparedDecisionCall } from '@alvin0/ai-agent-sdk-decision-adapter'\nimport { abortable, throwIfAborted } from '@alvin0/ai-agent-sdk-decision-adapter/transport'\n\nconst CAPABILITIES = Object.freeze({ questionTypes: Object.freeze(['choice', 'score', 'boolean'] as const), maxChoiceOptions: 255, maxScoreLevels: 10 })\nconst NULL_LOGGER: SdkLogger = Object.freeze({ child: () => NULL_LOGGER, trace() {}, debug() {}, info() {}, warn() {}, error() {}, fatal() {} })\ninterface CapturedTypesafeRequest { readonly input: DecisionInput; readonly wire: string; readonly timeout: number; readonly provider: string; readonly model: string; readonly headers: Readonly<Record<string, string>> }\nexport interface TypesafeAdapterOptions {\n readonly apiKey: CredentialInput\n /** API root including /v1. Default: https://api.typesafe.ai/v1 */\n readonly baseUrl?: string | URL\n readonly fetch?: typeof globalThis.fetch\n readonly headers?: Readonly<Record<string, string>>\n readonly requestTimeoutMs?: number\n readonly maxRequestBytes?: number\n readonly maxResponseBytes?: number\n readonly retryPolicy?: RetryPolicyConfig\n /** For explicitly configured development endpoints only. */\n readonly allowInsecureHttp?: boolean\n}\nexport interface TypesafePluginOptions extends TypesafeAdapterOptions {\n readonly id?: string\n readonly routes?: readonly string[]\n}\nfunction object(value: unknown): Record<string, unknown> {\n if (value === null || typeof value !== 'object' || Array.isArray(value)) throw new ModelError('TypeSafe returned an invalid response object', MODEL_ERROR_CODES.MALFORMED_RESPONSE)\n return value as Record<string, unknown>\n}\n/** Compare JSON rubric values without depending on object property order. */\nfunction sameDescription(expected: unknown, actual: unknown): boolean {\n if (expected === actual) return true\n if (expected === null || actual === null || typeof expected !== 'object' || typeof actual !== 'object') return false\n if (Array.isArray(expected)) return Array.isArray(actual) && expected.length === actual.length && expected.every((value, index) => sameDescription(value, actual[index]))\n if (Array.isArray(actual)) return false\n const keys = Object.keys(expected)\n return keys.length === Object.keys(actual).length && keys.every(key => Object.hasOwn(actual, key) && sameDescription((expected as Record<string, unknown>)[key], (actual as Record<string, unknown>)[key]))\n}\nfunction capturePublicHeaders(source: Readonly<Record<string, string>> | undefined): Readonly<Record<string, string>> {\n let headers: Readonly<Record<string, string>>\n try { headers = Object.freeze(Object.fromEntries(new Headers(source).entries())) }\n catch { throw new ModelError('Invalid TypeSafe custom headers', MODEL_ERROR_CODES.INVALID_REQUEST) }\n for (const key of Object.keys(headers)) {\n if (['authorization', 'content-type', 'accept', 'host', 'content-length', 'traceparent'].includes(key)) throw new ModelError('TypeSafe custom headers cannot override transport headers', MODEL_ERROR_CODES.INVALID_REQUEST)\n }\n return headers\n}\nfunction captureInvocationHeaders(context?: ModelInvocationContext): Readonly<Record<string, string>> {\n if (context?.providerOptions?.body !== undefined && Object.keys(context.providerOptions.body).length) throw new ModelError('TypeSafe does not support providerOptions.body overrides', MODEL_ERROR_CODES.INVALID_REQUEST)\n return capturePublicHeaders(context?.providerOptions?.headers)\n}\nfunction bound(value: number | undefined, fallback: number, name: string): number {\n const result = value ?? fallback\n if (!Number.isSafeInteger(result) || result <= 0 || result > 2_147_483_647) throw new Error(`Invalid TypeSafe ${name}`)\n return result\n}\nfunction responseUsage(value: unknown): UsageCounters | undefined {\n const raw = object(value).usage\n if (raw === undefined) return undefined\n const source = object(raw)\n const counters = {\n ...(source.input_tokens === undefined ? {} : { inputTokens: source.input_tokens }),\n ...(source.output_tokens === undefined ? {} : { outputTokens: source.output_tokens }),\n }\n const validated = validateUsageCounters(counters)\n if (validated.invalidFields.length || validated.overflow) throw new ModelError('Invalid TypeSafe usage counters', MODEL_ERROR_CODES.MALFORMED_RESPONSE)\n return validated.reported\n}\nfunction retryAfter(value: string | null): number | undefined {\n if (value === null) return undefined\n const seconds = Number(value)\n const delay = Number.isFinite(seconds) ? seconds * 1_000 : Date.parse(value) - Date.now()\n return Number.isFinite(delay) && delay > 0 ? delay : undefined\n}\nfunction statusCode(status: number): string {\n if (status === 401 || status === 403) return MODEL_ERROR_CODES.AUTH\n if (status === 429) return MODEL_ERROR_CODES.RATE_LIMIT\n if (status >= 500) return MODEL_ERROR_CODES.SERVER\n return MODEL_ERROR_CODES.INVALID_REQUEST\n}\nasync function readJson(response: Response, maxBytes: number, signal: AbortSignal): Promise<unknown> {\n const length = Number(response.headers.get('content-length'))\n if (length > maxBytes) {\n void response.body?.cancel().catch(() => {})\n throw new ModelError('TypeSafe response exceeds byte limit', MODEL_ERROR_CODES.MALFORMED_RESPONSE)\n }\n if (!response.body) throw new ModelError('TypeSafe returned an empty response', MODEL_ERROR_CODES.MALFORMED_RESPONSE)\n const reader = response.body.getReader()\n const decoder = new TextDecoder('utf-8', { fatal: true })\n let bytes = 0\n let text = ''\n try {\n for (;;) {\n const item = await abortable(reader.read(), signal)\n if (item.done) break\n bytes += item.value.byteLength\n if (bytes > maxBytes) throw new ModelError('TypeSafe response exceeds byte limit', MODEL_ERROR_CODES.MALFORMED_RESPONSE)\n text += decoder.decode(item.value, { stream: true })\n }\n text += decoder.decode()\n try { return JSON.parse(text) as unknown }\n catch { throw new ModelError('TypeSafe returned invalid JSON', MODEL_ERROR_CODES.MALFORMED_RESPONSE) }\n } catch (error) {\n throwIfAborted(signal)\n if (error instanceof ModelError) throw error\n throw new ModelError('TypeSafe returned an unreadable JSON response', MODEL_ERROR_CODES.MALFORMED_RESPONSE)\n } finally {\n // Do not wait indefinitely for a foreign stream's cancellation hook.\n void reader.cancel().catch(() => {})\n }\n}\nclass TypesafeDecisionAdapter extends DecisionAdapter {\n readonly #baseUrl: string\n readonly #credential: string | ((signal: AbortSignal, logger: SdkLogger) => Promise<string>)\n readonly #fetch: typeof globalThis.fetch\n readonly #headers: Readonly<Record<string, string>>\n readonly #timeout: number\n readonly #maxRequest: number\n readonly #maxResponse: number\n readonly #retry: ReturnType<typeof resolveRetryPolicy> | undefined\n readonly #rubrics = new WeakMap<DecisionInput['questions'], string>()\n constructor(options: TypesafeAdapterOptions) {\n super()\n const url = new URL(options.baseUrl ?? 'https://api.typesafe.ai/v1')\n if (url.username || url.password || url.search || url.hash || (url.protocol !== 'https:' && !(url.protocol === 'http:' && options.allowInsecureHttp === true))) throw new Error('TypeSafe requires an HTTPS API root without credentials, query or fragment')\n this.#baseUrl = url.href.replace(/\\/$/, '')\n this.#fetch = options.fetch ?? globalThis.fetch\n this.#headers = capturePublicHeaders(options.headers)\n this.#timeout = bound(options.requestTimeoutMs, 30_000, 'requestTimeoutMs')\n this.#maxRequest = bound(options.maxRequestBytes, 2_097_152, 'maxRequestBytes')\n this.#maxResponse = bound(options.maxResponseBytes, 4_194_304, 'maxResponseBytes')\n this.#retry = options.retryPolicy === undefined ? undefined : resolveRetryPolicy(options.retryPolicy, 'typesafe.retryPolicy')\n if (typeof options.apiKey === 'string') this.#credential = assertUsableApiKey(options.apiKey, 'TypeSafe', 'apiKey')\n else {\n const source = options.apiKey\n if (source?.kind !== 'credential-source' || source.apiVersion !== CREDENTIAL_CAPABILITY_API_VERSION || typeof source.resolve !== 'function') throw new Error('TypeSafe requires a compatible credential source')\n const resolve = source.resolve.bind(source)\n this.#credential = async (signal, logger) => assertUsableApiKey(await resolve({ signal, logger }), 'TypeSafe', 'credential source')\n }\n }\n override providerRetryPolicy(): ReturnType<typeof resolveRetryPolicy> | undefined { return this.#retry }\n override async resolveModel(provider: string, model: string): Promise<DecisionModelInfo> {\n return Object.freeze({ provider, id: model, name: model, capabilities: CAPABILITIES })\n }\n override async prepareDecisionCall(provider: string, model: string, _signal?: AbortSignal, context?: ModelInvocationContext): Promise<PreparedDecisionCall> {\n const headers = captureInvocationHeaders(context)\n let captured: { readonly request: DecisionRequest; readonly value: CapturedTypesafeRequest } | undefined\n return Object.freeze({ model: await this.resolveModel(provider, model), evaluate: (request: DecisionRequest, invocation = context) => {\n if (request.provider !== provider || request.model !== model) throw new ModelError('Prepared TypeSafe decision target does not match request', MODEL_ERROR_CODES.INVALID_REQUEST)\n if (captured && captured.request !== request) throw new ModelError('Prepared TypeSafe decision call cannot dispatch a different request', MODEL_ERROR_CODES.INVALID_REQUEST)\n captured ??= { request, value: this.#capture(request, invocation === context ? headers : captureInvocationHeaders(invocation)) }\n return this.#evaluate(captured.value, invocation)\n } })\n }\n override async listModels(provider: string, signal?: AbortSignal): Promise<readonly DecisionModelInfo[]> {\n const result = await this.#dispatch(provider, 'catalog', 'models', undefined, signal)\n const models = object(result.value).models\n if (!Array.isArray(models) || models.length > 1_024) throw new ModelError('Invalid TypeSafe model catalog', MODEL_ERROR_CODES.MALFORMED_RESPONSE)\n return Object.freeze(models.map(entry => {\n const info = object(entry)\n if (typeof info.name !== 'string' || !info.name.trim() || info.name.length > 256) throw new ModelError('Invalid TypeSafe model id', MODEL_ERROR_CODES.MALFORMED_RESPONSE)\n return Object.freeze({ provider, id: info.name, name: info.name, capabilities: CAPABILITIES })\n }))\n }\n override async evaluate(request: DecisionRequest, context?: ModelInvocationContext): Promise<DecisionResult> {\n return this.#evaluate(this.#capture(request, captureInvocationHeaders(context)), context)\n }\n #capture(request: DecisionRequest, headers: Readonly<Record<string, string>>): CapturedTypesafeRequest {\n if (typeof request.provider !== 'string' || !request.provider.trim() || typeof request.model !== 'string' || !request.model.trim()) throw new ModelError('TypeSafe requires provider and model identifiers', MODEL_ERROR_CODES.INVALID_REQUEST)\n const input = snapshotDecisionInput(request, CAPABILITIES)\n const timeout = Math.min(this.#timeout, input.timeoutMs ?? this.#timeout)\n let questions = this.#rubrics.get(input.questions)\n if (questions === undefined) {\n questions = JSON.stringify(Object.fromEntries(Object.entries(input.questions).map(([id, question]) => {\n switch (question.type) {\n case 'choice': return [id, { type: 'choice', instructions: question.instructions, criteria: question.options }]\n case 'score': return [id, { type: 'score', instructions: question.instructions, criteria: question.levels }]\n case 'boolean': return [id, { type: 'noul', instructions: question.instructions, ...(question.criteria === undefined ? {} : { criteria: question.criteria }) }]\n }\n })))\n this.#rubrics.set(input.questions, questions)\n }\n const wire = `{\"questions\":${questions},\"model\":${JSON.stringify(request.model)},\"state\":${JSON.stringify(input.state)}}`\n if (new TextEncoder().encode(wire).byteLength > this.#maxRequest) throw new ModelError('TypeSafe request exceeds byte limit', MODEL_ERROR_CODES.INVALID_REQUEST)\n return { input, wire, timeout, provider: request.provider, model: request.model, headers }\n }\n async #evaluate(captured: CapturedTypesafeRequest, context?: ModelInvocationContext): Promise<DecisionResult> {\n const { input, wire, timeout, provider, model } = captured\n const response = await this.#dispatch(provider, model, 'systemone', wire, input.signal, context, value => {\n const raw = object(value)\n const rawAnswers = object(raw.answers)\n // Validate wire identifiers/types BEFORE projecting, so surplus answers cannot disappear.\n if (Object.keys(rawAnswers).length !== Object.keys(input.questions).length) throw new ModelError('Unexpected TypeSafe answer ids', MODEL_ERROR_CODES.MALFORMED_RESPONSE)\n const answers: Record<string, DecisionAnswer> = Object.create(null) as Record<string, DecisionAnswer>\n for (const [id, question] of Object.entries(input.questions)) {\n if (!Object.hasOwn(rawAnswers, id)) throw new ModelError('Missing TypeSafe answer', MODEL_ERROR_CODES.MALFORMED_RESPONSE)\n const answer = object(rawAnswers[id])\n const evidence = { probabilitySource: 'provider' as const, ...(answer.confidence === undefined ? {} : { confidence: answer.confidence as number }) }\n switch (question.type) {\n case 'choice':\n if (answer.type !== 'choice' || answer.probabilities === undefined || answer.confidence === undefined) throw new ModelError('Invalid TypeSafe choice answer', MODEL_ERROR_CODES.MALFORMED_RESPONSE)\n answers[id] = { type: 'choice', choice: answer.choice as string, probabilities: answer.probabilities as Record<string, number>, ...evidence }\n break\n case 'score': {\n if (answer.type !== 'score' || answer.probabilities === undefined || answer.confidence === undefined) throw new ModelError('Invalid TypeSafe score answer', MODEL_ERROR_CODES.MALFORMED_RESPONSE)\n const legend = object(answer.legend)\n if (Object.keys(legend).length !== question.levels.length || question.levels.some((level, i) => !Object.hasOwn(legend, String(i)) || !sameDescription(level, legend[String(i)]))) throw new ModelError('Invalid TypeSafe score legend', MODEL_ERROR_CODES.MALFORMED_RESPONSE)\n answers[id] = { type: 'score', score: answer.score as number, probabilities: answer.probabilities as Record<string, number>, ...evidence }\n break\n }\n case 'boolean':\n if (answer.type !== 'noul') throw new ModelError('Invalid TypeSafe boolean answer', MODEL_ERROR_CODES.MALFORMED_RESPONSE)\n answers[id] = { type: 'boolean', value: (answer.noul as number) >= 0.5, probabilityTrue: answer.noul as number, probabilitySource: 'provider' }\n break\n }\n }\n const usage = responseUsage(raw)\n return validateDecisionResult({\n model: raw.model, answers,\n ...(usage === undefined ? {} : { usage }),\n }, input.questions)\n }, timeout, captured.headers)\n const result = response.value as DecisionResult\n return response.requestId === undefined ? result : validateDecisionResult({ ...result, providerRequestId: response.requestId }, input.questions)\n }\n async #dispatch(provider: string, model: string, path: string, body: string | undefined, callerSignal?: AbortSignal, context?: ModelInvocationContext, decode?: (value: unknown) => DecisionResult, timeout = this.#timeout, publicHeaders: Readonly<Record<string, string>> = {}): Promise<{ value: unknown; requestId?: string }> {\n const controller = new AbortController()\n const forward = () => controller.abort(callerSignal?.reason ?? new ModelError('TypeSafe call aborted', MODEL_ERROR_CODES.ABORTED))\n callerSignal?.addEventListener('abort', forward, { once: true })\n if (callerSignal?.aborted) forward()\n const timer = setTimeout(() => controller.abort(new ModelError('TypeSafe request timed out', MODEL_ERROR_CODES.TIMEOUT)), timeout)\n const signal = controller.signal\n let attempt: ProviderAttemptHandle | undefined\n let response: Response | undefined\n let sent = false\n let requestId: string | undefined\n let reportedUsage: UsageCounters | undefined\n const endAttempt = (input: EndProviderAttemptInput) => {\n // Consume ownership before extension code: a throwing sink must not be called again.\n const handle = attempt\n attempt = undefined\n handle?.end(input)\n }\n try {\n throwIfAborted(signal)\n const credential = this.#credential\n const key = typeof credential === 'string' ? credential : await abortable(credential(signal, context?.logger ?? NULL_LOGGER), signal)\n throwIfAborted(signal)\n const url = `${this.#baseUrl}/${path}`\n context?.declareProviderAttemptAccounting?.()\n attempt = await abortable(Promise.resolve(context?.startProviderAttempt?.({ provider, model, method: body === undefined ? 'GET' : 'POST', origin: new URL(url).origin }, signal)).then(handle => {\n if (signal.aborted) handle?.end({ status: 'aborted', dispatchState: 'not-sent' })\n return handle\n }), signal)\n throwIfAborted(signal)\n const headers = new Headers({ ...this.#headers, ...publicHeaders, ...attributionHeaders(), authorization: `Bearer ${key}`, accept: 'application/json', 'content-type': 'application/json' })\n if (attempt) headers.set('traceparent', attempt.traceparent)\n const received = await abortable<Response>(Promise.resolve().then(() => {\n throwIfAborted(signal)\n sent = true\n return this.#fetch(url, { method: body === undefined ? 'GET' : 'POST', headers, ...(body === undefined ? {} : { body }), signal, redirect: 'error' })\n }).then(result => {\n if (signal.aborted) void result.body?.cancel().catch(() => {})\n return result\n }).catch(() => {\n throwIfAborted(signal)\n throw new ModelError('TypeSafe transport failed', MODEL_ERROR_CODES.TRANSPORT)\n }), signal)\n response = received\n const headerId = response.headers.get('x-request-id') ?? response.headers.get('request-id')\n requestId = headerId && headerId.length <= 256 ? headerId : undefined\n if (!response.ok) {\n void response.body?.cancel().catch(() => {})\n const delay = retryAfter(response.headers.get('retry-after'))\n throw new ModelError(`TypeSafe HTTP request failed (${response.status})`, statusCode(response.status), { status: response.status, ...(delay === undefined ? {} : { providerRetryAfterMs: delay }), ...(requestId === undefined ? {} : { requestId: ProviderRequestId(requestId) }) })\n }\n const json = await readJson(response, this.#maxResponse, signal)\n // Usage is billable evidence even when the decision answers are malformed.\n if (decode !== undefined) reportedUsage = responseUsage(json)\n const value = decode ? decode(json) : json\n throwIfAborted(signal)\n const usage = reportedUsage\n endAttempt({ status: 'success', dispatchState: 'sent', httpStatus: response.status, ...(requestId === undefined ? {} : { providerRequestId: requestId }), ...(usage === undefined ? {} : { reported: usage, usageFinal: true }) })\n return { value, ...(requestId === undefined ? {} : { requestId }) }\n } catch (error) {\n let failure: unknown = error\n if (signal.aborted) { try { throwIfAborted(signal) } catch (aborted) { failure = aborted } }\n const normalized = normalizeModelFailure(failure)\n endAttempt({ status: normalized.code === MODEL_ERROR_CODES.ABORTED ? 'aborted' : 'error', dispatchState: sent ? 'sent' : 'not-sent', ...(response === undefined ? {} : { httpStatus: response.status }), ...(requestId === undefined ? {} : { providerRequestId: requestId }), ...(reportedUsage === undefined ? {} : { reported: reportedUsage, usageFinal: true }), error: { type: 'ModelError', message: 'TypeSafe request failed', code: normalized.code } })\n throw failure\n } finally {\n clearTimeout(timer)\n callerSignal?.removeEventListener('abort', forward)\n }\n }\n}\nexport function typesafeAdapter(options: TypesafeAdapterOptions): DecisionAdapter { return new TypesafeDecisionAdapter(options) }\nexport function typesafePlugin(options: TypesafePluginOptions): DecisionProviderPlugin {\n const adapter = typesafeAdapter(options)\n const routes = Object.freeze([...(options.routes ?? ['typesafe'])])\n return defineDecisionProviderPlugin({ id: options.id ?? 'typesafe', routes, setup(registrar) { const registration = registrar.registerAdapter(routes, adapter); return () => registration.dispose() } })\n}\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@alvin0/ai-agent-sdk-provider-typesafe",
3
+ "author": {
4
+ "name": "alvin0 - chaulamdinhai",
5
+ "email": "chaulamdinhai@gmail.com"
6
+ },
7
+ "version": "0.1.10",
8
+ "description": "Universal TypeSafe Jev decision adapter and provider plugin for ai-agent-sdk",
9
+ "license": "MIT",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/alvin0/ai-agent-sdk.git",
13
+ "directory": "packages/provider-typesafe"
14
+ },
15
+ "homepage": "https://github.com/alvin0/ai-agent-sdk/tree/main/packages/provider-typesafe#readme",
16
+ "bugs": {
17
+ "url": "https://github.com/alvin0/ai-agent-sdk/issues"
18
+ },
19
+ "type": "module",
20
+ "sideEffects": false,
21
+ "files": [
22
+ "dist",
23
+ "README.md",
24
+ "LICENSE"
25
+ ],
26
+ "main": "./dist/index.js",
27
+ "types": "./dist/index.d.ts",
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/index.d.ts",
31
+ "import": "./dist/index.js",
32
+ "default": "./dist/index.js"
33
+ },
34
+ "./package.json": "./package.json"
35
+ },
36
+ "publishConfig": {
37
+ "access": "public",
38
+ "provenance": true
39
+ },
40
+ "dependencies": {
41
+ "@alvin0/ai-agent-sdk-decision-adapter": "^0.1.10"
42
+ },
43
+ "peerDependencies": {
44
+ "@alvin0/ai-agent-sdk-core": "^0.1.10"
45
+ },
46
+ "devDependencies": {
47
+ "@alvin0/ai-agent-sdk-core": "^0.1.10",
48
+ "@arethetypeswrong/cli": "0.18.5",
49
+ "publint": "0.3.24",
50
+ "tsdown": "0.22.14",
51
+ "typescript": "7.0.2",
52
+ "vitest": "4.1.11"
53
+ },
54
+ "aiAgentSdk": {
55
+ "runtime": "universal",
56
+ "coreApi": 1,
57
+ "roles": [
58
+ "decision-provider"
59
+ ]
60
+ },
61
+ "scripts": {
62
+ "build": "tsdown",
63
+ "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true});require('node:fs').rmSync('artifacts',{recursive:true,force:true})\"",
64
+ "typecheck": "tsc --noEmit",
65
+ "test": "vitest run --config vitest.config.ts",
66
+ "pack": "pnpm pack --pack-destination artifacts",
67
+ "test:pack": "node ../../scripts/test-packed-decision.mts provider-typesafe",
68
+ "check:publint": "publint",
69
+ "check:types": "attw --profile esm-only --pack ."
70
+ }
71
+ }