@hraness/sys1 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,3520 @@
1
+ // src/http.ts
2
+ class HttpBodyLimitError extends Error {
3
+ maxBytes;
4
+ constructor(maxBytes) {
5
+ super(`HTTP body exceeds ${maxBytes} bytes`);
6
+ this.maxBytes = maxBytes;
7
+ this.name = "HttpBodyLimitError";
8
+ }
9
+ }
10
+ async function readBoundedText(source, maxBytes, signal) {
11
+ signal?.throwIfAborted();
12
+ if (source.body === null)
13
+ return "";
14
+ const reader = source.body.getReader();
15
+ const chunks = [];
16
+ let bytes = 0;
17
+ let rejectAbort;
18
+ const aborted = new Promise((_, reject) => {
19
+ rejectAbort = reject;
20
+ });
21
+ const onAbort = () => {
22
+ rejectAbort?.(signal?.reason);
23
+ reader.cancel(signal?.reason).catch(() => {});
24
+ };
25
+ signal?.addEventListener("abort", onAbort, { once: true });
26
+ try {
27
+ for (;; ) {
28
+ signal?.throwIfAborted();
29
+ const next = await Promise.race([reader.read(), aborted]);
30
+ signal?.throwIfAborted();
31
+ if (next.done)
32
+ break;
33
+ bytes += next.value.byteLength;
34
+ if (bytes > maxBytes) {
35
+ const error = new HttpBodyLimitError(maxBytes);
36
+ reader.cancel(error).catch(() => {});
37
+ throw error;
38
+ }
39
+ chunks.push(next.value);
40
+ }
41
+ const body = new Uint8Array(bytes);
42
+ let offset = 0;
43
+ for (const chunk of chunks) {
44
+ body.set(chunk, offset);
45
+ offset += chunk.byteLength;
46
+ }
47
+ return new TextDecoder().decode(body);
48
+ } finally {
49
+ signal?.removeEventListener("abort", onAbort);
50
+ reader.releaseLock();
51
+ }
52
+ }
53
+
54
+ // src/protocol.ts
55
+ import { z } from "zod";
56
+ var PROTOCOL_LIMITS = {
57
+ maxBodyBytes: 1048576,
58
+ maxStateBytes: 262144,
59
+ maxQuestions: 64,
60
+ maxQuestionNameChars: 128,
61
+ maxInstructionsChars: 4096,
62
+ maxCriterionChars: 1024,
63
+ maxOptionChars: 256,
64
+ maxChoiceOptions: 255,
65
+ minScoreLevels: 2,
66
+ maxScoreLevels: 10,
67
+ maxModelChars: 128
68
+ };
69
+ var jsonValueSchema = z.lazy(() => z.union([
70
+ z.string(),
71
+ z.number(),
72
+ z.boolean(),
73
+ z.null(),
74
+ z.array(jsonValueSchema),
75
+ z.record(z.string(), jsonValueSchema)
76
+ ]));
77
+ var entrySchema = z.union([
78
+ z.string(),
79
+ z.null(),
80
+ z.array(jsonValueSchema),
81
+ z.record(z.string(), jsonValueSchema)
82
+ ]);
83
+ function boundedEntry(maxBytes, message) {
84
+ return entrySchema.refine((value) => {
85
+ const text = typeof value === "string" ? value : JSON.stringify(value);
86
+ return new TextEncoder().encode(text).byteLength <= maxBytes;
87
+ }, message);
88
+ }
89
+ var instructionsSchema = boundedEntry(PROTOCOL_LIMITS.maxInstructionsChars, "instructions exceeds 4096 bytes");
90
+ var criterionSchema = boundedEntry(PROTOCOL_LIMITS.maxCriterionChars, "criterion exceeds 1024 bytes");
91
+ var noulQuestionSchema = z.object({
92
+ type: z.literal("noul"),
93
+ instructions: instructionsSchema.optional(),
94
+ criteria: z.object({
95
+ true: criterionSchema.optional(),
96
+ false: criterionSchema.optional()
97
+ }).nullable().optional()
98
+ });
99
+ var choiceQuestionSchema = z.object({
100
+ type: z.literal("choice"),
101
+ instructions: instructionsSchema.optional(),
102
+ criteria: z.record(z.string().min(1).max(PROTOCOL_LIMITS.maxOptionChars), criterionSchema).refine((options) => {
103
+ const count = Object.keys(options).length;
104
+ return count >= 1 && count <= PROTOCOL_LIMITS.maxChoiceOptions;
105
+ }, { message: "choice criteria needs 1..255 options" })
106
+ });
107
+ var scoreQuestionSchema = z.object({
108
+ type: z.literal("score"),
109
+ instructions: instructionsSchema.optional(),
110
+ criteria: z.array(criterionSchema).min(PROTOCOL_LIMITS.minScoreLevels).max(PROTOCOL_LIMITS.maxScoreLevels)
111
+ });
112
+ var questionSchema = z.discriminatedUnion("type", [
113
+ noulQuestionSchema,
114
+ choiceQuestionSchema,
115
+ scoreQuestionSchema
116
+ ]);
117
+ var systemOneRequestSchema = z.object({
118
+ model: z.string().min(1).max(PROTOCOL_LIMITS.maxModelChars).optional(),
119
+ state: entrySchema,
120
+ questions: z.record(z.string().min(1).max(PROTOCOL_LIMITS.maxQuestionNameChars), questionSchema).refine((questions) => {
121
+ const count = Object.keys(questions).length;
122
+ return count >= 1 && count <= PROTOCOL_LIMITS.maxQuestions;
123
+ }, { message: "questions needs 1..64 entries" })
124
+ });
125
+ var probabilitySchema = z.number().min(0).max(1);
126
+ var noulAnswerSchema = z.object({
127
+ type: z.literal("noul"),
128
+ noul: probabilitySchema
129
+ });
130
+ var choiceAnswerSchema = z.object({
131
+ type: z.literal("choice"),
132
+ choice: z.string().min(1),
133
+ probabilities: z.record(z.string().min(1), probabilitySchema),
134
+ confidence: probabilitySchema
135
+ }).superRefine((answer, ctx) => {
136
+ const count = Object.keys(answer.probabilities).length;
137
+ if (count < 1 || count > PROTOCOL_LIMITS.maxChoiceOptions) {
138
+ ctx.addIssue({ code: "custom", message: "choice probabilities needs 1..255 entries" });
139
+ }
140
+ if (!(answer.choice in answer.probabilities)) {
141
+ ctx.addIssue({ code: "custom", message: "choice is missing from probabilities" });
142
+ }
143
+ });
144
+ var scoreAnswerSchema = z.object({
145
+ type: z.literal("score"),
146
+ score: z.number().min(0),
147
+ legend: z.record(z.string().regex(/^(0|[1-9]\d*)$/), entrySchema),
148
+ probabilities: z.record(z.string().regex(/^(0|[1-9]\d*)$/), probabilitySchema),
149
+ confidence: probabilitySchema
150
+ }).superRefine((answer, ctx) => {
151
+ const legendKeys = Object.keys(answer.legend);
152
+ const probabilityKeys = Object.keys(answer.probabilities);
153
+ if (legendKeys.length < PROTOCOL_LIMITS.minScoreLevels || legendKeys.length > PROTOCOL_LIMITS.maxScoreLevels) {
154
+ ctx.addIssue({ code: "custom", message: "score legend needs 2..10 entries" });
155
+ }
156
+ if (legendKeys.length !== probabilityKeys.length || legendKeys.some((key) => !(key in answer.probabilities))) {
157
+ ctx.addIssue({ code: "custom", message: "score legend and probabilities must have identical keys" });
158
+ }
159
+ if (legendKeys.some((key, index) => key !== String(index))) {
160
+ ctx.addIssue({ code: "custom", message: "score keys must be contiguous from zero" });
161
+ }
162
+ if (legendKeys.length > 0 && answer.score > legendKeys.length - 1) {
163
+ ctx.addIssue({ code: "custom", message: "score exceeds the legend range" });
164
+ }
165
+ });
166
+ var answerSchema = z.discriminatedUnion("type", [
167
+ noulAnswerSchema,
168
+ choiceAnswerSchema,
169
+ scoreAnswerSchema
170
+ ]);
171
+ var systemOneResponseSchema = z.object({
172
+ model: z.string().min(1).max(PROTOCOL_LIMITS.maxModelChars),
173
+ answers: z.record(z.string().min(1).max(PROTOCOL_LIMITS.maxQuestionNameChars), answerSchema).refine((answers) => Object.keys(answers).length <= PROTOCOL_LIMITS.maxQuestions),
174
+ usage: z.object({
175
+ input_tokens: z.number().int().nonnegative(),
176
+ output_tokens: z.number().int().nonnegative()
177
+ })
178
+ });
179
+ function errorBody(type, message, details) {
180
+ return { error: { type, message, ...details === undefined ? {} : { details } } };
181
+ }
182
+ function serializedBytes(value) {
183
+ return new TextEncoder().encode(JSON.stringify(value)).byteLength;
184
+ }
185
+
186
+ // src/response.ts
187
+ function sameEntry(left, right) {
188
+ if (left === right)
189
+ return true;
190
+ if (left === null || right === null || typeof left !== "object" || typeof right !== "object") {
191
+ return false;
192
+ }
193
+ if (Array.isArray(left) !== Array.isArray(right))
194
+ return false;
195
+ const a = left;
196
+ const b = right;
197
+ const keys = Object.keys(a);
198
+ return keys.length === Object.keys(b).length && keys.every((key) => Object.hasOwn(b, key) && sameEntry(a[key], b[key]));
199
+ }
200
+ function matchesRequest(request, response) {
201
+ const questionNames = Object.keys(request.questions);
202
+ if (questionNames.length !== Object.keys(response.answers).length)
203
+ return false;
204
+ return questionNames.every((name) => {
205
+ const question = request.questions[name];
206
+ if (!Object.hasOwn(response.answers, name))
207
+ return false;
208
+ const answer = response.answers[name];
209
+ if (question.type !== answer.type)
210
+ return false;
211
+ if (question.type === "choice" && answer.type === "choice") {
212
+ const options = Object.keys(question.criteria);
213
+ return Object.hasOwn(question.criteria, answer.choice) && options.length === Object.keys(answer.probabilities).length && options.every((option) => Object.hasOwn(answer.probabilities, option));
214
+ }
215
+ if (question.type === "score" && answer.type === "score") {
216
+ return question.criteria.length === Object.keys(answer.legend).length && question.criteria.every((criterion, index) => sameEntry(criterion, answer.legend[String(index)]));
217
+ }
218
+ return true;
219
+ });
220
+ }
221
+ function scoreEndpoint(bounds, lowerTotal, maximum) {
222
+ let expected = bounds.reduce((sum, bound, level) => sum + level * bound.lower, 0);
223
+ let remaining = Math.max(0, 1 - lowerTotal);
224
+ for (let position = 0;position < bounds.length && remaining > 0; position++) {
225
+ const level = maximum ? bounds.length - 1 - position : position;
226
+ const bound = bounds[level];
227
+ const added = Math.min(remaining, bound.upper - bound.lower);
228
+ expected += level * added;
229
+ remaining -= added;
230
+ }
231
+ return expected;
232
+ }
233
+ function validateResponseForRequest(request, value, roundingDecimals = 3) {
234
+ try {
235
+ if (roundingDecimals !== 2 && roundingDecimals !== 3)
236
+ throw new Error;
237
+ const rounding = 0.5 * 10 ** -roundingDecimals;
238
+ const response = systemOneResponseSchema.parse(value);
239
+ if (!matchesRequest(request, response))
240
+ throw new Error;
241
+ for (const answer of Object.values(response.answers)) {
242
+ if (answer.type === "noul")
243
+ continue;
244
+ const values = Object.values(answer.probabilities);
245
+ const total = values.reduce((sum, probability) => sum + probability, 0);
246
+ const bounds = values.map((probability) => ({
247
+ lower: Math.max(0, probability - rounding),
248
+ upper: Math.min(1, probability + rounding)
249
+ }));
250
+ const lowerTotal = bounds.reduce((sum, bound) => sum + bound.lower, 0);
251
+ const upperTotal = bounds.reduce((sum, bound) => sum + bound.upper, 0);
252
+ if (total <= 0 || lowerTotal > 1 + 0.000000001 || upperTotal < 1 - 0.000000001)
253
+ throw new Error;
254
+ if (answer.type === "choice") {
255
+ if (answer.probabilities[answer.choice] + 0.000000001 < Math.max(...values))
256
+ throw new Error;
257
+ } else {
258
+ const minimum = scoreEndpoint(bounds, lowerTotal, false);
259
+ const maximum = scoreEndpoint(bounds, lowerTotal, true);
260
+ if (answer.score + rounding < minimum - 0.000000001 || answer.score - rounding > maximum + 0.000000001) {
261
+ throw new Error;
262
+ }
263
+ }
264
+ }
265
+ return response;
266
+ } catch {
267
+ throw new Error("Invalid or mismatched System One response");
268
+ }
269
+ }
270
+
271
+ // src/kev.ts
272
+ var pythonLeadingWhitespace = /^[\u0009-\u000d\u001c-\u0020\u0085\u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000]+/u;
273
+ function renderNumber(value) {
274
+ const wire = JSON.stringify(value);
275
+ if (!/[.e]/i.test(wire))
276
+ return wire;
277
+ if (Math.abs(value) < 0.0001 || Math.abs(value) >= 10000000000000000) {
278
+ return value.toExponential().replace(/e([+-])(\d)$/u, "e$10$2");
279
+ }
280
+ return wire;
281
+ }
282
+ function render(value, indent = 0) {
283
+ if (value === null)
284
+ return "";
285
+ if (typeof value === "string")
286
+ return value;
287
+ if (typeof value === "boolean")
288
+ return value ? "True" : "False";
289
+ if (typeof value === "number")
290
+ return renderNumber(value);
291
+ const pad = " ".repeat(indent);
292
+ if (Array.isArray(value)) {
293
+ return value.map((entry) => `${pad}- ${render(entry, indent + 1).replace(pythonLeadingWhitespace, "")}`).join(`
294
+ `);
295
+ }
296
+ return Object.entries(value).map(([key, entry]) => entry !== null && typeof entry === "object" ? `${pad}${key}:
297
+ ${render(entry, indent + 1)}` : `${pad}${key}: ${render(entry)}`).join(`
298
+ `);
299
+ }
300
+ function adaptKevRequest(request) {
301
+ const adapted = structuredClone(request);
302
+ for (const question of Object.values(adapted.questions)) {
303
+ if (question.instructions === undefined)
304
+ question.instructions = null;
305
+ }
306
+ return adapted;
307
+ }
308
+ function adaptKevResponse(request, value) {
309
+ try {
310
+ const response = systemOneResponseSchema.parse(value);
311
+ if (request.model !== undefined && response.model !== request.model)
312
+ throw new Error;
313
+ for (const [name, question] of Object.entries(request.questions)) {
314
+ if (question.type !== "score")
315
+ continue;
316
+ const answer = response.answers[name];
317
+ if (answer?.type !== "score" || Object.keys(answer.legend).length !== question.criteria.length) {
318
+ throw new Error;
319
+ }
320
+ if (!question.criteria.every((criterion, index) => answer.legend[String(index)] === render(criterion))) {
321
+ throw new Error;
322
+ }
323
+ answer.legend = Object.fromEntries(question.criteria.map((criterion, index) => [String(index), structuredClone(criterion)]));
324
+ }
325
+ return validateResponseForRequest(request, response, 2);
326
+ } catch {
327
+ throw new Error("Invalid or mismatched System One response");
328
+ }
329
+ }
330
+ // src/profile.ts
331
+ import { z as z2 } from "zod";
332
+ var identifier = z2.string().min(1).max(64).regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/).refine((value) => value === value.trim());
333
+ var profileQuestionSchema = z2.discriminatedUnion("type", [
334
+ noulQuestionSchema.extend({
335
+ criteria: noulQuestionSchema.shape.criteria.unwrap().unwrap().strict().nullable().optional()
336
+ }).strict(),
337
+ choiceQuestionSchema.strict(),
338
+ scoreQuestionSchema.strict()
339
+ ]);
340
+ var profileDefinitionSchema = z2.object({
341
+ version: z2.literal(1),
342
+ id: identifier,
343
+ revision: identifier,
344
+ model: systemOneRequestSchema.shape.model.unwrap().regex(/^[a-z0-9][a-z0-9.-]*\/\S+$/).refine((value) => value === value.trim()),
345
+ questions: z2.record(z2.string().min(1).max(PROTOCOL_LIMITS.maxQuestionNameChars), profileQuestionSchema).refine((questions) => {
346
+ const count = Object.keys(questions).length;
347
+ return count >= 1 && count <= PROTOCOL_LIMITS.maxQuestions;
348
+ })
349
+ }).strict();
350
+
351
+ class Sys1ProfileError extends Error {
352
+ code;
353
+ constructor(code) {
354
+ super(code === "invalid_profile" ? "Invalid Sys1 profile" : "Invalid Sys1 profile request");
355
+ this.name = "Sys1ProfileError";
356
+ this.code = code;
357
+ }
358
+ }
359
+ function snapshotJson(input, maxBytes) {
360
+ const ancestors = new Set;
361
+ const encoder = new TextEncoder;
362
+ let bytes = 0;
363
+ const account = (count) => {
364
+ bytes += count;
365
+ if (bytes > maxBytes)
366
+ throw new Error;
367
+ };
368
+ const copy = (value) => {
369
+ if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number") {
370
+ if (typeof value === "number" && !Number.isFinite(value))
371
+ throw new Error;
372
+ if (typeof value === "string" && value.length > maxBytes - bytes)
373
+ throw new Error;
374
+ account(encoder.encode(JSON.stringify(value)).byteLength);
375
+ return value;
376
+ }
377
+ if (typeof value !== "object" || ancestors.has(value))
378
+ throw new Error;
379
+ const array = Array.isArray(value);
380
+ const prototype = Object.getPrototypeOf(value);
381
+ if (prototype !== (array ? Array.prototype : Object.prototype) && !(prototype === null && !array))
382
+ throw new Error;
383
+ const descriptors = Object.getOwnPropertyDescriptors(value);
384
+ const keys = Reflect.ownKeys(descriptors);
385
+ if (keys.some((key) => typeof key !== "string"))
386
+ throw new Error;
387
+ ancestors.add(value);
388
+ account(2);
389
+ let result;
390
+ if (array) {
391
+ if (keys.length !== value.length + 1)
392
+ throw new Error;
393
+ const items = [];
394
+ for (let index = 0;index < value.length; index++) {
395
+ const descriptor = descriptors[String(index)];
396
+ if (descriptor === undefined || !descriptor.enumerable || !("value" in descriptor))
397
+ throw new Error;
398
+ if (index > 0)
399
+ account(1);
400
+ items.push(copy(descriptor.value));
401
+ }
402
+ result = items;
403
+ } else {
404
+ const object = Object.create(null);
405
+ for (let index = 0;index < keys.length; index++) {
406
+ const key = keys[index];
407
+ const descriptor = descriptors[key];
408
+ if (descriptor === undefined || !descriptor.enumerable || !("value" in descriptor))
409
+ throw new Error;
410
+ if (index > 0)
411
+ account(1);
412
+ account(encoder.encode(JSON.stringify(key)).byteLength + 1);
413
+ object[key] = copy(descriptor.value);
414
+ }
415
+ result = object;
416
+ }
417
+ ancestors.delete(value);
418
+ return result;
419
+ };
420
+ return copy(input);
421
+ }
422
+ function freezeDeep(value) {
423
+ if (value !== null && typeof value === "object") {
424
+ for (const child of Object.values(value))
425
+ freezeDeep(child);
426
+ Object.freeze(value);
427
+ }
428
+ return value;
429
+ }
430
+ function createProfile(input) {
431
+ let definition;
432
+ try {
433
+ definition = freezeDeep(profileDefinitionSchema.parse(snapshotJson(input, PROTOCOL_LIMITS.maxBodyBytes)));
434
+ } catch {
435
+ throw new Sys1ProfileError("invalid_profile");
436
+ }
437
+ return Object.freeze({
438
+ definition,
439
+ request(state) {
440
+ try {
441
+ const request = systemOneRequestSchema.parse(snapshotJson({
442
+ model: definition.model,
443
+ state,
444
+ questions: definition.questions
445
+ }, PROTOCOL_LIMITS.maxBodyBytes));
446
+ if (serializedBytes(request.state) > PROTOCOL_LIMITS.maxStateBytes)
447
+ throw new Error;
448
+ return request;
449
+ } catch {
450
+ throw new Sys1ProfileError("invalid_request");
451
+ }
452
+ }
453
+ });
454
+ }
455
+
456
+ // src/client.ts
457
+ var DEFAULT_BASE_URL = "http://127.0.0.1:13900";
458
+ var MAX_RESPONSE_BYTES = 4194304;
459
+ var ERROR_MESSAGES = {
460
+ invalid_options: "Invalid Sys1 client options",
461
+ invalid_request: "Invalid System One request",
462
+ aborted: "Sys1 request aborted",
463
+ timeout: "Sys1 request timed out",
464
+ transport_error: "Sys1 transport failed",
465
+ http_error: "Sys1 endpoint returned an HTTP error",
466
+ response_too_large: "Sys1 response exceeded the byte limit",
467
+ invalid_response: "Sys1 endpoint returned an invalid or mismatched response"
468
+ };
469
+
470
+ class Sys1ClientError extends Error {
471
+ code;
472
+ status;
473
+ constructor(code, status) {
474
+ super(ERROR_MESSAGES[code]);
475
+ this.name = "Sys1ClientError";
476
+ this.code = code;
477
+ this.status = status;
478
+ }
479
+ }
480
+ function metadata(headers) {
481
+ const result = {};
482
+ const adapter = headers.get("x-sys1-adapter");
483
+ const decimals = headers.get("x-sys1-probability-decimals");
484
+ if (adapter !== null || decimals !== null) {
485
+ if (adapter !== "kev" || decimals !== "2")
486
+ throw new Sys1ClientError("invalid_response");
487
+ result.adapter = "kev";
488
+ result.probabilityDecimals = 2;
489
+ }
490
+ const name = (key) => {
491
+ const value = headers.get(key);
492
+ if (value === null)
493
+ return;
494
+ if (!/^[a-zA-Z0-9_.:/-]{1,128}$/.test(value))
495
+ throw new Sys1ClientError("invalid_response");
496
+ return value;
497
+ };
498
+ const backend = name("x-sys1-backend");
499
+ if (backend !== undefined)
500
+ result.backend = backend;
501
+ const attempts = headers.get("x-sys1-attempts");
502
+ if (attempts !== null) {
503
+ if (!/^[1-9]\d{0,5}$/.test(attempts))
504
+ throw new Sys1ClientError("invalid_response");
505
+ result.attempts = Number(attempts);
506
+ }
507
+ const localAdapter = name("x-sys1-local-adapter");
508
+ if (localAdapter !== undefined) {
509
+ result.local = { adapter: localAdapter };
510
+ for (const [header, key] of [
511
+ ["x-sys1-local-min-coverage", "minCoverage"],
512
+ ["x-sys1-local-min-concentration", "minConcentration"]
513
+ ]) {
514
+ const value = headers.get(header);
515
+ if (value === null)
516
+ continue;
517
+ if (!/^(0(?:\.\d{1,16})?|1(?:\.0{1,16})?)$/.test(value)) {
518
+ throw new Sys1ClientError("invalid_response");
519
+ }
520
+ result.local[key] = Number(value);
521
+ }
522
+ }
523
+ return result;
524
+ }
525
+ function cancelBody(response) {
526
+ response.body?.cancel().catch(() => {
527
+ return;
528
+ });
529
+ }
530
+ function fetchUntilAborted(fetchFn, url, init, signal) {
531
+ return new Promise((resolve, reject) => {
532
+ const abort = () => reject(new Sys1ClientError("aborted"));
533
+ signal.addEventListener("abort", abort, { once: true });
534
+ if (signal.aborted) {
535
+ signal.removeEventListener("abort", abort);
536
+ abort();
537
+ return;
538
+ }
539
+ Promise.resolve().then(() => fetchFn(url, init)).then((response) => {
540
+ if (signal.aborted)
541
+ cancelBody(response);
542
+ resolve(response);
543
+ }, reject).finally(() => signal.removeEventListener("abort", abort));
544
+ });
545
+ }
546
+ function createClient(options = {}) {
547
+ let endpoint;
548
+ let headers;
549
+ const timeoutMs = options.timeoutMs ?? 30000;
550
+ const fetchFn = options.fetch ?? globalThis.fetch;
551
+ const adapter = options.adapter ?? "systemone";
552
+ try {
553
+ if (adapter !== "systemone" && adapter !== "kev")
554
+ throw new Error;
555
+ const base = options.baseUrl ?? DEFAULT_BASE_URL;
556
+ if (base.length > 2048)
557
+ throw new Error;
558
+ const url = new URL(base);
559
+ if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.search || url.hash) {
560
+ throw new Error;
561
+ }
562
+ url.pathname = `${url.pathname.replace(/\/+$/, "")}/v1/systemone`;
563
+ endpoint = url.href;
564
+ headers = new Headers(options.headers);
565
+ headers.set("content-type", "application/json");
566
+ headers.set("accept", "application/json");
567
+ let headerBytes = 0;
568
+ headers.forEach((value, key) => {
569
+ headerBytes += new TextEncoder().encode(key + value).byteLength;
570
+ });
571
+ if (headerBytes > 16384 || !Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 300000 || typeof fetchFn !== "function") {
572
+ throw new Error;
573
+ }
574
+ } catch {
575
+ throw new Sys1ClientError("invalid_options");
576
+ }
577
+ return {
578
+ async evaluate(input, evaluation = {}) {
579
+ if (evaluation.signal?.aborted)
580
+ throw new Sys1ClientError("aborted");
581
+ let request;
582
+ let body;
583
+ try {
584
+ const serialized = JSON.stringify(input);
585
+ if (new TextEncoder().encode(serialized).byteLength > PROTOCOL_LIMITS.maxBodyBytes)
586
+ throw new Error;
587
+ request = systemOneRequestSchema.parse(JSON.parse(serialized));
588
+ const state = typeof request.state === "string" ? request.state : JSON.stringify(request.state);
589
+ if (new TextEncoder().encode(state).byteLength > PROTOCOL_LIMITS.maxStateBytes)
590
+ throw new Error;
591
+ body = JSON.stringify(adapter === "kev" ? adaptKevRequest(request) : request);
592
+ if (new TextEncoder().encode(body).byteLength > PROTOCOL_LIMITS.maxBodyBytes)
593
+ throw new Error;
594
+ } catch {
595
+ throw new Sys1ClientError("invalid_request");
596
+ }
597
+ const controller = new AbortController;
598
+ let timedOut = false;
599
+ const onAbort = () => controller.abort();
600
+ evaluation.signal?.addEventListener("abort", onAbort, { once: true });
601
+ const timer = setTimeout(() => {
602
+ timedOut = true;
603
+ controller.abort();
604
+ }, timeoutMs);
605
+ try {
606
+ const response = await fetchUntilAborted(fetchFn, endpoint, {
607
+ method: "POST",
608
+ headers: new Headers(headers),
609
+ body,
610
+ signal: controller.signal,
611
+ redirect: "error",
612
+ credentials: "omit"
613
+ }, controller.signal);
614
+ if (!response.ok) {
615
+ cancelBody(response);
616
+ throw new Sys1ClientError("http_error", response.status);
617
+ }
618
+ const text = await readBoundedText(response, MAX_RESPONSE_BYTES, controller.signal);
619
+ let parsed;
620
+ let route;
621
+ try {
622
+ route = metadata(response.headers);
623
+ const value = JSON.parse(text);
624
+ if (adapter === "kev") {
625
+ parsed = adaptKevResponse(request, value);
626
+ route.adapter = "kev";
627
+ route.probabilityDecimals = 2;
628
+ } else {
629
+ parsed = validateResponseForRequest(request, value, route.probabilityDecimals ?? 3);
630
+ }
631
+ } catch {
632
+ throw new Sys1ClientError("invalid_response");
633
+ }
634
+ return { response: parsed, metadata: route };
635
+ } catch (error) {
636
+ if (controller.signal.aborted)
637
+ throw new Sys1ClientError(timedOut ? "timeout" : "aborted");
638
+ if (error instanceof Sys1ClientError)
639
+ throw error;
640
+ if (error instanceof HttpBodyLimitError)
641
+ throw new Sys1ClientError("response_too_large");
642
+ throw new Sys1ClientError("transport_error");
643
+ } finally {
644
+ clearTimeout(timer);
645
+ evaluation.signal?.removeEventListener("abort", onAbort);
646
+ }
647
+ }
648
+ };
649
+ }
650
+ // src/config.ts
651
+ import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
652
+ import { homedir } from "node:os";
653
+ import { join } from "node:path";
654
+ import { z as z3 } from "zod";
655
+
656
+ // src/defaults.ts
657
+ var LOCAL_MODEL_TIERS = ["compact", "quality"];
658
+ var DEFAULT_LOCAL_MODELS = {
659
+ compact: "qwen3-0.6b",
660
+ quality: "qwen3-1.7b"
661
+ };
662
+ var PLATFORM_TARGETS = {
663
+ "darwin-arm64": "Metal or CPU",
664
+ "darwin-x64": "CPU",
665
+ "linux-x64": "CUDA, Vulkan, or CPU",
666
+ "linux-arm64": "CPU",
667
+ "win32-x64": "CUDA, Vulkan, or CPU",
668
+ "win32-arm64": "CPU"
669
+ };
670
+ function platformRecommendation(options = {}) {
671
+ const platform = options.platform ?? process.platform;
672
+ const arch = options.arch ?? process.arch;
673
+ const target = `${platform}-${arch}`;
674
+ const acceleration = PLATFORM_TARGETS[target];
675
+ const tier = options.tier ?? "quality";
676
+ if (acceleration === undefined) {
677
+ return {
678
+ platform,
679
+ arch,
680
+ target,
681
+ supported: false,
682
+ acceleration: "unsupported",
683
+ tier,
684
+ model: null,
685
+ reason: `no supported Bun + llama.cpp package target for ${target}`
686
+ };
687
+ }
688
+ return {
689
+ platform,
690
+ arch,
691
+ target,
692
+ supported: true,
693
+ acceleration,
694
+ tier,
695
+ model: DEFAULT_LOCAL_MODELS[tier],
696
+ experimental: true,
697
+ reason: tier === "compact" ? "operator selected the experimental 0.6B diagnostic model" : "Qwen3 1.7B is the experimental default local model"
698
+ };
699
+ }
700
+
701
+ // src/config.ts
702
+ var ROUTING_POLICIES = [
703
+ "auto",
704
+ "prefer-local",
705
+ "prefer-hosted",
706
+ "local-only",
707
+ "hosted-only"
708
+ ];
709
+ var routingPolicySchema = z3.enum(ROUTING_POLICIES);
710
+ function isLoopbackHost(host) {
711
+ if (host === "localhost" || host === "::1")
712
+ return true;
713
+ const octets = host.split(".");
714
+ return octets.length === 4 && octets[0] === "127" && octets.every((octet) => /^\d{1,3}$/.test(octet) && Number(octet) <= 255);
715
+ }
716
+ var loopbackHostSchema = z3.string().min(1).max(255).refine(isLoopbackHost, "must be a loopback host");
717
+ var backendUrlSchema = z3.url().max(512).refine((value) => {
718
+ let url;
719
+ try {
720
+ url = new URL(value);
721
+ } catch {
722
+ return false;
723
+ }
724
+ const host = url.hostname.replace(/^\[|\]$/g, "");
725
+ return (url.protocol === "https:" || url.protocol === "http:" && isLoopbackHost(host)) && url.username === "" && url.password === "" && url.search === "" && url.hash === "";
726
+ }, "use HTTPS or loopback HTTP without URL credentials, query, or fragment");
727
+ var localBackendSchema = z3.object({
728
+ name: z3.string().min(1).max(64).regex(/^[a-z0-9][a-z0-9-]*$/, "lowercase letters, digits, hyphens").refine((name) => name !== "typesafe" && !name.startsWith("local-"), "typesafe and local-* names are reserved"),
729
+ base_url: backendUrlSchema,
730
+ model: z3.string().min(1).max(128),
731
+ adapter: z3.enum(["systemone", "kev"]).optional(),
732
+ size_b: z3.number().positive().max(1e4).optional(),
733
+ cost_rank: z3.number().int().min(0).max(1000).optional(),
734
+ capabilities: z3.object({
735
+ max_options: z3.number().int().positive().max(255).optional(),
736
+ max_questions: z3.number().int().positive().max(64).optional()
737
+ }).optional(),
738
+ enabled: z3.boolean().default(true)
739
+ });
740
+ var configSchema = z3.object({
741
+ version: z3.literal(1),
742
+ gateway: z3.object({
743
+ host: loopbackHostSchema.default("127.0.0.1"),
744
+ port: z3.number().int().min(1).max(65535).default(13900),
745
+ request_timeout_ms: z3.number().int().min(1000).max(120000).default(15000),
746
+ probe_timeout_ms: z3.number().int().min(200).max(1e4).default(1500)
747
+ }).prefault({}),
748
+ routing: z3.object({
749
+ policy: routingPolicySchema.default("auto")
750
+ }).prefault({}),
751
+ hosted: z3.object({
752
+ enabled: z3.boolean().default(false),
753
+ base_url: backendUrlSchema.default("https://api.typesafe.ai"),
754
+ model: z3.string().min(1).max(128).default("jev-1.13.0"),
755
+ api_key_env: z3.string().min(1).max(128).default("TYPESAFE_API_KEY")
756
+ }).prefault({}),
757
+ local: z3.object({
758
+ enabled: z3.boolean().default(true),
759
+ model: z3.string().min(1).max(64).regex(/^[a-z0-9][a-z0-9.-]*$/).default(DEFAULT_LOCAL_MODELS.quality),
760
+ context_tokens: z3.number().int().min(256).max(65536).default(2048),
761
+ eval_timeout_ms: z3.number().int().min(1000).max(300000).default(60000),
762
+ max_loaded_models: z3.number().int().min(1).max(4).default(1)
763
+ }).prefault({}),
764
+ backends: z3.array(localBackendSchema).max(64).refine((backends) => new Set(backends.map((backend) => backend.name)).size === backends.length, "backend names must be unique").default([])
765
+ });
766
+ var DEFAULT_CONFIG = configSchema.parse({ version: 1 });
767
+ function sys1Home(env = process.env) {
768
+ const override = env["SYS1_HOME"];
769
+ if (override !== undefined && override.length > 0)
770
+ return override;
771
+ return join(homedir(), ".sys1");
772
+ }
773
+ function configPath(home) {
774
+ return join(home, "config.json");
775
+ }
776
+ function pidPath(home) {
777
+ return join(home, "daemon.json");
778
+ }
779
+ function logPath(home) {
780
+ return join(home, "daemon.log");
781
+ }
782
+ function loadConfig(home) {
783
+ const path = configPath(home);
784
+ if (!existsSync(path)) {
785
+ return { ok: true, config: DEFAULT_CONFIG, path, existed: false };
786
+ }
787
+ let parsed;
788
+ try {
789
+ parsed = JSON.parse(readFileSync(path, "utf8"));
790
+ } catch {
791
+ return { ok: false, path, message: `config is not valid JSON: ${path}` };
792
+ }
793
+ const result = configSchema.safeParse(parsed);
794
+ if (!result.success) {
795
+ const issue = result.error.issues[0];
796
+ const where = issue === undefined ? "" : ` at ${issue.path.join(".")}`;
797
+ const what = issue === undefined ? "invalid config" : issue.message;
798
+ return { ok: false, path, message: `invalid config${where}: ${what}` };
799
+ }
800
+ return { ok: true, config: result.data, path, existed: true };
801
+ }
802
+ function saveConfig(home, config) {
803
+ mkdirSync(home, { recursive: true, mode: 448 });
804
+ chmodSync(home, 448);
805
+ const path = configPath(home);
806
+ writeFileSync(path, `${JSON.stringify(config, null, 2)}
807
+ `, { mode: 384 });
808
+ chmodSync(path, 384);
809
+ return path;
810
+ }
811
+ var SETTABLE_KEYS = {
812
+ "routing.policy": routingPolicySchema,
813
+ "gateway.host": loopbackHostSchema,
814
+ "gateway.port": z3.coerce.number().int().min(1).max(65535),
815
+ "gateway.request_timeout_ms": z3.coerce.number().int().min(1000).max(120000),
816
+ "gateway.probe_timeout_ms": z3.coerce.number().int().min(200).max(1e4),
817
+ "hosted.base_url": backendUrlSchema,
818
+ "hosted.model": z3.string().min(1).max(128),
819
+ "hosted.api_key_env": z3.string().min(1).max(128),
820
+ "local.enabled": z3.enum(["true", "false"]).transform((v) => v === "true"),
821
+ "local.model": z3.string().min(1).max(64).regex(/^[a-z0-9][a-z0-9.-]*$/),
822
+ "local.context_tokens": z3.coerce.number().int().min(256).max(65536),
823
+ "local.eval_timeout_ms": z3.coerce.number().int().min(1000).max(300000),
824
+ "local.max_loaded_models": z3.coerce.number().int().min(1).max(4)
825
+ };
826
+ function setConfigValue(config, key, rawValue) {
827
+ if (typeof key !== "string" || !Object.hasOwn(SETTABLE_KEYS, key)) {
828
+ return { ok: false, message: "unknown configuration key" };
829
+ }
830
+ const schema = SETTABLE_KEYS[key];
831
+ const parsed = schema.safeParse(rawValue);
832
+ if (!parsed.success) {
833
+ return { ok: false, message: `invalid value for ${key}: ${rawValue}` };
834
+ }
835
+ const next = structuredClone(config);
836
+ const [section, field] = key.split(".");
837
+ const target = next[section];
838
+ Object.defineProperty(target, field, { value: parsed.data, writable: true, enumerable: true, configurable: true });
839
+ return { ok: true, config: configSchema.parse(next) };
840
+ }
841
+ // src/router.ts
842
+ function requestNeeds(request) {
843
+ let maxOptions = 0;
844
+ const questions = Object.values(request.questions);
845
+ for (const question of questions) {
846
+ switch (question.type) {
847
+ case "choice":
848
+ maxOptions = Math.max(maxOptions, Object.keys(question.criteria).length);
849
+ break;
850
+ case "score":
851
+ maxOptions = Math.max(maxOptions, question.criteria.length);
852
+ break;
853
+ case "noul":
854
+ maxOptions = Math.max(maxOptions, 2);
855
+ break;
856
+ }
857
+ }
858
+ return { maxOptions, questions: questions.length };
859
+ }
860
+ function compatible(candidate, needs) {
861
+ const caps = candidate.capabilities;
862
+ if (caps === undefined)
863
+ return true;
864
+ if (caps.maxOptions !== undefined && needs.maxOptions > caps.maxOptions)
865
+ return false;
866
+ if (caps.maxQuestions !== undefined && needs.questions > caps.maxQuestions)
867
+ return false;
868
+ return true;
869
+ }
870
+ function byCheapest(a, b) {
871
+ const sizeA = a.size_b ?? Number.MAX_SAFE_INTEGER;
872
+ const sizeB = b.size_b ?? Number.MAX_SAFE_INTEGER;
873
+ if (sizeA !== sizeB)
874
+ return sizeA - sizeB;
875
+ if (a.cost_rank !== b.cost_rank)
876
+ return a.cost_rank - b.cost_rank;
877
+ return a.name.localeCompare(b.name);
878
+ }
879
+ function ordered(policy, candidates) {
880
+ const hosted = candidates.filter((c) => c.kind === "hosted");
881
+ const locals = candidates.filter((c) => c.kind === "local").sort(byCheapest);
882
+ switch (policy) {
883
+ case "prefer-local":
884
+ return [...locals, ...hosted];
885
+ case "local-only":
886
+ return locals;
887
+ case "hosted-only":
888
+ return hosted;
889
+ case "auto":
890
+ case "prefer-hosted":
891
+ return [...hosted, ...locals];
892
+ }
893
+ }
894
+ function chooseBackend(policy, requestedModel, candidates, needs) {
895
+ const fits = (candidate) => needs === undefined || compatible(candidate, needs);
896
+ if (requestedModel !== undefined && requestedModel !== "auto") {
897
+ const slash = requestedModel.indexOf("/");
898
+ if (slash > 0) {
899
+ const backendName = requestedModel.slice(0, slash);
900
+ const modelId = requestedModel.slice(slash + 1);
901
+ const pinned = candidates.find((c) => c.name === backendName);
902
+ if (pinned === undefined) {
903
+ return { ok: false, reason: "unknown_model", detail: `no backend named ${backendName}` };
904
+ }
905
+ if (ordered(policy, [pinned]).length === 0) {
906
+ return {
907
+ ok: false,
908
+ reason: "policy_restricted",
909
+ detail: `routing policy ${policy} excludes backend ${backendName}`
910
+ };
911
+ }
912
+ if (!pinned.models.includes(modelId) && pinned.models.length > 0) {
913
+ return {
914
+ ok: false,
915
+ reason: "unknown_model",
916
+ detail: `backend ${backendName} does not serve ${modelId}`
917
+ };
918
+ }
919
+ if (!pinned.available) {
920
+ return {
921
+ ok: false,
922
+ reason: "model_unavailable",
923
+ detail: `backend ${backendName} is not reachable`
924
+ };
925
+ }
926
+ if (!fits(pinned)) {
927
+ return {
928
+ ok: false,
929
+ reason: "request_unsupported",
930
+ detail: `request exceeds the published limits of backend ${backendName}`
931
+ };
932
+ }
933
+ return { ok: true, backend: pinned, reason: "pinned" };
934
+ }
935
+ const matching = candidates.filter((c) => c.models.includes(requestedModel));
936
+ const allowed = ordered(policy, matching);
937
+ if (matching.length > 0 && allowed.length === 0) {
938
+ return {
939
+ ok: false,
940
+ reason: "policy_restricted",
941
+ detail: `routing policy ${policy} excludes every backend serving ${requestedModel}`
942
+ };
943
+ }
944
+ const serving = allowed.filter(fits);
945
+ const available = serving.find((c) => c.available);
946
+ if (available !== undefined) {
947
+ return { ok: true, backend: available, reason: "model" };
948
+ }
949
+ if (serving.length > 0) {
950
+ return {
951
+ ok: false,
952
+ reason: "model_unavailable",
953
+ detail: `no reachable backend serves ${requestedModel}`
954
+ };
955
+ }
956
+ if (allowed.length > 0) {
957
+ return {
958
+ ok: false,
959
+ reason: "request_unsupported",
960
+ detail: `request exceeds the published limits of every backend serving ${requestedModel}`
961
+ };
962
+ }
963
+ return {
964
+ ok: false,
965
+ reason: "unknown_model",
966
+ detail: `no backend serves ${requestedModel}`
967
+ };
968
+ }
969
+ const viable = ordered(policy, candidates.filter((c) => c.explicitOnly !== true));
970
+ const fallback = viable.find((c) => c.available && fits(c));
971
+ if (fallback !== undefined) {
972
+ return { ok: true, backend: fallback, reason: policy === "auto" ? "auto" : "policy" };
973
+ }
974
+ if (viable.length > 0 && viable.every((c) => !fits(c))) {
975
+ return {
976
+ ok: false,
977
+ reason: "request_unsupported",
978
+ detail: "request exceeds the published limits of every configured backend"
979
+ };
980
+ }
981
+ return {
982
+ ok: false,
983
+ reason: "no_backend_available",
984
+ detail: "no configured backend is reachable"
985
+ };
986
+ }
987
+ // src/backends.ts
988
+ import { existsSync as existsSync3 } from "node:fs";
989
+ import { join as join3 } from "node:path";
990
+ import { z as z6 } from "zod";
991
+
992
+ // src/local/input.ts
993
+ class LocalInputError extends Error {
994
+ constructor(message) {
995
+ super(message);
996
+ this.name = "LocalInputError";
997
+ }
998
+ }
999
+
1000
+ // src/local/decide.ts
1001
+ var DECISION_LABELS = [
1002
+ "1",
1003
+ "2",
1004
+ "3",
1005
+ "4",
1006
+ "5",
1007
+ "6",
1008
+ "7",
1009
+ "8",
1010
+ "9",
1011
+ "A",
1012
+ "B",
1013
+ "C",
1014
+ "D",
1015
+ "E",
1016
+ "F",
1017
+ "G",
1018
+ "H",
1019
+ "I",
1020
+ "J",
1021
+ "K",
1022
+ "L",
1023
+ "M",
1024
+ "N",
1025
+ "O",
1026
+ "P",
1027
+ "Q",
1028
+ "R",
1029
+ "S",
1030
+ "T",
1031
+ "U",
1032
+ "V",
1033
+ "W",
1034
+ "X",
1035
+ "Y",
1036
+ "Z"
1037
+ ];
1038
+ var DECIDE_LIMITS = {
1039
+ maxStateChars: 6000,
1040
+ maxPromptChars: 16000,
1041
+ maxInstructionChars: 2000,
1042
+ maxOptionChars: 96,
1043
+ maxCriterionChars: 96,
1044
+ maxLabels: DECISION_LABELS.length
1045
+ };
1046
+ var NOUL_LABELS = ["yes", "no"];
1047
+ function truncate(text, max) {
1048
+ if (text.length > max)
1049
+ throw new LocalInputError(`local input exceeds ${max} characters`);
1050
+ return text;
1051
+ }
1052
+ function renderEntry(value, max) {
1053
+ const text = typeof value === "string" ? value : JSON.stringify(value);
1054
+ return truncate(text, max);
1055
+ }
1056
+ function renderState(state) {
1057
+ return renderEntry(state, DECIDE_LIMITS.maxStateChars);
1058
+ }
1059
+ function clamp(text) {
1060
+ if (text.length > DECIDE_LIMITS.maxPromptChars) {
1061
+ throw new LocalInputError(`decision prompt exceeds ${DECIDE_LIMITS.maxPromptChars} characters`);
1062
+ }
1063
+ return text;
1064
+ }
1065
+ function noulPrompt(state, q) {
1066
+ const criteria = [];
1067
+ if (q.criteria?.true != null) {
1068
+ criteria.push(`YES means: ${renderEntry(q.criteria.true, DECIDE_LIMITS.maxCriterionChars)}`);
1069
+ }
1070
+ if (q.criteria?.false != null) {
1071
+ criteria.push(`NO means: ${renderEntry(q.criteria.false, DECIDE_LIMITS.maxCriterionChars)}`);
1072
+ }
1073
+ return clamp([
1074
+ "You are a decision engine. Read the state, then answer the question with exactly one word: YES or NO.",
1075
+ "",
1076
+ "State:",
1077
+ '"""',
1078
+ renderState(state),
1079
+ '"""',
1080
+ `Question: ${renderEntry(q.instructions ?? "Is the claim true?", DECIDE_LIMITS.maxInstructionChars)}`,
1081
+ ...criteria.length > 0 ? [criteria.join(" ")] : [],
1082
+ "Answer:"
1083
+ ].join(`
1084
+ `));
1085
+ }
1086
+ function choicePrompt(state, q) {
1087
+ const lines = Object.entries(q.criteria).slice(0, DECIDE_LIMITS.maxLabels).map(([option, desc], i) => {
1088
+ const name = truncate(option, DECIDE_LIMITS.maxOptionChars);
1089
+ const detail = desc === null ? "" : ` — ${renderEntry(desc, DECIDE_LIMITS.maxCriterionChars)}`;
1090
+ return `${DECISION_LABELS[i]}: ${name}${detail}`;
1091
+ });
1092
+ return clamp([
1093
+ "You are a decision engine. Read the state, then answer with exactly one option label from the list. Answer with the label only.",
1094
+ "",
1095
+ "State:",
1096
+ '"""',
1097
+ renderState(state),
1098
+ '"""',
1099
+ `Question: ${renderEntry(q.instructions ?? "Choose the best option.", DECIDE_LIMITS.maxInstructionChars)}`,
1100
+ "Options:",
1101
+ ...lines,
1102
+ "Answer:"
1103
+ ].join(`
1104
+ `));
1105
+ }
1106
+ function scorePrompt(state, q) {
1107
+ const lines = q.criteria.map((desc, i) => `${DECISION_LABELS[i]}: ${desc === null ? "(undescribed)" : renderEntry(desc, DECIDE_LIMITS.maxCriterionChars)}`);
1108
+ return clamp([
1109
+ "You are a decision engine. Read the state, then rate it on the given scale. Answer with the level label only.",
1110
+ "",
1111
+ "State:",
1112
+ '"""',
1113
+ renderState(state),
1114
+ '"""',
1115
+ `Question: ${renderEntry(q.instructions ?? "Rate the state.", DECIDE_LIMITS.maxInstructionChars)}`,
1116
+ "Scale (lowest to highest):",
1117
+ ...lines,
1118
+ "Answer:"
1119
+ ].join(`
1120
+ `));
1121
+ }
1122
+ function answerLabels(question) {
1123
+ const count = question.type === "choice" ? Object.keys(question.criteria).length : question.type === "score" ? question.criteria.length : 2;
1124
+ if (count > DECIDE_LIMITS.maxLabels)
1125
+ throw new LocalInputError("too many local answer labels");
1126
+ switch (question.type) {
1127
+ case "noul":
1128
+ return [...NOUL_LABELS];
1129
+ case "choice":
1130
+ return DECISION_LABELS.slice(0, Object.keys(question.criteria).length);
1131
+ case "score":
1132
+ return DECISION_LABELS.slice(0, question.criteria.length);
1133
+ }
1134
+ }
1135
+ function decisionPrompt(state, question) {
1136
+ answerLabels(question);
1137
+ switch (question.type) {
1138
+ case "noul":
1139
+ return noulPrompt(state, question);
1140
+ case "choice":
1141
+ return choicePrompt(state, question);
1142
+ case "score":
1143
+ return scorePrompt(state, question);
1144
+ }
1145
+ }
1146
+ function normalize(text) {
1147
+ return text.trim().toLowerCase();
1148
+ }
1149
+ function tokenCoversLabel(tokenText, label) {
1150
+ const token = normalize(tokenText);
1151
+ if (token.length === 0)
1152
+ return false;
1153
+ const norm = normalize(label);
1154
+ if (token === norm)
1155
+ return true;
1156
+ if (norm.startsWith(token))
1157
+ return true;
1158
+ if (token.startsWith(norm)) {
1159
+ const rest = token.slice(norm.length);
1160
+ return rest.length > 0 && !/^[a-z0-9]/.test(rest);
1161
+ }
1162
+ return false;
1163
+ }
1164
+ function aggregateMass(entries, labels) {
1165
+ const masses = new Array(labels.length).fill(0);
1166
+ let total = 0;
1167
+ for (const [text, p] of entries) {
1168
+ if (!(p > 0))
1169
+ continue;
1170
+ total += p;
1171
+ for (let i = 0;i < labels.length; i += 1) {
1172
+ const label = labels[i];
1173
+ if (label !== undefined && tokenCoversLabel(text, label)) {
1174
+ masses[i] = (masses[i] ?? 0) + p;
1175
+ break;
1176
+ }
1177
+ }
1178
+ }
1179
+ let top = -1;
1180
+ let topMass = 0;
1181
+ for (let i = 0;i < masses.length; i += 1) {
1182
+ const mass = masses[i] ?? 0;
1183
+ if (mass > topMass) {
1184
+ topMass = mass;
1185
+ top = i;
1186
+ }
1187
+ }
1188
+ const covered = masses.reduce((a, b) => a + b, 0);
1189
+ return { masses, top, coverage: total > 0 ? Math.min(1, covered / total) : 0 };
1190
+ }
1191
+ function outcomeFromMass(mass, labels) {
1192
+ if (mass.top < 0)
1193
+ return null;
1194
+ const covered = mass.masses.reduce((a, b) => a + b, 0);
1195
+ const distribution = covered > 0 ? mass.masses.map((m) => m / covered) : mass.masses.map(() => 0);
1196
+ const label = labels[mass.top];
1197
+ if (label === undefined)
1198
+ return null;
1199
+ return { label, distribution, coverage: mass.coverage };
1200
+ }
1201
+ function confidenceOf(distribution) {
1202
+ const top = Math.max(...distribution);
1203
+ const uniform = 1 / distribution.length;
1204
+ if (distribution.length <= 1 || top <= uniform)
1205
+ return 0;
1206
+ return Math.min(1, (top - uniform) / (1 - uniform));
1207
+ }
1208
+ function round(value) {
1209
+ return Math.round(value * 1000) / 1000;
1210
+ }
1211
+ function toLocalAnswer(question, outcome) {
1212
+ const confidence = round(confidenceOf(outcome.distribution));
1213
+ switch (question.type) {
1214
+ case "noul":
1215
+ return { type: "noul", noul: round(outcome.distribution[0] ?? 0) };
1216
+ case "choice": {
1217
+ const keys = Object.keys(question.criteria);
1218
+ const selected = keys[DECISION_LABELS.indexOf(outcome.label)];
1219
+ if (selected === undefined)
1220
+ throw new Error("choice outcome has no selected option");
1221
+ const probabilities = {};
1222
+ keys.forEach((key, i) => {
1223
+ probabilities[key] = round(outcome.distribution[i] ?? 0);
1224
+ });
1225
+ return { type: "choice", choice: selected, probabilities, confidence };
1226
+ }
1227
+ case "score": {
1228
+ const legend = {};
1229
+ const probabilities = {};
1230
+ let score = 0;
1231
+ question.criteria.forEach((criterion, i) => {
1232
+ const key = String(i);
1233
+ const probability = outcome.distribution[i] ?? 0;
1234
+ legend[key] = criterion;
1235
+ probabilities[key] = round(probability);
1236
+ score += i * probability;
1237
+ });
1238
+ return { type: "score", score: round(score), legend, probabilities, confidence };
1239
+ }
1240
+ }
1241
+ }
1242
+ function toLocalResponse(model, answers, usage) {
1243
+ return { model, answers, usage };
1244
+ }
1245
+ function questionEntries(request) {
1246
+ return Object.entries(request.questions).slice(0, 64);
1247
+ }
1248
+
1249
+ // src/local/engine.ts
1250
+ import { spawn } from "node:child_process";
1251
+ import { fileURLToPath } from "node:url";
1252
+ import { z as z4 } from "zod";
1253
+
1254
+ // src/local/engine-types.ts
1255
+ class EngineUnavailableError extends Error {
1256
+ detail;
1257
+ constructor(message, detail) {
1258
+ super(message);
1259
+ this.name = "EngineUnavailableError";
1260
+ this.detail = detail;
1261
+ }
1262
+ }
1263
+ var ENGINE_IPC_LIMITS = {
1264
+ requestBytes: 131072,
1265
+ responseBytes: 16 * 1048576,
1266
+ maxEntries: 300000,
1267
+ maxTokenBytes: 4096,
1268
+ maxPromptBytes: 65536
1269
+ };
1270
+
1271
+ // src/local/engine.ts
1272
+ var distributionSchema = z4.object({
1273
+ entries: z4.array(z4.tuple([
1274
+ z4.string().refine((text) => Buffer.byteLength(text) <= ENGINE_IPC_LIMITS.maxTokenBytes),
1275
+ z4.number().finite().positive().max(1)
1276
+ ])).max(ENGINE_IPC_LIMITS.maxEntries),
1277
+ inputTokens: z4.number().int().nonnegative().max(1e6)
1278
+ }).strict();
1279
+ var probeSchema = z4.object({
1280
+ ok: z4.boolean(),
1281
+ backend: z4.string().max(128).optional(),
1282
+ gpu_offloading: z4.boolean().optional(),
1283
+ supported_backends: z4.array(z4.string().max(128)).max(32).optional(),
1284
+ message: z4.string().max(512).optional()
1285
+ }).strict();
1286
+ var responseSchema = z4.discriminatedUnion("kind", [
1287
+ z4.object({ id: z4.number().int(), kind: z4.literal("distribution"), value: distributionSchema }).strict(),
1288
+ z4.object({ id: z4.number().int(), kind: z4.literal("probe"), value: probeSchema }).strict(),
1289
+ z4.object({ id: z4.number().int(), kind: z4.literal("error"), detail: z4.string().max(128) }).strict(),
1290
+ z4.object({ id: z4.number().int(), kind: z4.literal("unsupported"), detail: z4.literal("context_limit") }).strict()
1291
+ ]);
1292
+ function defaultWorker() {
1293
+ const entry = new URL(import.meta.url.endsWith(".ts") ? "./engine-worker.ts" : "./engine-worker.js", import.meta.url);
1294
+ return spawn(process.execPath, [fileURLToPath(entry)], {
1295
+ stdio: ["pipe", "pipe", "ignore"],
1296
+ windowsHide: true
1297
+ });
1298
+ }
1299
+ function unavailable(detail) {
1300
+ return new EngineUnavailableError("local engine unavailable", detail);
1301
+ }
1302
+
1303
+ class EngineWorker {
1304
+ phase = "created";
1305
+ child;
1306
+ collected;
1307
+ pipes;
1308
+ pending = null;
1309
+ chunks = [];
1310
+ bytes = 0;
1311
+ nextId = 0;
1312
+ stopped = false;
1313
+ stopPromise;
1314
+ constructor(factory) {
1315
+ this.child = factory();
1316
+ this.pipes = this.child.stdio.filter((pipe) => pipe !== null && pipe !== undefined);
1317
+ const pipesClosed = this.pipes.map((pipe) => new Promise((resolve) => {
1318
+ if (pipe.closed)
1319
+ resolve();
1320
+ else
1321
+ pipe.once("close", () => resolve());
1322
+ }));
1323
+ const processClosed = new Promise((resolve) => this.child.once("close", () => {
1324
+ this.stopped = true;
1325
+ const pending = this.pending;
1326
+ this.pending = null;
1327
+ pending?.reject(unavailable("worker_exited"));
1328
+ resolve();
1329
+ }));
1330
+ this.collected = Promise.all([processClosed, ...pipesClosed]).then(() => {});
1331
+ this.child.on("error", () => {
1332
+ this.fail("worker_failed");
1333
+ });
1334
+ this.child.stdin?.on("error", () => {
1335
+ this.fail("worker_input_failed");
1336
+ });
1337
+ const output = this.child.stdout;
1338
+ if (this.child.stdin === null || output == null) {
1339
+ this.fail("worker_pipes_unavailable");
1340
+ return;
1341
+ }
1342
+ output.on("error", () => {
1343
+ this.fail("worker_output_failed");
1344
+ });
1345
+ output.on("data", (chunk) => this.accept(chunk));
1346
+ }
1347
+ accept(chunk) {
1348
+ if (this.stopped)
1349
+ return;
1350
+ this.phase = "response_started";
1351
+ this.bytes += chunk.byteLength;
1352
+ if (this.bytes > ENGINE_IPC_LIMITS.responseBytes) {
1353
+ this.fail("worker_response_limit");
1354
+ return;
1355
+ }
1356
+ this.chunks.push(chunk);
1357
+ const newline = chunk.indexOf(10);
1358
+ if (newline < 0)
1359
+ return;
1360
+ if (newline !== chunk.length - 1) {
1361
+ this.fail("worker_response_invalid");
1362
+ return;
1363
+ }
1364
+ let response;
1365
+ try {
1366
+ response = responseSchema.parse(JSON.parse(Buffer.concat(this.chunks, this.bytes).toString("utf8")));
1367
+ } catch {
1368
+ this.fail("worker_response_invalid");
1369
+ return;
1370
+ }
1371
+ this.chunks = [];
1372
+ this.bytes = 0;
1373
+ const pending = this.pending;
1374
+ if (pending === null || pending.id !== response.id) {
1375
+ this.fail("worker_response_mismatch");
1376
+ return;
1377
+ }
1378
+ this.pending = null;
1379
+ this.phase = "response_received";
1380
+ pending.resolve(response);
1381
+ }
1382
+ async fail(detail) {
1383
+ const pending = this.pending;
1384
+ this.pending = null;
1385
+ await this.stop();
1386
+ pending?.reject(unavailable(detail));
1387
+ }
1388
+ async request(payload, signal) {
1389
+ if (this.stopped)
1390
+ throw unavailable("worker_stopped");
1391
+ if (signal.aborted)
1392
+ throw unavailable("inference_aborted");
1393
+ if (this.pending !== null)
1394
+ throw unavailable("worker_busy");
1395
+ const id = ++this.nextId;
1396
+ const wire = `${JSON.stringify({ ...payload, id })}
1397
+ `;
1398
+ if (Buffer.byteLength(wire) > ENGINE_IPC_LIMITS.requestBytes)
1399
+ throw unavailable("worker_request_limit");
1400
+ const abort = () => {
1401
+ this.fail("inference_aborted");
1402
+ };
1403
+ try {
1404
+ return await new Promise((resolve, reject) => {
1405
+ this.pending = { id, resolve, reject };
1406
+ signal.addEventListener("abort", abort, { once: true });
1407
+ if (signal.aborted)
1408
+ abort();
1409
+ else {
1410
+ this.phase = "request_sent";
1411
+ this.child.stdin.write(wire);
1412
+ }
1413
+ });
1414
+ } finally {
1415
+ signal.removeEventListener("abort", abort);
1416
+ }
1417
+ }
1418
+ stop() {
1419
+ if (this.stopPromise !== undefined)
1420
+ return this.stopPromise;
1421
+ this.stopped = true;
1422
+ this.stopPromise = (async () => {
1423
+ this.child.kill("SIGKILL");
1424
+ for (const pipe of this.pipes)
1425
+ pipe.destroy();
1426
+ await this.collected;
1427
+ this.chunks = [];
1428
+ this.bytes = 0;
1429
+ })();
1430
+ return this.stopPromise;
1431
+ }
1432
+ exitStatus() {
1433
+ const signal = this.child.signalCode;
1434
+ if (signal !== null && /^SIG[A-Z0-9]{1,16}$/.test(signal))
1435
+ return signal;
1436
+ const code = this.child.exitCode;
1437
+ return code !== null && Number.isSafeInteger(code) ? String(code) : "unknown";
1438
+ }
1439
+ }
1440
+
1441
+ class LlamaEngine {
1442
+ options;
1443
+ factory;
1444
+ modelId;
1445
+ worker = null;
1446
+ queue = Promise.resolve();
1447
+ disposed = false;
1448
+ shutdown = new AbortController;
1449
+ disposePromise;
1450
+ constructor(options, factory = defaultWorker) {
1451
+ this.options = options;
1452
+ this.factory = factory;
1453
+ this.modelId = options.modelId;
1454
+ if (!Number.isInteger(options.evalTimeoutMs) || options.evalTimeoutMs < 1 || options.evalTimeoutMs > 300000) {
1455
+ throw unavailable("invalid_timeout");
1456
+ }
1457
+ }
1458
+ firstTokenDistribution(prompt, signal) {
1459
+ if (this.disposed)
1460
+ return Promise.reject(unavailable("engine_disposed"));
1461
+ if (Buffer.byteLength(prompt) > ENGINE_IPC_LIMITS.maxPromptBytes)
1462
+ return Promise.reject(unavailable("prompt_limit"));
1463
+ const timeout = new AbortController;
1464
+ const timer = setTimeout(() => timeout.abort(), this.options.evalTimeoutMs);
1465
+ const combined = AbortSignal.any([this.shutdown.signal, timeout.signal, ...signal === undefined ? [] : [signal]]);
1466
+ let active = false;
1467
+ const run = this.queue.then(async () => {
1468
+ if (combined.aborted)
1469
+ throw unavailable("inference_aborted");
1470
+ active = true;
1471
+ this.worker ??= new EngineWorker(this.factory);
1472
+ const worker = this.worker;
1473
+ try {
1474
+ const response = await worker.request({ op: "evaluate", options: this.options, prompt }, combined);
1475
+ if (response.kind === "unsupported")
1476
+ throw new LocalInputError("prompt tokens exceed the model context capacity");
1477
+ if (response.kind !== "distribution") {
1478
+ throw unavailable(response.kind === "error" ? response.detail : "worker_response_mismatch");
1479
+ }
1480
+ return response.value;
1481
+ } catch (error) {
1482
+ await worker.stop();
1483
+ if (this.worker === worker)
1484
+ this.worker = null;
1485
+ throw error;
1486
+ }
1487
+ });
1488
+ this.queue = run.catch(() => {});
1489
+ return new Promise((resolve, reject) => {
1490
+ const abortQueued = () => {
1491
+ if (!active)
1492
+ reject(unavailable("inference_aborted"));
1493
+ };
1494
+ combined.addEventListener("abort", abortQueued, { once: true });
1495
+ if (combined.aborted)
1496
+ abortQueued();
1497
+ run.then(resolve, reject).finally(() => {
1498
+ clearTimeout(timer);
1499
+ combined.removeEventListener("abort", abortQueued);
1500
+ });
1501
+ });
1502
+ }
1503
+ dispose() {
1504
+ if (this.disposePromise !== undefined)
1505
+ return this.disposePromise;
1506
+ this.disposed = true;
1507
+ this.shutdown.abort();
1508
+ this.disposePromise = (async () => {
1509
+ await this.worker?.stop();
1510
+ await this.queue;
1511
+ this.worker = null;
1512
+ })();
1513
+ return this.disposePromise;
1514
+ }
1515
+ }
1516
+ async function probeNativeRuntime(options = {}) {
1517
+ const started = performance.now();
1518
+ const timeoutMs = options.timeoutMs ?? 30000;
1519
+ if (!Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 30000) {
1520
+ return { ok: false, failure_code: "invalid_timeout", elapsed_ms: 0, message: "native runtime probe failed: invalid_timeout" };
1521
+ }
1522
+ let worker;
1523
+ const controller = new AbortController;
1524
+ let deadlineReached = false;
1525
+ const timer = setTimeout(() => {
1526
+ deadlineReached = true;
1527
+ controller.abort();
1528
+ }, timeoutMs);
1529
+ const failure = (code) => {
1530
+ const elapsed = Math.max(0, Math.round(performance.now() - started));
1531
+ const phase = worker?.phase ?? "created";
1532
+ const exit = code === "worker_exited" ? `; exit=${worker?.exitStatus() ?? "unknown"}` : "";
1533
+ return {
1534
+ ok: false,
1535
+ failure_code: code,
1536
+ elapsed_ms: elapsed,
1537
+ message: `native runtime probe failed: ${code}; elapsed_ms=${elapsed}; phase=${phase}${exit}`
1538
+ };
1539
+ };
1540
+ try {
1541
+ worker = new EngineWorker(options.workerFactory ?? defaultWorker);
1542
+ const response = await worker.request({ op: "probe" }, controller.signal);
1543
+ if (response.kind !== "probe")
1544
+ return failure("worker_response_mismatch");
1545
+ const value = response.value;
1546
+ if (!value.ok)
1547
+ return failure("native_unavailable");
1548
+ return {
1549
+ ok: value.ok,
1550
+ elapsed_ms: Math.max(0, Math.round(performance.now() - started)),
1551
+ ...value.backend === undefined ? {} : { backend: value.backend },
1552
+ ...value.gpu_offloading === undefined ? {} : { gpu_offloading: value.gpu_offloading },
1553
+ ...value.supported_backends === undefined ? {} : { supported_backends: value.supported_backends }
1554
+ };
1555
+ } catch (error) {
1556
+ const allowed = new Set([
1557
+ "worker_exited",
1558
+ "worker_failed",
1559
+ "worker_input_failed",
1560
+ "worker_output_failed",
1561
+ "worker_pipes_unavailable",
1562
+ "worker_response_limit",
1563
+ "worker_response_invalid",
1564
+ "worker_response_mismatch",
1565
+ "worker_request_limit",
1566
+ "worker_stopped",
1567
+ "worker_busy"
1568
+ ]);
1569
+ return failure(deadlineReached ? "probe_timeout" : error instanceof EngineUnavailableError && allowed.has(error.detail) ? error.detail : "worker_failed");
1570
+ } finally {
1571
+ clearTimeout(timer);
1572
+ await worker?.stop();
1573
+ }
1574
+ }
1575
+
1576
+ // src/local/store.ts
1577
+ import {
1578
+ chmodSync as chmodSync2,
1579
+ closeSync,
1580
+ createReadStream,
1581
+ existsSync as existsSync2,
1582
+ lstatSync,
1583
+ mkdirSync as mkdirSync2,
1584
+ openSync,
1585
+ readFileSync as readFileSync2,
1586
+ readSync,
1587
+ renameSync,
1588
+ rmSync,
1589
+ statSync,
1590
+ writeFileSync as writeFileSync2
1591
+ } from "node:fs";
1592
+ import { createWriteStream } from "node:fs";
1593
+ import { createHash } from "node:crypto";
1594
+ import { join as join2 } from "node:path";
1595
+ import { z as z5 } from "zod";
1596
+ var MODEL_LIMITS = {
1597
+ maxManifestBytes: 65536,
1598
+ maxModelBytes: 8589934592,
1599
+ maxModels: 16,
1600
+ maxGgufTensors: 1e6,
1601
+ maxGgufMetadataEntries: 1e6,
1602
+ downloadTimeoutMs: 3600000
1603
+ };
1604
+ var modelIdSchema = z5.string().min(1).max(64).regex(/^[a-z0-9][a-z0-9.-]*$/, "lowercase letters, digits, dots, hyphens");
1605
+ var MODEL_KINDS = ["gguf"];
1606
+ var modelKindSchema = z5.enum(MODEL_KINDS);
1607
+ var KIND_EXTENSIONS = {
1608
+ gguf: "gguf"
1609
+ };
1610
+ var installedModelSchema = z5.object({
1611
+ id: modelIdSchema,
1612
+ kind: modelKindSchema.default("gguf"),
1613
+ file: z5.string().min(1).max(128).regex(/^[a-z0-9][a-z0-9.-]*\.gguf$/, "safe lowercase model filename"),
1614
+ size_b: z5.number().positive().max(1e4).optional(),
1615
+ source: z5.string().min(1).max(512),
1616
+ sha256: z5.string().regex(/^[0-9a-f]{64}$/),
1617
+ bytes: z5.number().int().positive(),
1618
+ context: z5.number().int().min(64).max(1048576).default(4096),
1619
+ installed_at: z5.string().max(64)
1620
+ });
1621
+ var manifestSchema = z5.object({
1622
+ version: z5.literal(1),
1623
+ models: z5.array(installedModelSchema).max(MODEL_LIMITS.maxModels).default([])
1624
+ });
1625
+ var MODEL_REGISTRY = [
1626
+ {
1627
+ id: "qwen3-0.6b",
1628
+ kind: "gguf",
1629
+ size_b: 0.6,
1630
+ repo: "unsloth/Qwen3-0.6B-GGUF",
1631
+ revision: "50968a4468ef4233ed78cd7c3de230dd1d61a56b",
1632
+ file: "Qwen3-0.6B-Q4_0.gguf",
1633
+ sha256: "33bcc57074ec7b6eada5a90651ee546ec0c2b271002c22baf9f1b2dd1e8f75cb",
1634
+ bytes: 382156480,
1635
+ context: 2048,
1636
+ description: "Qwen3 0.6B Q4_0: experimental diagnostic model (365 MiB)",
1637
+ experimental: true
1638
+ },
1639
+ {
1640
+ id: "qwen3-1.7b",
1641
+ kind: "gguf",
1642
+ size_b: 1.7,
1643
+ repo: "unsloth/Qwen3-1.7B-GGUF",
1644
+ revision: "d7f544eead698dbd1f15126ef60b45a1e1933222",
1645
+ file: "Qwen3-1.7B-Q4_K_M.gguf",
1646
+ sha256: "b139949c5bd74937ad8ed8c8cf3d9ffb1e99c866c823204dc42c0d91fa181897",
1647
+ bytes: 1107409472,
1648
+ context: 2048,
1649
+ description: "Qwen3 1.7B Q4_K_M: experimental default local model (1.0 GiB)",
1650
+ experimental: true
1651
+ },
1652
+ {
1653
+ id: "qwen3.5-4b",
1654
+ kind: "gguf",
1655
+ size_b: 4,
1656
+ repo: "unsloth/Qwen3.5-4B-GGUF",
1657
+ revision: "e87f176479d0855a907a41277aca2f8ee7a09523",
1658
+ file: "Qwen3.5-4B-Q4_K_M.gguf",
1659
+ sha256: "00fe7986ff5f6b463e62455821146049db6f9313603938a70800d1fb69ef11a4",
1660
+ bytes: 2740937888,
1661
+ context: 2048,
1662
+ description: "Qwen3.5 4B Q4_K_M: experimental local decision model (2.6 GiB)",
1663
+ experimental: true
1664
+ }
1665
+ ];
1666
+ function modelsDir(home) {
1667
+ return join2(home, "models");
1668
+ }
1669
+ function manifestPath(home) {
1670
+ return join2(modelsDir(home), "manifest.json");
1671
+ }
1672
+ function loadManifestChecked(home) {
1673
+ const path = manifestPath(home);
1674
+ if (!existsSync2(path))
1675
+ return { ok: true, manifest: { version: 1, models: [] } };
1676
+ let parsed;
1677
+ try {
1678
+ const stats = lstatSync(path);
1679
+ if (!stats.isFile() || stats.isSymbolicLink()) {
1680
+ return { ok: false, message: "model manifest is not a regular file" };
1681
+ }
1682
+ if (stats.size > MODEL_LIMITS.maxManifestBytes) {
1683
+ return { ok: false, message: `model manifest exceeds ${MODEL_LIMITS.maxManifestBytes} bytes` };
1684
+ }
1685
+ parsed = JSON.parse(readFileSync2(path, "utf8"));
1686
+ } catch {
1687
+ return { ok: false, message: `model manifest is not valid JSON: ${path}` };
1688
+ }
1689
+ const records = typeof parsed === "object" && parsed !== null && "models" in parsed ? parsed.models : undefined;
1690
+ if (Array.isArray(records) && records.some((model) => {
1691
+ if (typeof model !== "object" || model === null)
1692
+ return false;
1693
+ const record = model;
1694
+ return record["kind"] === "scorer" || record["kind"] === "needle" || Object.keys(record).some((key) => key.startsWith("engine_")) || typeof record["file"] === "string" && /\.(pt|cact)$/.test(record["file"]);
1695
+ })) {
1696
+ return {
1697
+ ok: false,
1698
+ message: "legacy scorer/Needle model inventory is unsupported; use a new SYS1_HOME for Qwen. Existing manifest and model files were not changed."
1699
+ };
1700
+ }
1701
+ const result = manifestSchema.safeParse(parsed);
1702
+ if (!result.success) {
1703
+ const issue = result.error.issues[0];
1704
+ return {
1705
+ ok: false,
1706
+ message: `invalid model manifest${issue === undefined ? "" : ` at ${issue.path.join(".")}: ${issue.message}`}`
1707
+ };
1708
+ }
1709
+ return { ok: true, manifest: result.data };
1710
+ }
1711
+
1712
+ class ModelStoreError extends Error {
1713
+ name = "ModelStoreError";
1714
+ }
1715
+ function loadManifest(home) {
1716
+ const result = loadManifestChecked(home);
1717
+ if (!result.ok)
1718
+ throw new ModelStoreError(result.message);
1719
+ return result.manifest;
1720
+ }
1721
+ function saveManifest(home, manifest) {
1722
+ const current = loadManifestChecked(home);
1723
+ if (!current.ok)
1724
+ throw new ModelStoreError(current.message);
1725
+ const parsed = manifestSchema.parse(manifest);
1726
+ mkdirSync2(modelsDir(home), { recursive: true, mode: 448 });
1727
+ const path = manifestPath(home);
1728
+ const temporary = `${path}.tmp`;
1729
+ writeFileSync2(temporary, `${JSON.stringify(parsed, null, 2)}
1730
+ `, { mode: 384 });
1731
+ renameSync(temporary, path);
1732
+ chmodSync2(path, 384);
1733
+ }
1734
+ function modelFilePath(home, model) {
1735
+ return join2(modelsDir(home), model.file);
1736
+ }
1737
+ function inspectGgufFile(path) {
1738
+ let descriptor = null;
1739
+ try {
1740
+ const stats = lstatSync(path);
1741
+ if (!stats.isFile() || stats.isSymbolicLink()) {
1742
+ return { ok: false, message: "model path is not a regular file" };
1743
+ }
1744
+ if (stats.size < 24)
1745
+ return { ok: false, message: "GGUF header is truncated" };
1746
+ descriptor = openSync(path, "r");
1747
+ const header = Buffer.alloc(24);
1748
+ const bytesRead = readSync(descriptor, header, 0, header.length, 0);
1749
+ if (bytesRead !== header.length)
1750
+ return { ok: false, message: "GGUF header is truncated" };
1751
+ if (header.toString("ascii", 0, 4) !== "GGUF") {
1752
+ return { ok: false, message: "file does not start with GGUF magic" };
1753
+ }
1754
+ const version = header.readUInt32LE(4);
1755
+ if (version < 1 || version > 3) {
1756
+ return { ok: false, message: `unsupported GGUF version ${version}` };
1757
+ }
1758
+ const tensors = header.readBigUInt64LE(8);
1759
+ const metadata2 = header.readBigUInt64LE(16);
1760
+ if (tensors > BigInt(MODEL_LIMITS.maxGgufTensors)) {
1761
+ return { ok: false, message: "GGUF tensor count exceeds the admission limit" };
1762
+ }
1763
+ if (metadata2 > BigInt(MODEL_LIMITS.maxGgufMetadataEntries)) {
1764
+ return { ok: false, message: "GGUF metadata count exceeds the admission limit" };
1765
+ }
1766
+ return {
1767
+ ok: true,
1768
+ version,
1769
+ tensors: Number(tensors),
1770
+ metadata_entries: Number(metadata2),
1771
+ bytes: stats.size
1772
+ };
1773
+ } catch (error) {
1774
+ return { ok: false, message: error instanceof Error ? error.message : "cannot inspect GGUF" };
1775
+ } finally {
1776
+ if (descriptor !== null)
1777
+ closeSync(descriptor);
1778
+ }
1779
+ }
1780
+ function installedModels(home) {
1781
+ return loadManifest(home).models.filter((model) => {
1782
+ try {
1783
+ const stats = lstatSync(modelFilePath(home, model));
1784
+ if (!stats.isFile() || stats.isSymbolicLink())
1785
+ return false;
1786
+ return true;
1787
+ } catch {
1788
+ return false;
1789
+ }
1790
+ });
1791
+ }
1792
+ function findInstalled(home, id) {
1793
+ return installedModels(home).find((model) => model.id === id);
1794
+ }
1795
+ function findRegistry(id) {
1796
+ return MODEL_REGISTRY.find((entry) => entry.id === id);
1797
+ }
1798
+ function resolvePullTarget(ref) {
1799
+ const entry = findRegistry(ref);
1800
+ if (entry !== undefined) {
1801
+ if (!/^[0-9a-f]{40}$/.test(entry.revision)) {
1802
+ return { error: `invalid registry revision for ${entry.id}; expected an immutable 40-character commit` };
1803
+ }
1804
+ return { kind: "registry", entry };
1805
+ }
1806
+ if (ref.startsWith("hf:")) {
1807
+ const rest = ref.slice(3);
1808
+ const colon = rest.lastIndexOf(":");
1809
+ if (colon <= 0)
1810
+ return { error: "hf ref looks like hf:<org>/<repo>:<file.gguf>" };
1811
+ const repo = rest.slice(0, colon);
1812
+ const file = rest.slice(colon + 1);
1813
+ if (!/^[\w.-]+\/[\w.-]+$/.test(repo))
1814
+ return { error: `bad repo ${repo}` };
1815
+ const segments = file.split("/");
1816
+ if (!/^[\w./-]+\.gguf$/.test(file) || file.startsWith("/") || segments.some((segment) => segment === "." || segment === "..")) {
1817
+ return { error: `file must be a relative .gguf path: ${file}` };
1818
+ }
1819
+ const modelKind = "gguf";
1820
+ const id = file.replace(/\.gguf$/, "").toLowerCase().replace(/[^a-z0-9.-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
1821
+ if (!modelIdSchema.safeParse(id).success)
1822
+ return { error: `cannot derive a model id from ${file}` };
1823
+ return { kind: "hf", modelKind, repo, file, id };
1824
+ }
1825
+ return { error: `unknown model ${ref}; run \`sys1 pull --list\` for the registry` };
1826
+ }
1827
+ async function fetchHfSha256(repo, file, fetchFn) {
1828
+ try {
1829
+ const dir = file.includes("/") ? file.slice(0, file.lastIndexOf("/")) : "";
1830
+ const treePath = dir.length > 0 ? `/${dir}` : "";
1831
+ const response = await fetchFn(`https://huggingface.co/api/models/${repo}/tree/main${treePath}`, {
1832
+ signal: AbortSignal.timeout(15000)
1833
+ });
1834
+ if (!response.ok)
1835
+ return null;
1836
+ const entries = await response.json();
1837
+ if (!Array.isArray(entries))
1838
+ return null;
1839
+ const base = file.includes("/") ? file.slice(file.lastIndexOf("/") + 1) : file;
1840
+ for (const item of entries) {
1841
+ if (typeof item !== "object" || item === null)
1842
+ continue;
1843
+ const path = item["path"];
1844
+ if (path !== file && path !== base)
1845
+ continue;
1846
+ const lfs = item["lfs"];
1847
+ if (typeof lfs === "object" && lfs !== null) {
1848
+ const oid = lfs["oid"];
1849
+ if (typeof oid === "string" && /^[0-9a-f]{64}$/.test(oid))
1850
+ return oid;
1851
+ }
1852
+ }
1853
+ return null;
1854
+ } catch {
1855
+ return null;
1856
+ }
1857
+ }
1858
+ async function downloadVerified(url, tmpPath, expectedSha, expectedBytes, fetchFn, onProgress) {
1859
+ let response;
1860
+ try {
1861
+ response = await fetchFn(url, {
1862
+ redirect: "follow",
1863
+ signal: AbortSignal.timeout(MODEL_LIMITS.downloadTimeoutMs)
1864
+ });
1865
+ } catch (error) {
1866
+ return { ok: false, message: `download failed: ${error instanceof Error ? error.name : "transport"}` };
1867
+ }
1868
+ if (!response.ok || response.body === null) {
1869
+ return { ok: false, message: `download failed: HTTP ${response.status} for ${url}` };
1870
+ }
1871
+ const lengthHeader = response.headers.get("content-length");
1872
+ const contentLength = lengthHeader === null ? null : Number(lengthHeader);
1873
+ if (contentLength !== null && Number.isFinite(contentLength) && contentLength > MODEL_LIMITS.maxModelBytes) {
1874
+ return { ok: false, message: "model exceeds the 8 GiB store limit" };
1875
+ }
1876
+ if (expectedBytes !== null && contentLength !== null && Number.isFinite(contentLength) && contentLength !== expectedBytes) {
1877
+ return { ok: false, message: `remote size mismatch for ${url}` };
1878
+ }
1879
+ const hash = createHash("sha256");
1880
+ const sink = createWriteStream(tmpPath, { mode: 384 });
1881
+ let writeFailure;
1882
+ sink.on("error", (error) => {
1883
+ writeFailure = error;
1884
+ });
1885
+ let received = 0;
1886
+ try {
1887
+ const reader = response.body.getReader();
1888
+ for (;; ) {
1889
+ const { done, value } = await reader.read();
1890
+ if (done)
1891
+ break;
1892
+ received += value.byteLength;
1893
+ if (received > MODEL_LIMITS.maxModelBytes) {
1894
+ throw new Error("model exceeds the 8 GiB store limit");
1895
+ }
1896
+ hash.update(value);
1897
+ if (!sink.write(value)) {
1898
+ await new Promise((resolveDrain, rejectDrain) => {
1899
+ const drained = () => {
1900
+ sink.off("error", failed);
1901
+ resolveDrain();
1902
+ };
1903
+ const failed = (error) => {
1904
+ sink.off("drain", drained);
1905
+ rejectDrain(error);
1906
+ };
1907
+ sink.once("drain", drained);
1908
+ sink.once("error", failed);
1909
+ });
1910
+ }
1911
+ if (writeFailure !== undefined)
1912
+ throw writeFailure;
1913
+ onProgress?.(received, expectedBytes);
1914
+ }
1915
+ await new Promise((resolveEnd, rejectEnd) => {
1916
+ sink.once("finish", resolveEnd);
1917
+ sink.once("error", rejectEnd);
1918
+ sink.end();
1919
+ });
1920
+ if (writeFailure !== undefined)
1921
+ throw writeFailure;
1922
+ } catch (error) {
1923
+ sink.destroy();
1924
+ rmSync(tmpPath, { force: true });
1925
+ return { ok: false, message: `download failed: ${error instanceof Error ? error.message : "io"}` };
1926
+ }
1927
+ const digest = hash.digest("hex");
1928
+ if (digest !== expectedSha) {
1929
+ rmSync(tmpPath, { force: true });
1930
+ return {
1931
+ ok: false,
1932
+ message: `sha256 mismatch: got ${digest.slice(0, 16)}…, expected ${expectedSha.slice(0, 16)}…`
1933
+ };
1934
+ }
1935
+ if (expectedBytes !== null && received !== expectedBytes) {
1936
+ rmSync(tmpPath, { force: true });
1937
+ return { ok: false, message: `size mismatch: got ${received}, expected ${expectedBytes}` };
1938
+ }
1939
+ return { ok: true, received, digest };
1940
+ }
1941
+ async function pullModel(home, ref, options = {}) {
1942
+ const target = resolvePullTarget(ref);
1943
+ if ("error" in target)
1944
+ return { ok: false, message: target.error };
1945
+ const fetchFn = options.fetchFn ?? fetch;
1946
+ const id = target.kind === "registry" ? target.entry.id : target.id;
1947
+ const repo = target.kind === "registry" ? target.entry.repo : target.repo;
1948
+ const revision = target.kind === "registry" ? target.entry.revision : "main";
1949
+ const file = target.kind === "registry" ? target.entry.file : target.file;
1950
+ const modelKind = target.kind === "registry" ? target.entry.kind : target.modelKind;
1951
+ const loaded = loadManifestChecked(home);
1952
+ if (!loaded.ok)
1953
+ return { ok: false, message: loaded.message };
1954
+ const manifest = loaded.manifest;
1955
+ if (manifest.models.some((model) => model.id === id)) {
1956
+ return { ok: false, message: `${id} is already installed` };
1957
+ }
1958
+ const extension = KIND_EXTENSIONS[modelKind];
1959
+ const finalPath = join2(modelsDir(home), `${id}.${extension}`);
1960
+ if (existsSync2(finalPath)) {
1961
+ return { ok: false, message: `${finalPath} exists but is not registered; move it before pulling` };
1962
+ }
1963
+ if (manifest.models.length >= MODEL_LIMITS.maxModels) {
1964
+ return { ok: false, message: `model store is full (${MODEL_LIMITS.maxModels})` };
1965
+ }
1966
+ let expectedSha = options.sha256;
1967
+ let expectedBytes = null;
1968
+ if (target.kind === "registry") {
1969
+ expectedSha = target.entry.sha256;
1970
+ expectedBytes = target.entry.bytes;
1971
+ } else if (expectedSha === undefined) {
1972
+ expectedSha = await fetchHfSha256(repo, file, fetchFn) ?? undefined;
1973
+ }
1974
+ if (expectedSha === undefined) {
1975
+ return {
1976
+ ok: false,
1977
+ message: `no sha256 available for ${repo}:${file}; pass --sha256 to verify the download`
1978
+ };
1979
+ }
1980
+ mkdirSync2(modelsDir(home), { recursive: true, mode: 448 });
1981
+ const tmpPath = `${finalPath}.download`;
1982
+ const weights = await downloadVerified(`https://huggingface.co/${repo}/resolve/${revision}/${file}`, tmpPath, expectedSha, expectedBytes, fetchFn, options.onProgress);
1983
+ if (!weights.ok)
1984
+ return { ok: false, message: weights.message ?? "download failed" };
1985
+ const received = weights.received ?? 0;
1986
+ const digest = weights.digest ?? "";
1987
+ const inspection = inspectGgufFile(tmpPath);
1988
+ if (!inspection.ok) {
1989
+ rmSync(tmpPath, { force: true });
1990
+ return { ok: false, message: `invalid GGUF ${file}: ${inspection.message}` };
1991
+ }
1992
+ renameSync(tmpPath, finalPath);
1993
+ chmodSync2(finalPath, 384);
1994
+ const next = structuredClone(manifest);
1995
+ next.models.push({
1996
+ id,
1997
+ kind: modelKind,
1998
+ file: `${id}.${extension}`,
1999
+ ...target.kind === "registry" ? { size_b: target.entry.size_b } : {},
2000
+ source: `hf:${repo}:${file}`,
2001
+ sha256: digest,
2002
+ bytes: received,
2003
+ context: target.kind === "registry" ? target.entry.context : 2048,
2004
+ installed_at: new Date().toISOString()
2005
+ });
2006
+ saveManifest(home, next);
2007
+ return { ok: true, id, path: finalPath, bytes: received };
2008
+ }
2009
+ function removeModel(home, id) {
2010
+ const loaded = loadManifestChecked(home);
2011
+ if (!loaded.ok)
2012
+ return { ok: false, message: loaded.message };
2013
+ const manifest = loaded.manifest;
2014
+ const model = manifest.models.find((entry) => entry.id === id);
2015
+ if (model === undefined)
2016
+ return { ok: false, message: `no installed model named ${id}` };
2017
+ rmSync(modelFilePath(home, model), { force: true });
2018
+ manifest.models = manifest.models.filter((entry) => entry.id !== id);
2019
+ saveManifest(home, manifest);
2020
+ return { ok: true, message: `removed ${id}` };
2021
+ }
2022
+ async function hashFile(path) {
2023
+ const hash = createHash("sha256");
2024
+ try {
2025
+ for await (const chunk of createReadStream(path))
2026
+ hash.update(chunk);
2027
+ return hash.digest("hex");
2028
+ } catch {
2029
+ return null;
2030
+ }
2031
+ }
2032
+ async function verifyModel(home, id) {
2033
+ const loaded = loadManifestChecked(home);
2034
+ if (!loaded.ok)
2035
+ return { ok: false, message: loaded.message };
2036
+ const model = loaded.manifest.models.find((entry) => entry.id === id);
2037
+ if (model === undefined)
2038
+ return { ok: false, message: `no installed model named ${id}` };
2039
+ const path = modelFilePath(home, model);
2040
+ const actual = await hashFile(path);
2041
+ if (actual === null)
2042
+ return { ok: false, message: "cannot read model" };
2043
+ if (actual !== model.sha256) {
2044
+ return { ok: false, expected: model.sha256, actual, message: "sha256 mismatch" };
2045
+ }
2046
+ const gguf = inspectGgufFile(path);
2047
+ if (!gguf.ok) {
2048
+ return {
2049
+ ok: false,
2050
+ expected: model.sha256,
2051
+ actual,
2052
+ message: `invalid GGUF: ${gguf.message}`
2053
+ };
2054
+ }
2055
+ return { ok: true, expected: model.sha256, actual, gguf };
2056
+ }
2057
+ function storeBytes(home) {
2058
+ return installedModels(home).reduce((total, model) => {
2059
+ let bytes = 0;
2060
+ try {
2061
+ bytes += statSync(modelFilePath(home, model)).size;
2062
+ } catch {
2063
+ return total;
2064
+ }
2065
+ return total + bytes;
2066
+ }, 0);
2067
+ }
2068
+
2069
+ // src/local/runner.ts
2070
+ var BUILTIN_PREFIX = "local-";
2071
+ function builtinName(modelId) {
2072
+ return `${BUILTIN_PREFIX}${modelId}`;
2073
+ }
2074
+ function builtinCandidates(home, enabled, selectedModel = DEFAULT_LOCAL_MODELS.quality) {
2075
+ if (!enabled)
2076
+ return [];
2077
+ return installedModels(home).map((model) => ({
2078
+ name: builtinName(model.id),
2079
+ kind: "local",
2080
+ builtin: true,
2081
+ model,
2082
+ available: true,
2083
+ models: [model.id],
2084
+ size_b: model.size_b ?? null,
2085
+ cost_rank: 0,
2086
+ explicitOnly: model.id !== selectedModel,
2087
+ capabilities: { maxOptions: DECIDE_LIMITS.maxLabels }
2088
+ }));
2089
+ }
2090
+ function defaultEngineFactory(contextSize, evalTimeoutMs) {
2091
+ return (model, path) => new LlamaEngine({
2092
+ modelPath: path,
2093
+ modelId: model.id,
2094
+ contextSize,
2095
+ evalTimeoutMs
2096
+ });
2097
+ }
2098
+
2099
+ class LocalRunner {
2100
+ options;
2101
+ engines = new Map;
2102
+ shutdownController = new AbortController;
2103
+ requestQueue = Promise.resolve();
2104
+ constructor(options) {
2105
+ this.options = options;
2106
+ }
2107
+ loadedModels() {
2108
+ return [...this.engines.keys()];
2109
+ }
2110
+ residentCount() {
2111
+ return this.engines.size;
2112
+ }
2113
+ async evictOldest() {
2114
+ let oldest = null;
2115
+ for (const [id, entry] of this.engines) {
2116
+ if (oldest === null || entry.touched < oldest.touched)
2117
+ oldest = { id, touched: entry.touched };
2118
+ }
2119
+ if (oldest === null)
2120
+ return;
2121
+ const stale = this.engines.get(oldest.id);
2122
+ this.engines.delete(oldest.id);
2123
+ if (stale !== undefined)
2124
+ await stale.engine.dispose().catch(() => {});
2125
+ }
2126
+ async engineFor(model) {
2127
+ const cached = this.engines.get(model.id);
2128
+ if (cached !== undefined) {
2129
+ cached.touched = Date.now();
2130
+ return cached.engine;
2131
+ }
2132
+ if (this.residentCount() >= this.options.maxLoadedModels) {
2133
+ await this.evictOldest();
2134
+ }
2135
+ const engine = this.options.engineFactory(model, modelFilePath(this.options.home, model));
2136
+ this.engines.set(model.id, { engine, touched: Date.now() });
2137
+ return engine;
2138
+ }
2139
+ decide(request, modelId, signal) {
2140
+ const activeSignal = signal === undefined ? this.shutdownController.signal : AbortSignal.any([signal, this.shutdownController.signal]);
2141
+ const run = this.requestQueue.then(() => this.decideNow(request, modelId, activeSignal)).catch((error) => ({
2142
+ ok: false,
2143
+ error: {
2144
+ type: error instanceof LocalInputError ? "local_question_unsupported" : activeSignal.aborted ? "inference_timeout" : "inference_failed",
2145
+ message: error instanceof Error ? error.message : "local inference failed"
2146
+ }
2147
+ }));
2148
+ this.requestQueue = run;
2149
+ return run;
2150
+ }
2151
+ async decideNow(request, modelId, signal) {
2152
+ signal?.throwIfAborted();
2153
+ const model = findInstalled(this.options.home, modelId);
2154
+ if (model === undefined) {
2155
+ return {
2156
+ ok: false,
2157
+ error: { type: "unknown_model", message: `no installed model named ${modelId}` }
2158
+ };
2159
+ }
2160
+ return this.decideGguf(request, model, signal);
2161
+ }
2162
+ async decideGguf(request, model, signal) {
2163
+ const questions = questionEntries(request);
2164
+ const unsupported = questions.find(([, question]) => question.type === "choice" ? Object.keys(question.criteria).length > DECIDE_LIMITS.maxLabels : question.type === "score" && question.criteria.length > DECIDE_LIMITS.maxLabels);
2165
+ if (unsupported !== undefined) {
2166
+ return {
2167
+ ok: false,
2168
+ error: {
2169
+ type: "local_question_unsupported",
2170
+ message: `builtin GGUF models support at most ${DECIDE_LIMITS.maxLabels} choice options`
2171
+ }
2172
+ };
2173
+ }
2174
+ let engine;
2175
+ try {
2176
+ engine = await this.engineFor(model);
2177
+ } catch (error) {
2178
+ return {
2179
+ ok: false,
2180
+ error: {
2181
+ type: "engine_unavailable",
2182
+ message: error instanceof Error ? error.message : "engine failed to load"
2183
+ }
2184
+ };
2185
+ }
2186
+ const answers = {};
2187
+ const diagnostics = {};
2188
+ let inputTokens = 0;
2189
+ try {
2190
+ signal?.throwIfAborted();
2191
+ for (const [name, question] of questions) {
2192
+ signal?.throwIfAborted();
2193
+ const labels = answerLabels(question);
2194
+ const prompt = decisionPrompt(request.state, question);
2195
+ const distribution = await engine.firstTokenDistribution(prompt, signal);
2196
+ inputTokens += distribution.inputTokens;
2197
+ const mass = aggregateMass(distribution.entries, labels);
2198
+ const outcome = outcomeFromMass(mass, labels);
2199
+ if (outcome === null) {
2200
+ return {
2201
+ ok: false,
2202
+ error: {
2203
+ type: "inference_unreadable",
2204
+ message: `model assigned no probability mass to allowed labels for ${name}`
2205
+ }
2206
+ };
2207
+ }
2208
+ answers[name] = toLocalAnswer(question, outcome);
2209
+ diagnostics[name] = {
2210
+ coverage: Math.round(outcome.coverage * 1000) / 1000,
2211
+ concentration: Math.round(confidenceOf(outcome.distribution) * 1000) / 1000
2212
+ };
2213
+ }
2214
+ } catch (error) {
2215
+ const unavailable2 = error instanceof EngineUnavailableError;
2216
+ return {
2217
+ ok: false,
2218
+ error: {
2219
+ type: error instanceof LocalInputError ? "local_question_unsupported" : signal?.aborted === true ? "inference_timeout" : unavailable2 ? "engine_unavailable" : "inference_failed",
2220
+ message: error instanceof Error ? error.message : "local inference failed"
2221
+ }
2222
+ };
2223
+ }
2224
+ return {
2225
+ ok: true,
2226
+ adapter: "generic-gguf",
2227
+ response: toLocalResponse(model.id, answers, {
2228
+ input_tokens: inputTokens,
2229
+ output_tokens: 0
2230
+ }),
2231
+ diagnostics
2232
+ };
2233
+ }
2234
+ async dispose() {
2235
+ this.shutdownController.abort();
2236
+ await this.requestQueue.catch(() => {});
2237
+ const engines = [...this.engines.values()];
2238
+ this.engines.clear();
2239
+ await Promise.all(engines.map((entry) => entry.engine.dispose().catch(() => {})));
2240
+ }
2241
+ }
2242
+
2243
+ // src/backends.ts
2244
+ var HOSTED_BACKEND_NAME = "typesafe";
2245
+ var MAX_PROBE_BYTES = 1048576;
2246
+ var MAX_RESPONSE_BYTES2 = 4194304;
2247
+ function trimTrailingSlashes(value) {
2248
+ let end = value.length;
2249
+ while (end > 0 && value[end - 1] === "/")
2250
+ end -= 1;
2251
+ return value.slice(0, end);
2252
+ }
2253
+ function boundedSignal(timeoutMs, signal) {
2254
+ const timeout = AbortSignal.timeout(timeoutMs);
2255
+ return signal === undefined ? timeout : AbortSignal.any([signal, timeout]);
2256
+ }
2257
+ var modelsResponseSchema = z6.union([
2258
+ z6.object({ data: z6.array(z6.union([z6.string(), z6.object({ id: z6.string() })])) }),
2259
+ z6.object({ models: z6.array(z6.union([z6.string(), z6.object({ id: z6.string() })])) }),
2260
+ z6.array(z6.union([z6.string(), z6.object({ id: z6.string() })]))
2261
+ ]);
2262
+ var limitsResponseSchema = z6.object({
2263
+ max_answers_per_question: z6.number().int().positive().optional(),
2264
+ max_questions: z6.number().int().positive().optional()
2265
+ }).loose();
2266
+ function extractModelIds(body) {
2267
+ const parsed = modelsResponseSchema.safeParse(body);
2268
+ if (!parsed.success)
2269
+ return [];
2270
+ const list = Array.isArray(parsed.data) ? parsed.data : ("data" in parsed.data) ? parsed.data.data : parsed.data.models;
2271
+ return list.map((entry) => typeof entry === "string" ? entry : entry.id).filter((id) => id.length > 0).slice(0, 512);
2272
+ }
2273
+ function extractBackendCapabilities(body) {
2274
+ const parsed = limitsResponseSchema.safeParse(body);
2275
+ if (!parsed.success)
2276
+ return null;
2277
+ return {
2278
+ ...parsed.data.max_answers_per_question === undefined ? {} : { maxOptions: parsed.data.max_answers_per_question },
2279
+ ...parsed.data.max_questions === undefined ? {} : { maxQuestions: parsed.data.max_questions }
2280
+ };
2281
+ }
2282
+ function constrainedCapabilities(configured, published) {
2283
+ const maxOptions = configured?.maxOptions === undefined ? published.maxOptions : published.maxOptions === undefined ? configured.maxOptions : Math.min(configured.maxOptions, published.maxOptions);
2284
+ const maxQuestions = configured?.maxQuestions === undefined ? published.maxQuestions : published.maxQuestions === undefined ? configured.maxQuestions : Math.min(configured.maxQuestions, published.maxQuestions);
2285
+ return {
2286
+ ...maxOptions === undefined ? {} : { maxOptions },
2287
+ ...maxQuestions === undefined ? {} : { maxQuestions }
2288
+ };
2289
+ }
2290
+ function runtimeBackends(config, env, home) {
2291
+ const backends = [];
2292
+ if (config.hosted.enabled) {
2293
+ const apiKey = env[config.hosted.api_key_env];
2294
+ if (apiKey !== undefined && apiKey.length > 0) {
2295
+ backends.push({
2296
+ name: HOSTED_BACKEND_NAME,
2297
+ kind: "hosted",
2298
+ available: false,
2299
+ models: [config.hosted.model],
2300
+ size_b: null,
2301
+ cost_rank: 0,
2302
+ base_url: trimTrailingSlashes(config.hosted.base_url),
2303
+ headers: { authorization: `Bearer ${apiKey}` },
2304
+ default_model: config.hosted.model
2305
+ });
2306
+ }
2307
+ }
2308
+ for (const local of config.backends) {
2309
+ if (!local.enabled)
2310
+ continue;
2311
+ backends.push(localRuntimeBackend(local));
2312
+ }
2313
+ if (home !== undefined && config.local.enabled && config.routing.policy !== "hosted-only") {
2314
+ for (const candidate of builtinCandidates(home, true, config.local.model)) {
2315
+ backends.push({
2316
+ name: candidate.name,
2317
+ kind: "local",
2318
+ available: candidate.available,
2319
+ models: candidate.models,
2320
+ size_b: candidate.size_b,
2321
+ cost_rank: candidate.cost_rank,
2322
+ explicitOnly: candidate.explicitOnly,
2323
+ capabilities: candidate.capabilities,
2324
+ base_url: "",
2325
+ headers: {},
2326
+ default_model: candidate.model.id,
2327
+ builtin: {
2328
+ model: candidate.model,
2329
+ path: join3(modelsDir(home), candidate.model.file)
2330
+ }
2331
+ });
2332
+ }
2333
+ }
2334
+ return backends;
2335
+ }
2336
+ function localRuntimeBackend(local) {
2337
+ const hostname = new URL(local.base_url).hostname;
2338
+ const host = hostname.startsWith("[") ? hostname.slice(1, -1) : hostname;
2339
+ return {
2340
+ name: local.name,
2341
+ explicitOnly: true,
2342
+ kind: isLoopbackHost(host) ? "local" : "hosted",
2343
+ available: false,
2344
+ models: [local.model],
2345
+ size_b: local.size_b ?? null,
2346
+ cost_rank: local.cost_rank ?? 0,
2347
+ ...local.capabilities === undefined ? {} : {
2348
+ capabilities: {
2349
+ ...local.capabilities.max_options === undefined ? {} : { maxOptions: local.capabilities.max_options },
2350
+ ...local.capabilities.max_questions === undefined ? {} : { maxQuestions: local.capabilities.max_questions }
2351
+ }
2352
+ },
2353
+ base_url: trimTrailingSlashes(local.base_url),
2354
+ headers: {},
2355
+ default_model: local.model,
2356
+ ...local.adapter === undefined ? {} : { adapter: local.adapter }
2357
+ };
2358
+ }
2359
+ async function probeLimits(backend, timeoutMs, fetchFn, signal) {
2360
+ try {
2361
+ const activeSignal = boundedSignal(timeoutMs, signal);
2362
+ activeSignal.throwIfAborted();
2363
+ const response = await fetchFn(`${backend.base_url}/v1/limits`, {
2364
+ method: "GET",
2365
+ headers: { accept: "application/json", ...backend.headers },
2366
+ redirect: "manual",
2367
+ signal: activeSignal
2368
+ });
2369
+ if (!response.ok) {
2370
+ response.body?.cancel().catch(() => {});
2371
+ return;
2372
+ }
2373
+ const published = extractBackendCapabilities(JSON.parse(await readBoundedText(response, MAX_PROBE_BYTES, activeSignal)));
2374
+ if (published === null)
2375
+ return;
2376
+ backend.capabilities = constrainedCapabilities(backend.capabilities, published);
2377
+ } catch {}
2378
+ }
2379
+ async function probeBackend(backend, timeoutMs, fetchFn = fetch, signal) {
2380
+ signal?.throwIfAborted();
2381
+ if (backend.builtin !== undefined) {
2382
+ const present = existsSync3(backend.builtin.path);
2383
+ return {
2384
+ available: present,
2385
+ models: backend.models,
2386
+ latency_ms: 0,
2387
+ detail: present ? null : "model file missing"
2388
+ };
2389
+ }
2390
+ const started = Date.now();
2391
+ try {
2392
+ const activeSignal = boundedSignal(timeoutMs, signal);
2393
+ const response = await fetchFn(`${backend.base_url}/v1/models`, {
2394
+ method: "GET",
2395
+ headers: { accept: "application/json", ...backend.headers },
2396
+ redirect: "manual",
2397
+ signal: activeSignal
2398
+ });
2399
+ const latency = Date.now() - started;
2400
+ if (!response.ok) {
2401
+ response.body?.cancel().catch(() => {});
2402
+ return {
2403
+ available: false,
2404
+ models: backend.models,
2405
+ latency_ms: latency,
2406
+ detail: `GET /v1/models -> ${response.status}`
2407
+ };
2408
+ }
2409
+ const body = JSON.parse(await readBoundedText(response, MAX_PROBE_BYTES, activeSignal));
2410
+ const models = extractModelIds(body);
2411
+ await probeLimits(backend, timeoutMs, fetchFn, signal);
2412
+ return {
2413
+ available: true,
2414
+ models: models.length > 0 ? models : backend.models,
2415
+ latency_ms: latency,
2416
+ detail: null
2417
+ };
2418
+ } catch (error) {
2419
+ return {
2420
+ available: false,
2421
+ models: backend.models,
2422
+ latency_ms: null,
2423
+ detail: error instanceof Error ? error.name : "probe_failed"
2424
+ };
2425
+ }
2426
+ }
2427
+ async function probeAll(backends, timeoutMs, fetchFn = fetch, signal) {
2428
+ const results = await Promise.all(backends.map(async (backend) => {
2429
+ const result = await probeBackend(backend, timeoutMs, fetchFn, signal);
2430
+ backend.available = result.available;
2431
+ backend.models = result.models;
2432
+ return [backend.name, result];
2433
+ }));
2434
+ return new Map(results);
2435
+ }
2436
+ async function forwardToBackend(backend, rawBody, resolvedModel, timeoutMs, fetchFn = fetch, signal) {
2437
+ let body = rawBody;
2438
+ if (resolvedModel !== undefined) {
2439
+ try {
2440
+ const parsed = JSON.parse(rawBody);
2441
+ parsed["model"] = resolvedModel;
2442
+ body = JSON.stringify(parsed);
2443
+ } catch {}
2444
+ }
2445
+ const activeSignal = boundedSignal(timeoutMs, signal);
2446
+ let response;
2447
+ try {
2448
+ activeSignal.throwIfAborted();
2449
+ response = await fetchFn(`${backend.base_url}/v1/systemone`, {
2450
+ method: "POST",
2451
+ headers: {
2452
+ "content-type": "application/json",
2453
+ accept: "application/json",
2454
+ ...backend.headers
2455
+ },
2456
+ body,
2457
+ redirect: "manual",
2458
+ signal: activeSignal
2459
+ });
2460
+ } catch (error) {
2461
+ const detail = error instanceof Error && error.name === "TimeoutError" ? "backend timeout" : error instanceof Error ? error.name : "transport failure";
2462
+ return { kind: "transport", detail };
2463
+ }
2464
+ try {
2465
+ const text = await readBoundedText(response, MAX_RESPONSE_BYTES2, activeSignal);
2466
+ return {
2467
+ kind: "response",
2468
+ status: response.status,
2469
+ body: text,
2470
+ content_type: response.headers.get("content-type") ?? "application/json"
2471
+ };
2472
+ } catch (error) {
2473
+ return {
2474
+ kind: "response",
2475
+ status: 502,
2476
+ body: JSON.stringify(errorBody(error instanceof HttpBodyLimitError ? "backend_response_too_large" : "backend_response_unreadable", error instanceof HttpBodyLimitError ? "backend response exceeds 4 MiB" : "backend response body could not be read")),
2477
+ content_type: "application/json"
2478
+ };
2479
+ }
2480
+ }
2481
+ // src/qualification.ts
2482
+ var QUALIFICATION_BODY_LIMIT = 4194304;
2483
+ async function boundedText(response) {
2484
+ if (response.body === null)
2485
+ return "";
2486
+ const reader = response.body.getReader();
2487
+ const chunks = [];
2488
+ let size = 0;
2489
+ try {
2490
+ for (;; ) {
2491
+ const next = await reader.read();
2492
+ if (next.done)
2493
+ break;
2494
+ size += next.value.byteLength;
2495
+ if (size > QUALIFICATION_BODY_LIMIT) {
2496
+ await reader.cancel();
2497
+ throw new Error("response exceeds 4 MiB");
2498
+ }
2499
+ chunks.push(next.value);
2500
+ }
2501
+ } finally {
2502
+ reader.releaseLock();
2503
+ }
2504
+ return new TextDecoder().decode(Buffer.concat(chunks, size));
2505
+ }
2506
+ async function requestJson(url, init, timeoutMs, fetchFn) {
2507
+ try {
2508
+ const response = await fetchFn(url, {
2509
+ ...init,
2510
+ signal: AbortSignal.timeout(timeoutMs)
2511
+ });
2512
+ const text = await boundedText(response);
2513
+ if (!response.ok)
2514
+ return { ok: false, detail: `HTTP ${response.status}` };
2515
+ try {
2516
+ return { ok: true, body: JSON.parse(text) };
2517
+ } catch {
2518
+ return { ok: false, detail: "response is not valid JSON" };
2519
+ }
2520
+ } catch (error) {
2521
+ return {
2522
+ ok: false,
2523
+ detail: error instanceof Error ? error.message.slice(0, 256) : "request failed"
2524
+ };
2525
+ }
2526
+ }
2527
+ function withoutTrailingSlashes(value) {
2528
+ let end = value.length;
2529
+ while (end > 0 && value[end - 1] === "/")
2530
+ end -= 1;
2531
+ return value.slice(0, end);
2532
+ }
2533
+ function distributionTotal(probabilities) {
2534
+ return Object.values(probabilities).reduce((sum, probability) => sum + probability, 0);
2535
+ }
2536
+ function responseContract(body) {
2537
+ const parsed = systemOneResponseSchema.safeParse(body);
2538
+ if (!parsed.success)
2539
+ return "response does not match the System One schema";
2540
+ const refund = parsed.data.answers["refund"];
2541
+ const department = parsed.data.answers["department"];
2542
+ const urgency = parsed.data.answers["urgency"];
2543
+ if (refund?.type !== "noul" || department?.type !== "choice" || urgency?.type !== "score") {
2544
+ return "response does not preserve the requested answer types";
2545
+ }
2546
+ if (Object.keys(parsed.data.answers).length !== 3) {
2547
+ return "response answer keys differ from the request";
2548
+ }
2549
+ if (Object.keys(department.probabilities).sort().join(",") !== "billing,technical") {
2550
+ return "choice probability keys differ from the request";
2551
+ }
2552
+ if (Object.keys(urgency.legend).join(",") !== "0,1,2" || Object.keys(urgency.probabilities).join(",") !== "0,1,2") {
2553
+ return "score keys differ from the request";
2554
+ }
2555
+ if (Math.abs(distributionTotal(department.probabilities) - 1) > 0.02) {
2556
+ return "choice probabilities are not normalized";
2557
+ }
2558
+ if (Math.abs(distributionTotal(urgency.probabilities) - 1) > 0.02) {
2559
+ return "score probabilities are not normalized";
2560
+ }
2561
+ const expected = Object.entries(urgency.probabilities).reduce((sum, [level, probability]) => sum + Number(level) * probability, 0);
2562
+ if (Math.abs(expected - urgency.score) > 0.02) {
2563
+ return "score does not match its probability-weighted levels";
2564
+ }
2565
+ return null;
2566
+ }
2567
+ var QUALIFICATION_REQUEST = {
2568
+ state: "The customer was charged twice, requests a refund, and can still use checkout.",
2569
+ questions: {
2570
+ refund: {
2571
+ type: "noul",
2572
+ instructions: "Does the customer explicitly request a refund?"
2573
+ },
2574
+ department: {
2575
+ type: "choice",
2576
+ instructions: "Which team should handle this request?",
2577
+ criteria: {
2578
+ billing: "Charges, payments, and refunds",
2579
+ technical: "Software defects and unavailable features"
2580
+ }
2581
+ },
2582
+ urgency: {
2583
+ type: "score",
2584
+ instructions: "Rate current operational urgency.",
2585
+ criteria: ["no active impact", "limited impact", "critical outage"]
2586
+ }
2587
+ }
2588
+ };
2589
+ async function qualifyBackend(backend, options) {
2590
+ const fetchFn = options.fetchFn ?? fetch;
2591
+ const base = withoutTrailingSlashes(backend.base_url);
2592
+ const checks = [];
2593
+ const modelsResponse = await requestJson(`${base}/v1/models`, { method: "GET", redirect: "manual", headers: { accept: "application/json" } }, options.probeTimeoutMs, fetchFn);
2594
+ if (!modelsResponse.ok) {
2595
+ checks.push({ id: "models", status: "fail", summary: "model discovery failed", detail: modelsResponse.detail });
2596
+ } else {
2597
+ const models = extractModelIds(modelsResponse.body);
2598
+ checks.push(models.includes(backend.model) ? { id: "models", status: "pass", summary: `backend serves ${backend.model}` } : {
2599
+ id: "models",
2600
+ status: "fail",
2601
+ summary: `backend does not advertise ${backend.model}`,
2602
+ detail: models.length === 0 ? "no valid model ids returned" : `advertised: ${models.slice(0, 8).join(", ")}`
2603
+ });
2604
+ }
2605
+ const limitsResponse = await requestJson(`${base}/v1/limits`, { method: "GET", redirect: "manual", headers: { accept: "application/json" } }, options.probeTimeoutMs, fetchFn);
2606
+ if (!limitsResponse.ok) {
2607
+ checks.push(backend.capabilities === undefined ? { id: "limits", status: "warn", summary: "backend does not publish limits", detail: limitsResponse.detail } : { id: "limits", status: "fail", summary: "configured capabilities could not be verified", detail: limitsResponse.detail });
2608
+ } else {
2609
+ const capabilities = extractBackendCapabilities(limitsResponse.body);
2610
+ if (capabilities === null) {
2611
+ checks.push({ id: "limits", status: "fail", summary: "limits response is invalid" });
2612
+ } else {
2613
+ const configured = backend.capabilities;
2614
+ const tooSmall = configured?.max_options !== undefined && (capabilities.maxOptions === undefined || capabilities.maxOptions < configured.max_options) || configured?.max_questions !== undefined && (capabilities.maxQuestions === undefined || capabilities.maxQuestions < configured.max_questions);
2615
+ checks.push(tooSmall ? { id: "limits", status: "fail", summary: "published limits are below the configured capabilities" } : {
2616
+ id: "limits",
2617
+ status: "pass",
2618
+ summary: `limits published${capabilities.maxOptions === undefined ? "" : ` · ${capabilities.maxOptions} options`}${capabilities.maxQuestions === undefined ? "" : ` · ${capabilities.maxQuestions} questions`}`
2619
+ });
2620
+ }
2621
+ }
2622
+ const qualification = { ...QUALIFICATION_REQUEST, model: backend.model };
2623
+ const decisionResponse = await requestJson(`${base}/v1/systemone`, {
2624
+ method: "POST",
2625
+ redirect: "manual",
2626
+ headers: { accept: "application/json", "content-type": "application/json" },
2627
+ body: JSON.stringify(backend.adapter === "kev" ? adaptKevRequest(qualification) : qualification)
2628
+ }, options.requestTimeoutMs, fetchFn);
2629
+ if (!decisionResponse.ok) {
2630
+ checks.push({ id: "systemone", status: "fail", summary: "System One qualification request failed", detail: decisionResponse.detail });
2631
+ } else {
2632
+ let issue;
2633
+ if (backend.adapter === "kev") {
2634
+ try {
2635
+ adaptKevResponse(qualification, decisionResponse.body);
2636
+ issue = null;
2637
+ } catch {
2638
+ issue = "response does not match the Kev decision contract";
2639
+ }
2640
+ } else {
2641
+ issue = responseContract(decisionResponse.body);
2642
+ }
2643
+ checks.push(issue === null ? { id: "systemone", status: "pass", summary: "noul, choice, and score response is conformant" } : { id: "systemone", status: "fail", summary: issue });
2644
+ }
2645
+ return {
2646
+ version: 1,
2647
+ backend: backend.name,
2648
+ base_url: base,
2649
+ model: backend.model,
2650
+ ok: checks.every((check) => check.status !== "fail"),
2651
+ checks
2652
+ };
2653
+ }
2654
+ // src/gateway.ts
2655
+ var SYS1_VERSION = "0.17.0";
2656
+ var MAX_ATTEMPTS = 2;
2657
+ function currentConfig(deps) {
2658
+ return deps.reloadConfig === undefined ? deps.config : deps.reloadConfig();
2659
+ }
2660
+ function json(body, status = 200, headers = {}) {
2661
+ return new Response(JSON.stringify(body), {
2662
+ status,
2663
+ headers: { "content-type": "application/json", ...headers }
2664
+ });
2665
+ }
2666
+ function forwardModel(requested, backend) {
2667
+ if (requested === undefined || requested === "auto")
2668
+ return backend.default_model;
2669
+ const slash = requested.indexOf("/");
2670
+ if (slash > 0)
2671
+ return requested.slice(slash + 1);
2672
+ return requested;
2673
+ }
2674
+ function localDiagnosticHeaders(adapter, diagnostics) {
2675
+ const values = diagnostics === undefined ? [] : Object.values(diagnostics);
2676
+ const coverage = values.length === 0 ? 0 : Math.min(...values.map((value) => value.coverage));
2677
+ const concentration = values.length === 0 ? 0 : Math.min(...values.map((value) => value.concentration));
2678
+ return {
2679
+ "x-sys1-local-adapter": adapter ?? "generic-gguf",
2680
+ "x-sys1-local-min-coverage": coverage.toFixed(3),
2681
+ "x-sys1-local-min-concentration": concentration.toFixed(3)
2682
+ };
2683
+ }
2684
+ function createFetchHandler(deps) {
2685
+ const fetchFn = deps.fetchFn ?? fetch;
2686
+ let runner = deps.localRunner ?? null;
2687
+ function localRunner(config) {
2688
+ if (deps.home === undefined)
2689
+ return null;
2690
+ if (runner === null) {
2691
+ runner = new LocalRunner({
2692
+ home: deps.home,
2693
+ maxLoadedModels: config.local.max_loaded_models,
2694
+ engineFactory: defaultEngineFactory(config.local.context_tokens, config.local.eval_timeout_ms)
2695
+ });
2696
+ }
2697
+ return runner;
2698
+ }
2699
+ async function boundedLocalDecision(active, body, modelId, signal) {
2700
+ signal.throwIfAborted();
2701
+ let onAbort;
2702
+ const cancelled = new Promise((_, reject) => {
2703
+ onAbort = () => reject(signal.reason);
2704
+ signal.addEventListener("abort", onAbort, { once: true });
2705
+ });
2706
+ try {
2707
+ return await Promise.race([
2708
+ active.decide(body, modelId, signal),
2709
+ cancelled
2710
+ ]);
2711
+ } finally {
2712
+ if (onAbort !== undefined)
2713
+ signal.removeEventListener("abort", onAbort);
2714
+ }
2715
+ }
2716
+ function loadActiveConfig() {
2717
+ try {
2718
+ return currentConfig(deps);
2719
+ } catch (error) {
2720
+ return json(errorBody("config_invalid", error instanceof Error ? error.message : "config could not be loaded"), 503);
2721
+ }
2722
+ }
2723
+ async function handleModels(config, signal) {
2724
+ const backends = runtimeBackends(config, deps.env, deps.home);
2725
+ await probeAll(backends, config.gateway.probe_timeout_ms, fetchFn, signal);
2726
+ signal.throwIfAborted();
2727
+ const data = backends.flatMap((backend) => backend.models.map((id) => ({
2728
+ id,
2729
+ backend: backend.name,
2730
+ kind: backend.kind,
2731
+ available: backend.available,
2732
+ capabilities: backend.capabilities ?? null,
2733
+ explicit_only: backend.explicitOnly === true,
2734
+ ...backend.adapter === undefined ? {} : { adapter: backend.adapter }
2735
+ })));
2736
+ return json({ object: "list", data });
2737
+ }
2738
+ async function handleSystemOne(request, config, signal) {
2739
+ const lengthHeader = request.headers.get("content-length");
2740
+ if (lengthHeader !== null && Number(lengthHeader) > PROTOCOL_LIMITS.maxBodyBytes) {
2741
+ return json(errorBody("request_too_large", "body exceeds 1 MiB"), 413);
2742
+ }
2743
+ let rawBody;
2744
+ try {
2745
+ rawBody = await readBoundedText(request, PROTOCOL_LIMITS.maxBodyBytes, signal);
2746
+ } catch (error) {
2747
+ signal.throwIfAborted();
2748
+ return error instanceof HttpBodyLimitError ? json(errorBody("request_too_large", "body exceeds 1 MiB"), 413) : json(errorBody("request_unreadable", "request body could not be read"), 400);
2749
+ }
2750
+ let parsedJson;
2751
+ try {
2752
+ parsedJson = JSON.parse(rawBody);
2753
+ } catch {
2754
+ return json(errorBody("invalid_json", "request body is not valid JSON"), 400);
2755
+ }
2756
+ const parsed = systemOneRequestSchema.safeParse(parsedJson);
2757
+ if (!parsed.success) {
2758
+ const issue = parsed.error.issues[0];
2759
+ const where = issue === undefined ? "" : `${issue.path.join(".")}: `;
2760
+ const what = issue === undefined ? "invalid request" : issue.message;
2761
+ return json(errorBody("invalid_request", `${where}${what}`), 422);
2762
+ }
2763
+ const body = parsed.data;
2764
+ if (serializedBytes(body.state) > PROTOCOL_LIMITS.maxStateBytes) {
2765
+ return json(errorBody("request_too_large", "state exceeds 256 KiB"), 413);
2766
+ }
2767
+ const backends = runtimeBackends(config, deps.env, deps.home);
2768
+ if (backends.length === 0) {
2769
+ return json(errorBody("no_backend_configured", "no backends configured; run `sys1 setup`, `sys1 jev enable`, or add a backend with `sys1 backend add`"), 503);
2770
+ }
2771
+ const policy = config.routing.policy;
2772
+ const allowedProbes = backends.filter((backend) => policy === "local-only" ? backend.kind === "local" : policy === "hosted-only" ? backend.kind === "hosted" : true);
2773
+ await probeAll(allowedProbes, config.gateway.probe_timeout_ms, fetchFn, signal);
2774
+ signal.throwIfAborted();
2775
+ const pinned = body.model !== undefined && body.model.includes("/");
2776
+ const needs = requestNeeds(body);
2777
+ let remaining = backends;
2778
+ let lastTransport = null;
2779
+ let attempts = 0;
2780
+ for (let attempt = 0;attempt < MAX_ATTEMPTS && remaining.length > 0; attempt += 1) {
2781
+ signal.throwIfAborted();
2782
+ const choice = chooseBackend(policy, body.model, remaining, needs);
2783
+ if (!choice.ok) {
2784
+ if (lastTransport !== null)
2785
+ break;
2786
+ const status = choice.reason === "unknown_model" ? 404 : choice.reason === "request_unsupported" || choice.reason === "policy_restricted" ? 422 : 503;
2787
+ return json(errorBody(choice.reason, choice.detail), status);
2788
+ }
2789
+ const backend = choice.backend;
2790
+ const hopRequest = { ...body, model: forwardModel(body.model, backend) ?? backend.default_model };
2791
+ const forwardedBody = backend.adapter === "kev" ? JSON.stringify(adaptKevRequest(hopRequest)) : rawBody;
2792
+ if (new TextEncoder().encode(forwardedBody).byteLength > PROTOCOL_LIMITS.maxBodyBytes) {
2793
+ return json(errorBody("request_too_large", "adapted request body exceeds 1 MiB"), 413);
2794
+ }
2795
+ attempts += 1;
2796
+ let result;
2797
+ if (backend.builtin !== undefined) {
2798
+ const active = localRunner(config);
2799
+ if (active === null) {
2800
+ result = {
2801
+ kind: "response",
2802
+ status: 503,
2803
+ body: JSON.stringify(errorBody("engine_unavailable", "local runner unavailable")),
2804
+ content_type: "application/json"
2805
+ };
2806
+ } else {
2807
+ const decided = await boundedLocalDecision(active, body, backend.builtin.model.id, signal);
2808
+ result = decided.ok ? {
2809
+ kind: "response",
2810
+ status: 200,
2811
+ body: JSON.stringify(decided.response),
2812
+ content_type: "application/json",
2813
+ extra_headers: localDiagnosticHeaders(decided.adapter, decided.diagnostics)
2814
+ } : {
2815
+ kind: "response",
2816
+ status: decided.error?.type === "local_question_unsupported" ? 422 : decided.error?.type === "inference_timeout" ? 504 : decided.error?.type === "engine_unavailable" ? 503 : 502,
2817
+ body: JSON.stringify(errorBody(decided.error?.type ?? "inference_failed", decided.error?.type === "local_question_unsupported" ? "request exceeds the local adapter's supported input limits" : decided.error?.type === "inference_timeout" ? "local inference timed out" : "local inference could not answer the request")),
2818
+ content_type: "application/json"
2819
+ };
2820
+ }
2821
+ } else {
2822
+ result = await forwardToBackend(backend, forwardedBody, forwardModel(body.model, backend), config.gateway.request_timeout_ms, fetchFn, signal);
2823
+ }
2824
+ signal.throwIfAborted();
2825
+ if (result.kind === "response") {
2826
+ let status = result.status;
2827
+ let responseBody = result.body;
2828
+ let contentType = result.content_type;
2829
+ if (status >= 200 && status < 300) {
2830
+ contentType = "application/json";
2831
+ try {
2832
+ const value = JSON.parse(responseBody ?? "");
2833
+ responseBody = JSON.stringify(backend.adapter === "kev" ? adaptKevResponse(hopRequest, value) : validateResponseForRequest(body, value));
2834
+ } catch {
2835
+ status = 502;
2836
+ responseBody = JSON.stringify(errorBody("backend_response_invalid", "backend response does not match the requested decision contract"));
2837
+ }
2838
+ }
2839
+ return new Response([204, 205, 304].includes(status) ? null : responseBody, {
2840
+ status,
2841
+ headers: {
2842
+ "content-type": contentType,
2843
+ "x-sys1-backend": backend.name,
2844
+ "x-sys1-attempts": String(attempt + 1),
2845
+ ...backend.adapter === "kev" && status >= 200 && status < 300 ? { "x-sys1-adapter": "kev", "x-sys1-probability-decimals": "2" } : {},
2846
+ ..."extra_headers" in result ? result.extra_headers : {}
2847
+ }
2848
+ });
2849
+ }
2850
+ lastTransport = result.detail;
2851
+ if (pinned)
2852
+ break;
2853
+ remaining = remaining.filter((candidate) => candidate.name !== backend.name);
2854
+ }
2855
+ return json(errorBody("no_backend_available", `no reachable backend answered the request${lastTransport === null ? "" : ` (${lastTransport})`}`), 503, { "x-sys1-attempts": String(attempts) });
2856
+ }
2857
+ return async function handler(request) {
2858
+ const url = new URL(request.url);
2859
+ if (request.method === "GET" && url.pathname === "/healthz") {
2860
+ return json({
2861
+ ok: true,
2862
+ version: SYS1_VERSION,
2863
+ ...deps.daemon === undefined || request.headers.get("authorization") !== `Bearer ${deps.daemon.instance}` ? {} : { pid: process.pid, instance: deps.daemon.instance }
2864
+ });
2865
+ }
2866
+ if (request.method === "POST" && url.pathname === "/_sys1/shutdown" && deps.daemon !== undefined) {
2867
+ if (request.headers.get("authorization") !== `Bearer ${deps.daemon.instance}`) {
2868
+ return json(errorBody("unauthorized", "daemon ownership proof required"), 401);
2869
+ }
2870
+ const daemon = deps.daemon;
2871
+ setTimeout(() => {
2872
+ Promise.resolve().then(() => daemon.onShutdown()).catch(() => {});
2873
+ }, 0);
2874
+ return json({ ok: true }, 202);
2875
+ }
2876
+ const models = request.method === "GET" && url.pathname === "/v1/models";
2877
+ const decision = request.method === "POST" && url.pathname === "/v1/systemone";
2878
+ if (models || decision) {
2879
+ const config = loadActiveConfig();
2880
+ if (config instanceof Response)
2881
+ return config;
2882
+ const controller = new AbortController;
2883
+ const signal = controller.signal;
2884
+ const onCallerAbort = () => controller.abort(request.signal.reason);
2885
+ request.signal.addEventListener("abort", onCallerAbort, { once: true });
2886
+ if (request.signal.aborted)
2887
+ onCallerAbort();
2888
+ const timer = setTimeout(() => {
2889
+ controller.abort(new DOMException("request deadline exceeded", "TimeoutError"));
2890
+ }, config.gateway.request_timeout_ms);
2891
+ try {
2892
+ signal.throwIfAborted();
2893
+ return await (models ? handleModels(config, signal) : handleSystemOne(request, config, signal));
2894
+ } catch (error) {
2895
+ if (request.signal.aborted) {
2896
+ return json(errorBody("request_cancelled", "caller cancelled the request"), 499);
2897
+ }
2898
+ if (signal.aborted) {
2899
+ return json(errorBody("inference_timeout", "request deadline exceeded"), 504);
2900
+ }
2901
+ if (error instanceof ModelStoreError) {
2902
+ return json(errorBody("model_store_invalid", "local model inventory is invalid; run `sys1 doctor`. Legacy scorer/Needle inventories require a new SYS1_HOME; existing files were not changed."), 503);
2903
+ }
2904
+ return json(errorBody("gateway_unavailable", "gateway could not process the request"), 503);
2905
+ } finally {
2906
+ clearTimeout(timer);
2907
+ request.signal.removeEventListener("abort", onCallerAbort);
2908
+ }
2909
+ }
2910
+ return json(errorBody("not_found", "unknown route"), 404);
2911
+ };
2912
+ }
2913
+ function networkAdmission(request) {
2914
+ const url = new URL(request.url);
2915
+ const hostname = url.hostname.startsWith("[") ? url.hostname.slice(1, -1) : url.hostname;
2916
+ if (!isLoopbackHost(hostname) || url.username !== "" || url.password !== "") {
2917
+ return json(errorBody("request_host_forbidden", "gateway requires a loopback request authority"), 403);
2918
+ }
2919
+ const host = request.headers.get("host");
2920
+ if (host !== null) {
2921
+ try {
2922
+ const authority = new URL(`${url.protocol}//${host}`);
2923
+ if (authority.origin !== url.origin || authority.username !== "" || authority.password !== "" || authority.pathname !== "/" || authority.search !== "" || authority.hash !== "") {
2924
+ return json(errorBody("request_host_forbidden", "request Host must match its loopback authority"), 403);
2925
+ }
2926
+ } catch {
2927
+ return json(errorBody("request_host_forbidden", "request Host is invalid"), 403);
2928
+ }
2929
+ }
2930
+ if (request.headers.has("origin") || request.headers.has("sec-fetch-site")) {
2931
+ return json(errorBody("browser_request_forbidden", "browser requests are not supported by the local gateway"), 403);
2932
+ }
2933
+ if (request.method === "POST" && url.pathname === "/v1/systemone" && request.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase() !== "application/json") {
2934
+ return json(errorBody("unsupported_media_type", "decision requests require application/json"), 415);
2935
+ }
2936
+ return null;
2937
+ }
2938
+ function createNetworkFetchHandler(deps) {
2939
+ const handler = createFetchHandler(deps);
2940
+ return async (request) => {
2941
+ const rejected = networkAdmission(request);
2942
+ if (rejected !== null) {
2943
+ request.body?.cancel().catch(() => {});
2944
+ return rejected;
2945
+ }
2946
+ return handler(request);
2947
+ };
2948
+ }
2949
+ function startGateway(deps) {
2950
+ const config = configSchema.parse(deps.config);
2951
+ const localRunner = deps.localRunner ?? (deps.home === undefined ? undefined : new LocalRunner({
2952
+ home: deps.home,
2953
+ maxLoadedModels: config.local.max_loaded_models,
2954
+ engineFactory: defaultEngineFactory(config.local.context_tokens, config.local.eval_timeout_ms)
2955
+ }));
2956
+ const handler = createNetworkFetchHandler({ ...deps, config, ...localRunner === undefined ? {} : { localRunner } });
2957
+ const server = Bun.serve({
2958
+ hostname: config.gateway.host,
2959
+ port: deps.port ?? config.gateway.port,
2960
+ fetch: handler,
2961
+ maxRequestBodySize: PROTOCOL_LIMITS.maxBodyBytes
2962
+ });
2963
+ const boundPort = server.port ?? deps.port ?? config.gateway.port;
2964
+ return {
2965
+ url: `http://${config.gateway.host.includes(":") ? `[${config.gateway.host}]` : config.gateway.host}:${boundPort}`,
2966
+ port: boundPort,
2967
+ stop: async () => {
2968
+ const disposing = localRunner?.dispose();
2969
+ await server.stop(true);
2970
+ if (disposing !== undefined)
2971
+ await disposing;
2972
+ }
2973
+ };
2974
+ }
2975
+ // src/doctor.ts
2976
+ import {
2977
+ accessSync,
2978
+ constants,
2979
+ existsSync as existsSync5,
2980
+ readdirSync,
2981
+ statSync as statSync2
2982
+ } from "node:fs";
2983
+ import { dirname } from "node:path";
2984
+
2985
+ // src/daemon.ts
2986
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, openSync as openSync2, readFileSync as readFileSync3, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "node:fs";
2987
+ import { z as z7 } from "zod";
2988
+ var pidFileSchema = z7.object({
2989
+ pid: z7.number().int().positive(),
2990
+ port: z7.number().int().positive(),
2991
+ host: loopbackHostSchema,
2992
+ instance: z7.string().uuid(),
2993
+ started_at: z7.string()
2994
+ });
2995
+ function readPidFile(home) {
2996
+ const path = pidPath(home);
2997
+ if (!existsSync4(path))
2998
+ return null;
2999
+ try {
3000
+ const parsed = pidFileSchema.safeParse(JSON.parse(readFileSync3(path, "utf8")));
3001
+ return parsed.success ? parsed.data : null;
3002
+ } catch {
3003
+ return null;
3004
+ }
3005
+ }
3006
+ function processAlive(pid) {
3007
+ try {
3008
+ process.kill(pid, 0);
3009
+ return true;
3010
+ } catch {
3011
+ return false;
3012
+ }
3013
+ }
3014
+ function gatewayUrl(host, port) {
3015
+ return `http://${host.includes(":") && !host.startsWith("[") ? `[${host}]` : host}:${port}`;
3016
+ }
3017
+ async function healthz(host, port, timeoutMs = 1500, fetchFn = fetch) {
3018
+ try {
3019
+ const response = await fetchFn(`${gatewayUrl(host, port)}/healthz`, {
3020
+ signal: AbortSignal.timeout(timeoutMs),
3021
+ redirect: "error"
3022
+ });
3023
+ return response.ok;
3024
+ } catch {
3025
+ return false;
3026
+ }
3027
+ }
3028
+ async function ownedHealth(record, fetchFn = fetch) {
3029
+ try {
3030
+ const response = await fetchFn(`${gatewayUrl(record.host, record.port)}/healthz`, {
3031
+ headers: { authorization: `Bearer ${record.instance}` },
3032
+ signal: AbortSignal.timeout(1500),
3033
+ redirect: "error"
3034
+ });
3035
+ if (!response.ok)
3036
+ return false;
3037
+ const value = JSON.parse(await readBoundedText(response, 4096, AbortSignal.timeout(1500)));
3038
+ const parsed = z7.object({ ok: z7.literal(true), pid: z7.number(), instance: z7.string() }).safeParse(value);
3039
+ return parsed.success && parsed.data.pid === record.pid && parsed.data.instance === record.instance;
3040
+ } catch {
3041
+ return false;
3042
+ }
3043
+ }
3044
+ async function daemonStatus(home, config, fetchFn = fetch) {
3045
+ const record = readPidFile(home);
3046
+ if (record !== null && processAlive(record.pid)) {
3047
+ if (await ownedHealth(record, fetchFn)) {
3048
+ return {
3049
+ state: "running",
3050
+ pid: record.pid,
3051
+ port: record.port,
3052
+ host: record.host,
3053
+ started_at: record.started_at
3054
+ };
3055
+ }
3056
+ return { state: "stale_pidfile", pid: record.pid };
3057
+ }
3058
+ if (record !== null)
3059
+ return { state: "stale_pidfile", pid: record.pid };
3060
+ if (await healthz(config.gateway.host, config.gateway.port, 800, fetchFn)) {
3061
+ return { state: "foreign_listener", port: config.gateway.port };
3062
+ }
3063
+ return { state: "stopped" };
3064
+ }
3065
+ async function daemonUp(options) {
3066
+ const { home, config, cliEntry, env } = options;
3067
+ const fetchFn = options.fetchFn ?? fetch;
3068
+ const waitMs = options.waitMs ?? 8000;
3069
+ const prior = await daemonStatus(home, config, fetchFn);
3070
+ if (prior.state === "running") {
3071
+ return {
3072
+ ok: false,
3073
+ message: `already running (pid ${prior.pid}) at ${gatewayUrl(prior.host, prior.port)}`
3074
+ };
3075
+ }
3076
+ if (prior.state === "foreign_listener") {
3077
+ return {
3078
+ ok: false,
3079
+ message: `port ${prior.port} already serves a health check not owned by sys1; pick another with --port`
3080
+ };
3081
+ }
3082
+ mkdirSync3(home, { recursive: true, mode: 448 });
3083
+ const log = openSync2(logPath(home), "a");
3084
+ const child = Bun.spawn([process.execPath, cliEntry, "serve", "--daemon-child", "--port", String(config.gateway.port)], {
3085
+ stdin: "ignore",
3086
+ stdout: log,
3087
+ stderr: log,
3088
+ env: { ...env, SYS1_HOME: home },
3089
+ detached: true
3090
+ });
3091
+ child.unref();
3092
+ const deadline = Date.now() + waitMs;
3093
+ while (Date.now() < deadline) {
3094
+ const record = readPidFile(home);
3095
+ if (record !== null && record.pid === child.pid && processAlive(record.pid)) {
3096
+ if (await ownedHealth(record, fetchFn)) {
3097
+ return {
3098
+ ok: true,
3099
+ message: "running",
3100
+ pid: record.pid,
3101
+ url: gatewayUrl(record.host, record.port)
3102
+ };
3103
+ }
3104
+ }
3105
+ if (child.exitCode !== null) {
3106
+ return {
3107
+ ok: false,
3108
+ message: `daemon exited during startup (code ${child.exitCode}); see ${logPath(home)}`
3109
+ };
3110
+ }
3111
+ await Bun.sleep(100);
3112
+ }
3113
+ return { ok: false, message: `daemon did not become healthy within ${waitMs}ms` };
3114
+ }
3115
+ async function daemonDown(home, config, fetchFn = fetch) {
3116
+ const record = readPidFile(home);
3117
+ if (record === null) {
3118
+ const state = await daemonStatus(home, config, fetchFn);
3119
+ if (state.state === "foreign_listener") {
3120
+ return {
3121
+ ok: false,
3122
+ message: `port ${state.port} answers but has no sys1 pid file; refusing to stop it`
3123
+ };
3124
+ }
3125
+ return { ok: true, message: "not running" };
3126
+ }
3127
+ if (!processAlive(record.pid)) {
3128
+ rmSync2(pidPath(home), { force: true });
3129
+ return { ok: true, message: "not running (cleared stale pid file)" };
3130
+ }
3131
+ if (!await ownedHealth(record, fetchFn)) {
3132
+ return { ok: false, message: "daemon ownership could not be verified; refusing to signal a saved PID" };
3133
+ }
3134
+ try {
3135
+ const response = await fetchFn(`${gatewayUrl(record.host, record.port)}/_sys1/shutdown`, {
3136
+ method: "POST",
3137
+ headers: { authorization: `Bearer ${record.instance}` },
3138
+ signal: AbortSignal.timeout(1500),
3139
+ redirect: "error"
3140
+ });
3141
+ if (response.status !== 202)
3142
+ return { ok: false, message: "daemon refused authenticated shutdown" };
3143
+ await response.body?.cancel();
3144
+ } catch {
3145
+ return { ok: false, message: "daemon shutdown could not be confirmed" };
3146
+ }
3147
+ const deadline = Date.now() + 5000;
3148
+ while (Date.now() < deadline) {
3149
+ if (!processAlive(record.pid) || readPidFile(home)?.instance !== record.instance) {
3150
+ return { ok: true, message: `stopped daemon ${record.pid}` };
3151
+ }
3152
+ await Bun.sleep(100);
3153
+ }
3154
+ return { ok: false, message: "daemon accepted shutdown but has not exited; no PID signal sent" };
3155
+ }
3156
+ function writePidFile(home, pid, host, port, instance) {
3157
+ const temporary = `${pidPath(home)}.${crypto.randomUUID()}.tmp`;
3158
+ try {
3159
+ writeFileSync3(temporary, `${JSON.stringify({ pid, host, port, instance, started_at: new Date().toISOString() })}
3160
+ `, { mode: 384, flag: "wx" });
3161
+ renameSync2(temporary, pidPath(home));
3162
+ } finally {
3163
+ rmSync2(temporary, { force: true });
3164
+ }
3165
+ }
3166
+ function clearPidFile(home, pid) {
3167
+ const record = readPidFile(home);
3168
+ if (record !== null && record.pid === pid) {
3169
+ rmSync2(pidPath(home), { force: true });
3170
+ }
3171
+ }
3172
+
3173
+ // src/doctor.ts
3174
+ function compareVersion(actual, required) {
3175
+ const left = actual.split(".").map((part) => Number(part));
3176
+ const right = required.split(".").map((part) => Number(part));
3177
+ for (let i = 0;i < Math.max(left.length, right.length); i += 1) {
3178
+ const a = left[i] ?? 0;
3179
+ const b = right[i] ?? 0;
3180
+ if (a !== b)
3181
+ return a > b ? 1 : -1;
3182
+ }
3183
+ return 0;
3184
+ }
3185
+ function boundedDetail(value, home) {
3186
+ return value.replaceAll(home, "$SYS1_HOME").slice(0, 512);
3187
+ }
3188
+ function stateDirectoryCheck(home) {
3189
+ if (home.length === 0 || home.length > 4096) {
3190
+ return { id: "state.directory", status: "fail", summary: "the state folder path is invalid; set SYS1_HOME to a folder you can write to" };
3191
+ }
3192
+ let target = home;
3193
+ for (let i = 0;i < 32 && !existsSync5(target); i += 1) {
3194
+ const parent = dirname(target);
3195
+ if (parent === target)
3196
+ break;
3197
+ target = parent;
3198
+ }
3199
+ try {
3200
+ const stats = statSync2(target);
3201
+ if (!stats.isDirectory()) {
3202
+ return { id: "state.directory", status: "fail", summary: "the state folder path points inside a file, not a folder" };
3203
+ }
3204
+ accessSync(target, constants.R_OK | constants.W_OK);
3205
+ return {
3206
+ id: "state.directory",
3207
+ status: "pass",
3208
+ summary: existsSync5(home) ? "state folder is readable and writable" : "state folder can be created"
3209
+ };
3210
+ } catch {
3211
+ return { id: "state.directory", status: "fail", summary: "state folder isn't readable and writable" };
3212
+ }
3213
+ }
3214
+ function inspectModelFile(home, model) {
3215
+ const path = modelFilePath(home, model);
3216
+ return inspectGgufFile(path);
3217
+ }
3218
+ function modelChecks(home) {
3219
+ const loaded = loadManifestChecked(home);
3220
+ if (!loaded.ok) {
3221
+ return {
3222
+ manifest: {
3223
+ id: "models.manifest",
3224
+ status: "fail",
3225
+ summary: "the list of installed models is damaged",
3226
+ detail: boundedDetail(loaded.message, home)
3227
+ },
3228
+ files: { id: "models.files", status: "fail", summary: "model files can't be checked" },
3229
+ inventory: { id: "models.inventory", status: "warn", summary: "model folder can't be compared with the installed list" },
3230
+ validModelIds: []
3231
+ };
3232
+ }
3233
+ const manifest = loaded.manifest;
3234
+ const problems = [];
3235
+ for (const model of manifest.models) {
3236
+ const inspection = inspectModelFile(home, model);
3237
+ if (!inspection.ok) {
3238
+ problems.push({ id: model.id, issue: boundedDetail(inspection.message ?? "inspection failed", home) });
3239
+ } else if (inspection.bytes !== model.bytes) {
3240
+ problems.push({ id: model.id, issue: "file size differs from the size recorded at install" });
3241
+ }
3242
+ }
3243
+ let inventory = {
3244
+ id: "models.inventory",
3245
+ status: "pass",
3246
+ summary: "model folder has no leftover or unknown files"
3247
+ };
3248
+ const directory = modelsDir(home);
3249
+ if (existsSync5(directory)) {
3250
+ try {
3251
+ const entries = readdirSync(directory, { withFileTypes: true }).slice(0, 257);
3252
+ const admitted = new Set(manifest.models.map((model) => model.file));
3253
+ const stale = entries.filter((entry) => entry.name.endsWith(".download") || entry.name.endsWith(".tmp")).length;
3254
+ const orphaned = entries.filter((entry) => entry.isFile() && /\.(gguf|pt|cact|exe)$|^[^.]+\.engine$/.test(entry.name) && !entry.name.endsWith(".download") && !admitted.has(entry.name)).length;
3255
+ const links = entries.filter((entry) => entry.isSymbolicLink()).length;
3256
+ if (entries.length > 256 || stale > 0 || orphaned > 0 || links > 0) {
3257
+ inventory = {
3258
+ id: "models.inventory",
3259
+ status: "warn",
3260
+ summary: "model folder has files sys1 didn't install (leftover downloads, unknown models or links); check them before deleting",
3261
+ detail: {
3262
+ stale_downloads: stale,
3263
+ orphaned_ggufs: orphaned,
3264
+ symbolic_links: links,
3265
+ inventory_truncated: entries.length > 256
3266
+ }
3267
+ };
3268
+ }
3269
+ } catch {
3270
+ inventory = { id: "models.inventory", status: "fail", summary: "model folder can't be read" };
3271
+ }
3272
+ }
3273
+ return {
3274
+ manifest: {
3275
+ id: "models.manifest",
3276
+ status: "pass",
3277
+ summary: `${manifest.models.length} model${manifest.models.length === 1 ? "" : "s"} installed`
3278
+ },
3279
+ files: problems.length === 0 ? {
3280
+ id: "models.files",
3281
+ status: "pass",
3282
+ summary: `${manifest.models.length} model file${manifest.models.length === 1 ? "" : "s"} present and readable`
3283
+ } : {
3284
+ id: "models.files",
3285
+ status: "fail",
3286
+ summary: `${problems.length} model file${problems.length === 1 ? " is" : "s are"} missing or damaged; run sys1 pull again`,
3287
+ detail: problems
3288
+ },
3289
+ inventory,
3290
+ validModelIds: manifest.models.filter((model) => !problems.some((problem) => problem.id === model.id)).map((model) => model.id)
3291
+ };
3292
+ }
3293
+ function routingCheck(config, env, validModelIds) {
3294
+ const hosted = config.hosted.enabled && (env[config.hosted.api_key_env]?.length ?? 0) > 0;
3295
+ const local = config.local.enabled && validModelIds.includes(config.local.model);
3296
+ const explicit = config.backends.filter((backend) => {
3297
+ if (!backend.enabled)
3298
+ return false;
3299
+ const hostname = new URL(backend.base_url).hostname.replace(/^\[|\]$/g, "");
3300
+ const onMachine = isLoopbackHost(hostname);
3301
+ return config.routing.policy === "local-only" ? onMachine : config.routing.policy === "hosted-only" ? !onMachine : true;
3302
+ }).length;
3303
+ if (explicit > 0 && (config.routing.policy === "hosted-only" ? !hosted : config.routing.policy === "local-only" ? !local : !hosted && !local)) {
3304
+ return { id: "routing.candidates", status: "warn", summary: 'only named HTTP backends are set up; each request must name one as "model": "backend/model"' };
3305
+ }
3306
+ if (config.routing.policy === "hosted-only" && !hosted) {
3307
+ return { id: "routing.candidates", status: "fail", summary: "routing is set to hosted Jev only, but no Jev credential is set" };
3308
+ }
3309
+ if (config.routing.policy === "local-only" && !local) {
3310
+ return { id: "routing.candidates", status: "fail", summary: `local model ${config.local.model} isn't available; run sys1 setup or name another backend in each request` };
3311
+ }
3312
+ if (!hosted && !local) {
3313
+ return { id: "routing.candidates", status: "warn", summary: `nothing can answer requests yet: local model ${config.local.model} isn't installed or turned on` };
3314
+ }
3315
+ return {
3316
+ id: "routing.candidates",
3317
+ status: "pass",
3318
+ summary: "at least one model can answer requests",
3319
+ detail: { hosted, local }
3320
+ };
3321
+ }
3322
+ function daemonCheck(state) {
3323
+ switch (state.state) {
3324
+ case "running":
3325
+ return { id: "daemon", status: "pass", summary: "background gateway is running" };
3326
+ case "stopped":
3327
+ return { id: "daemon", status: "pass", summary: "background gateway is stopped" };
3328
+ case "stale_pidfile":
3329
+ return { id: "daemon", status: "warn", summary: "background gateway isn't responding, or its process record is out of date" };
3330
+ case "foreign_listener":
3331
+ return { id: "daemon", status: "fail", summary: "another program is using the gateway port" };
3332
+ }
3333
+ }
3334
+ async function runDoctor(options) {
3335
+ const env = options.env ?? process.env;
3336
+ const runtimeVersion = options.runtimeVersion ?? Bun.version;
3337
+ const checks = [];
3338
+ checks.push(compareVersion(runtimeVersion, "1.3.14") >= 0 ? { id: "runtime.bun", status: "pass", summary: `Bun ${runtimeVersion} is supported` } : { id: "runtime.bun", status: "fail", summary: `Bun ${runtimeVersion} is below 1.3.14` });
3339
+ checks.push(stateDirectoryCheck(options.home));
3340
+ const loadedConfig = loadConfig(options.home);
3341
+ const config = loadedConfig.ok ? loadedConfig.config : DEFAULT_CONFIG;
3342
+ checks.push(loadedConfig.ok ? { id: "config", status: "pass", summary: loadedConfig.existed ? "settings are valid" : "no settings file yet; the defaults are valid" } : {
3343
+ id: "config",
3344
+ status: "fail",
3345
+ summary: "the settings file is invalid",
3346
+ detail: boundedDetail(loadedConfig.message, options.home)
3347
+ });
3348
+ const nativeProbe = options.nativeProbe ?? probeNativeRuntime;
3349
+ if (!config.local.enabled) {
3350
+ checks.push({ id: "native.runtime", status: "pass", summary: "local model is turned off" });
3351
+ } else {
3352
+ const native = await nativeProbe();
3353
+ checks.push(native.ok ? {
3354
+ id: "native.runtime",
3355
+ status: "pass",
3356
+ summary: `local model runtime (llama.cpp) is available via ${native.backend ?? "cpu"}`,
3357
+ detail: {
3358
+ gpu_offloading: native.gpu_offloading ?? false,
3359
+ supported_backends: native.supported_backends ?? [],
3360
+ ...native.elapsed_ms !== undefined && Number.isSafeInteger(native.elapsed_ms) && native.elapsed_ms >= 0 ? { native_probe_ms: native.elapsed_ms } : {}
3361
+ }
3362
+ } : {
3363
+ id: "native.runtime",
3364
+ status: "fail",
3365
+ summary: "local model runtime (llama.cpp) isn't available on this computer",
3366
+ ...native.message === undefined ? {} : { detail: boundedDetail(native.message, options.home) }
3367
+ });
3368
+ }
3369
+ const models = modelChecks(options.home);
3370
+ checks.push(models.manifest, models.files, models.inventory);
3371
+ checks.push(loadedConfig.ok ? routingCheck(config, env, models.validModelIds) : { id: "routing.candidates", status: "fail", summary: "routing can't be checked until the config is fixed" });
3372
+ if (loadedConfig.ok) {
3373
+ const daemonProbe = options.daemonProbe ?? daemonStatus;
3374
+ checks.push(daemonCheck(await daemonProbe(options.home, config)));
3375
+ } else {
3376
+ checks.push({ id: "daemon", status: "warn", summary: "gateway check skipped because the config is invalid" });
3377
+ }
3378
+ const counts = checks.reduce((result, check) => {
3379
+ result[check.status] += 1;
3380
+ return result;
3381
+ }, { pass: 0, warn: 0, fail: 0 });
3382
+ return { ok: counts.fail === 0, version: 1, checks, counts };
3383
+ }
3384
+ // src/runtime.ts
3385
+ function createRouter(options) {
3386
+ const config = configSchema.parse(options.config);
3387
+ if (options.env === undefined)
3388
+ throw new Error("createRouter requires an explicit env object");
3389
+ const owned = options.localRunner === undefined && options.home !== undefined;
3390
+ const runner = options.localRunner ?? (options.home === undefined ? undefined : new LocalRunner({
3391
+ home: options.home,
3392
+ maxLoadedModels: config.local.max_loaded_models,
3393
+ engineFactory: defaultEngineFactory(config.local.context_tokens, config.local.eval_timeout_ms)
3394
+ }));
3395
+ const handler = createFetchHandler({
3396
+ config,
3397
+ env: { ...options.env },
3398
+ ...options.home === undefined ? {} : { home: options.home },
3399
+ ...runner === undefined ? {} : { localRunner: runner },
3400
+ ...options.fetchFn === undefined ? {} : { fetchFn: options.fetchFn }
3401
+ });
3402
+ const shutdown = new AbortController;
3403
+ let disposal;
3404
+ const handle = async (request) => {
3405
+ if (shutdown.signal.aborted) {
3406
+ return Response.json(errorBody("router_disposed", "embedded router is disposed"), { status: 503 });
3407
+ }
3408
+ return handler(new Request(request, { signal: AbortSignal.any([request.signal, shutdown.signal]) }));
3409
+ };
3410
+ const client = createClient({
3411
+ timeoutMs: config.gateway.request_timeout_ms,
3412
+ fetch: (input, init) => handle(new Request(input, init))
3413
+ });
3414
+ return {
3415
+ evaluate: client.evaluate,
3416
+ fetch: handle,
3417
+ dispose() {
3418
+ if (disposal === undefined) {
3419
+ shutdown.abort();
3420
+ disposal = owned ? runner.dispose() : Promise.resolve();
3421
+ }
3422
+ return disposal;
3423
+ }
3424
+ };
3425
+ }
3426
+ export {
3427
+ writePidFile,
3428
+ verifyModel,
3429
+ toLocalResponse,
3430
+ toLocalAnswer,
3431
+ systemOneResponseSchema,
3432
+ systemOneRequestSchema,
3433
+ sys1Home,
3434
+ storeBytes,
3435
+ startGateway,
3436
+ setConfigValue,
3437
+ serializedBytes,
3438
+ scoreQuestionSchema,
3439
+ scoreAnswerSchema,
3440
+ saveManifest,
3441
+ saveConfig,
3442
+ runtimeBackends,
3443
+ runDoctor,
3444
+ routingPolicySchema,
3445
+ resolvePullTarget,
3446
+ requestNeeds,
3447
+ removeModel,
3448
+ readPidFile,
3449
+ questionSchema,
3450
+ qualifyBackend,
3451
+ pullModel,
3452
+ processAlive,
3453
+ probeNativeRuntime,
3454
+ probeBackend,
3455
+ probeAll,
3456
+ platformRecommendation,
3457
+ outcomeFromMass,
3458
+ noulQuestionSchema,
3459
+ noulAnswerSchema,
3460
+ modelsDir,
3461
+ modelFilePath,
3462
+ manifestPath,
3463
+ loopbackHostSchema,
3464
+ localBackendSchema,
3465
+ loadManifestChecked,
3466
+ loadManifest,
3467
+ loadConfig,
3468
+ isLoopbackHost,
3469
+ installedModels,
3470
+ inspectGgufFile,
3471
+ healthz,
3472
+ forwardToBackend,
3473
+ findRegistry,
3474
+ findInstalled,
3475
+ extractModelIds,
3476
+ extractBackendCapabilities,
3477
+ errorBody,
3478
+ entrySchema,
3479
+ defaultEngineFactory,
3480
+ decisionPrompt,
3481
+ daemonUp,
3482
+ daemonStatus,
3483
+ daemonDown,
3484
+ createRouter,
3485
+ createProfile,
3486
+ createFetchHandler,
3487
+ createClient,
3488
+ configSchema,
3489
+ confidenceOf,
3490
+ clearPidFile,
3491
+ chooseBackend,
3492
+ choiceQuestionSchema,
3493
+ choiceAnswerSchema,
3494
+ builtinName,
3495
+ builtinCandidates,
3496
+ answerSchema,
3497
+ answerLabels,
3498
+ aggregateMass,
3499
+ Sys1ProfileError,
3500
+ Sys1ClientError,
3501
+ SYS1_VERSION,
3502
+ SETTABLE_KEYS,
3503
+ ROUTING_POLICIES,
3504
+ PROTOCOL_LIMITS,
3505
+ ModelStoreError,
3506
+ MODEL_REGISTRY,
3507
+ MODEL_LIMITS,
3508
+ MODEL_KINDS,
3509
+ LocalRunner,
3510
+ LlamaEngine,
3511
+ LOCAL_MODEL_TIERS,
3512
+ HOSTED_BACKEND_NAME,
3513
+ EngineUnavailableError,
3514
+ DEFAULT_LOCAL_MODELS,
3515
+ DEFAULT_CONFIG,
3516
+ DEFAULT_BASE_URL,
3517
+ DECISION_LABELS,
3518
+ DECIDE_LIMITS,
3519
+ BUILTIN_PREFIX
3520
+ };