modelpact 2.0.2

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.
Files changed (107) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +372 -0
  3. package/dist/helpers/abort.d.ts +14 -0
  4. package/dist/helpers/abort.d.ts.map +1 -0
  5. package/dist/helpers/abort.js +22 -0
  6. package/dist/helpers/abort.js.map +1 -0
  7. package/dist/helpers/lifetime.d.ts +34 -0
  8. package/dist/helpers/lifetime.d.ts.map +1 -0
  9. package/dist/helpers/lifetime.js +71 -0
  10. package/dist/helpers/lifetime.js.map +1 -0
  11. package/dist/helpers/monitor.d.ts +52 -0
  12. package/dist/helpers/monitor.d.ts.map +1 -0
  13. package/dist/helpers/monitor.js +69 -0
  14. package/dist/helpers/monitor.js.map +1 -0
  15. package/dist/helpers/ndjson.d.ts +19 -0
  16. package/dist/helpers/ndjson.d.ts.map +1 -0
  17. package/dist/helpers/ndjson.js +39 -0
  18. package/dist/helpers/ndjson.js.map +1 -0
  19. package/dist/helpers/overflow.d.ts +50 -0
  20. package/dist/helpers/overflow.d.ts.map +1 -0
  21. package/dist/helpers/overflow.js +72 -0
  22. package/dist/helpers/overflow.js.map +1 -0
  23. package/dist/helpers/transcript.d.ts +22 -0
  24. package/dist/helpers/transcript.d.ts.map +1 -0
  25. package/dist/helpers/transcript.js +26 -0
  26. package/dist/helpers/transcript.js.map +1 -0
  27. package/dist/index.d.ts +23 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +19 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/lifecycle/01_access.d.ts +13 -0
  32. package/dist/lifecycle/01_access.d.ts.map +1 -0
  33. package/dist/lifecycle/01_access.js +45 -0
  34. package/dist/lifecycle/01_access.js.map +1 -0
  35. package/dist/lifecycle/02_download.d.ts +8 -0
  36. package/dist/lifecycle/02_download.d.ts.map +1 -0
  37. package/dist/lifecycle/02_download.js +12 -0
  38. package/dist/lifecycle/02_download.js.map +1 -0
  39. package/dist/lifecycle/03_open.d.ts +22 -0
  40. package/dist/lifecycle/03_open.d.ts.map +1 -0
  41. package/dist/lifecycle/03_open.js +39 -0
  42. package/dist/lifecycle/03_open.js.map +1 -0
  43. package/dist/lifecycle/04_generate.d.ts +15 -0
  44. package/dist/lifecycle/04_generate.d.ts.map +1 -0
  45. package/dist/lifecycle/04_generate.js +154 -0
  46. package/dist/lifecycle/04_generate.js.map +1 -0
  47. package/dist/lifecycle/05_close.d.ts +4 -0
  48. package/dist/lifecycle/05_close.d.ts.map +1 -0
  49. package/dist/lifecycle/05_close.js +8 -0
  50. package/dist/lifecycle/05_close.js.map +1 -0
  51. package/dist/providers/create.d.ts +5 -0
  52. package/dist/providers/create.d.ts.map +1 -0
  53. package/dist/providers/create.js +7 -0
  54. package/dist/providers/create.js.map +1 -0
  55. package/dist/providers/mock.d.ts +36 -0
  56. package/dist/providers/mock.d.ts.map +1 -0
  57. package/dist/providers/mock.js +128 -0
  58. package/dist/providers/mock.js.map +1 -0
  59. package/dist/providers/ollama.d.ts +33 -0
  60. package/dist/providers/ollama.d.ts.map +1 -0
  61. package/dist/providers/ollama.js +275 -0
  62. package/dist/providers/ollama.js.map +1 -0
  63. package/dist/providers/prompt-api.d.ts +23 -0
  64. package/dist/providers/prompt-api.d.ts.map +1 -0
  65. package/dist/providers/prompt-api.js +203 -0
  66. package/dist/providers/prompt-api.js.map +1 -0
  67. package/dist/providers/registry.d.ts +22 -0
  68. package/dist/providers/registry.d.ts.map +1 -0
  69. package/dist/providers/registry.js +30 -0
  70. package/dist/providers/registry.js.map +1 -0
  71. package/dist/testing/contract.d.ts +47 -0
  72. package/dist/testing/contract.d.ts.map +1 -0
  73. package/dist/testing/contract.js +518 -0
  74. package/dist/testing/contract.js.map +1 -0
  75. package/dist/testing/index.d.ts +9 -0
  76. package/dist/testing/index.d.ts.map +1 -0
  77. package/dist/testing/index.js +9 -0
  78. package/dist/testing/index.js.map +1 -0
  79. package/dist/types/backend.d.ts +63 -0
  80. package/dist/types/backend.d.ts.map +1 -0
  81. package/dist/types/backend.js +11 -0
  82. package/dist/types/backend.js.map +1 -0
  83. package/dist/types/failures.d.ts +115 -0
  84. package/dist/types/failures.d.ts.map +1 -0
  85. package/dist/types/failures.js +68 -0
  86. package/dist/types/failures.js.map +1 -0
  87. package/dist/types/foundations.d.ts +35 -0
  88. package/dist/types/foundations.d.ts.map +1 -0
  89. package/dist/types/foundations.js +30 -0
  90. package/dist/types/foundations.js.map +1 -0
  91. package/dist/types/messages.d.ts +30 -0
  92. package/dist/types/messages.d.ts.map +1 -0
  93. package/dist/types/messages.js +2 -0
  94. package/dist/types/messages.js.map +1 -0
  95. package/dist/types/provider.d.ts +22 -0
  96. package/dist/types/provider.d.ts.map +1 -0
  97. package/dist/types/provider.js +2 -0
  98. package/dist/types/provider.js.map +1 -0
  99. package/dist/types/session.d.ts +156 -0
  100. package/dist/types/session.d.ts.map +1 -0
  101. package/dist/types/session.js +2 -0
  102. package/dist/types/session.js.map +1 -0
  103. package/dist/types/usage.d.ts +25 -0
  104. package/dist/types/usage.d.ts.map +1 -0
  105. package/dist/types/usage.js +16 -0
  106. package/dist/types/usage.js.map +1 -0
  107. package/package.json +85 -0
@@ -0,0 +1,11 @@
1
+ /**
2
+ * What a backend supplies; `../lifecycle` turns it into an `AiProvider`.
3
+ *
4
+ * The contract's runtime guarantees — one generation at a time, an abort that
5
+ * leaves the session open, a close that refuses everything after it, an
6
+ * overflow that fires once — are implemented once, in `../lifecycle/01_access.ts`
7
+ * through `05_close.ts`. A backend never sees them: it says what the model is
8
+ * and how to talk to it, and the order of calls is the lifecycle's business.
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=backend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backend.js","sourceRoot":"","sources":["../../src/types/backend.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
@@ -0,0 +1,115 @@
1
+ import type { Modality } from "./messages.js";
2
+ import type { ContextUsage } from "./usage.js";
3
+ /**
4
+ * The contract's failure vocabulary: any refusal from any provider — browser,
5
+ * Ollama, mock — is one of these kinds.
6
+ *
7
+ * The set is cut by the caller's next move, not by exception name: kinds merge
8
+ * where the reaction would be the same and split where it differs, even when
9
+ * the spec throws one exception for both (`unsupported-config` vs
10
+ * `unsupported-input`). The mechanical name-to-kind mapping lives in
11
+ * `failureFromError`; the kinds with no spec exception behind them (`unsupported`,
12
+ * `busy`, `unknown`) say so in their own docs below.
13
+ *
14
+ * Each failure carries exactly the data its kind implies. `cause` is factored
15
+ * into an intersection to avoid repeating it; narrowing on `kind` still works
16
+ * through that, which `src/types.test-d.ts` checks.
17
+ */
18
+ export type AiFailure = {
19
+ readonly cause?: unknown;
20
+ } & (
21
+ /** No such API in this runtime. */
22
+ {
23
+ readonly kind: "unsupported";
24
+ }
25
+ /** Blocked by the `language-model` Permissions-Policy (`NotAllowedError`). */
26
+ | {
27
+ readonly kind: "not-allowed";
28
+ }
29
+ /**
30
+ * The environment has a model, but not for this request. Both lists name the
31
+ * parts that did not fit; empty or absent means the provider did not narrow
32
+ * it down.
33
+ */
34
+ | {
35
+ readonly kind: "unsupported-config";
36
+ readonly languages: readonly string[];
37
+ readonly modalities?: readonly Modality[];
38
+ }
39
+ /**
40
+ * The message the caller built is not accepted. Kept apart from
41
+ * `unsupported-config` because it is a bug in the caller, not the
42
+ * environment — the spec throws one `NotSupportedError` for both.
43
+ */
44
+ | {
45
+ readonly kind: "unsupported-input";
46
+ readonly detail: string;
47
+ }
48
+ /**
49
+ * `QuotaExceededError`: usage passed the window. Retrying without trimming
50
+ * history will fail again, hence the measurement travels along.
51
+ */
52
+ | {
53
+ readonly kind: "context-overflow";
54
+ readonly usage: ContextUsage;
55
+ }
56
+ /** Explicit abort, or a call on a closed session — the spec throws `AbortError` for both. */
57
+ | {
58
+ readonly kind: "aborted";
59
+ readonly reason: string;
60
+ }
61
+ /** Malformed request: empty message list, misplaced system turn, conflicting options. */
62
+ | {
63
+ readonly kind: "invalid-input";
64
+ readonly detail: string;
65
+ }
66
+ /** Document not fully active. */
67
+ | {
68
+ readonly kind: "invalid-state";
69
+ readonly detail: string;
70
+ }
71
+ /**
72
+ * A generation is already running on this session; `detail` names the call
73
+ * holding it. Its own kind rather than a shade of `invalid-state`: the spec
74
+ * spends that one on a single condition (the document is not fully active),
75
+ * and the caller's move differs — wait, or open a second session.
76
+ *
77
+ * Not a spec exception: the spec says nothing about two `prompt()` calls at
78
+ * once, and points at `clone()` for work meant to run in parallel. A
79
+ * provider here has to serialize instead.
80
+ */
81
+ | {
82
+ readonly kind: "busy";
83
+ readonly detail: string;
84
+ }
85
+ /** The browser's own catch-all (`OperationError`). */
86
+ | {
87
+ readonly kind: "failed";
88
+ readonly detail: string;
89
+ } | {
90
+ readonly kind: "unknown";
91
+ });
92
+ /** Every kind in the vocabulary, under a name; derived, and a type rather than an enum — see `AccessKind` in `./session.ts`. */
93
+ export type FailureKind = AiFailure["kind"];
94
+ /**
95
+ * Where thrown exceptions enter the vocabulary: whatever a provider caught
96
+ * becomes an `AiFailure`, mapped by DOMException name. This is the generic
97
+ * half of the mapping — a provider that knows the call site refines what a
98
+ * name alone cannot say (see the `NotSupportedError` and
99
+ * `QuotaExceededError` branches).
100
+ *
101
+ * Matches on `error.name` rather than `instanceof DOMException`, because the
102
+ * spec throws a plain `TypeError` that the latter would miss. The trade-off is
103
+ * that a genuine `TypeError` from a bug in the adapter also lands in
104
+ * `invalid-input`.
105
+ */
106
+ export declare function failureFromError(error: unknown): AiFailure;
107
+ /**
108
+ * For the boundaries where throwing is unavoidable — React error boundaries,
109
+ * tests. Inside the adapter, failures travel as values.
110
+ */
111
+ export declare class AiError extends Error {
112
+ readonly failure: AiFailure;
113
+ constructor(failure: AiFailure);
114
+ }
115
+ //# sourceMappingURL=failures.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"failures.d.ts","sourceRoot":"","sources":["../../src/types/failures.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAK/C;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,SAAS,GAAG;IAAE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG;AACrD,mCAAmC;AACjC;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;CAAE;AAClC,8EAA8E;GAC5E;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;CAAE;AAClC;;;;GAIG;GACD;IACE,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IACtC,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,QAAQ,EAAE,CAAC;CAC3C;AACH;;;;GAIG;GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AACjE;;;GAGG;GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAA;CAAE;AACrE,6FAA6F;GAC3F;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AACvD,yFAAyF;GACvF;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AAC7D,iCAAiC;GAC/B;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AAC7D;;;;;;;;;GASG;GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AACpD,sDAAsD;GACpD;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACpD;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CAAE,CAC/B,CAAC;AAEF,gIAAgI;AAChI,MAAM,MAAM,WAAW,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC;AAE5C;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,SAAS,CA0C1D;AAED;;;GAGG;AACH,qBAAa,OAAQ,SAAQ,KAAK;IACpB,QAAQ,CAAC,OAAO,EAAE,SAAS;gBAAlB,OAAO,EAAE,SAAS;CAMxC"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Where thrown exceptions enter the vocabulary: whatever a provider caught
3
+ * becomes an `AiFailure`, mapped by DOMException name. This is the generic
4
+ * half of the mapping — a provider that knows the call site refines what a
5
+ * name alone cannot say (see the `NotSupportedError` and
6
+ * `QuotaExceededError` branches).
7
+ *
8
+ * Matches on `error.name` rather than `instanceof DOMException`, because the
9
+ * spec throws a plain `TypeError` that the latter would miss. The trade-off is
10
+ * that a genuine `TypeError` from a bug in the adapter also lands in
11
+ * `invalid-input`.
12
+ */
13
+ export function failureFromError(error) {
14
+ const errorName = error instanceof Error ? error.name : "";
15
+ const detail = error instanceof Error ? error.message : String(error);
16
+ const isDomException = typeof DOMException !== "undefined" && error instanceof DOMException;
17
+ switch (errorName) {
18
+ case "AbortError":
19
+ return { kind: "aborted", reason: detail, cause: error };
20
+ case "InvalidStateError":
21
+ return { kind: "invalid-state", detail, cause: error };
22
+ case "NotAllowedError":
23
+ return { kind: "not-allowed", cause: error };
24
+ case "NotSupportedError":
25
+ // The name covers both "wrong environment" and "wrong message" and
26
+ // cannot tell them apart; a provider that knows the call site must build
27
+ // `unsupported-input` itself. Empty list means "not narrowed down".
28
+ return { kind: "unsupported-config", languages: [], cause: error };
29
+ case "OperationError":
30
+ return { kind: "failed", detail, cause: error };
31
+ case "QuotaExceededError":
32
+ // The exception carries no measurement; a provider holding the session
33
+ // can refine this.
34
+ return {
35
+ kind: "context-overflow",
36
+ usage: { kind: "unknown" },
37
+ cause: error,
38
+ };
39
+ case "SyntaxError":
40
+ // The spec's SyntaxError is a DOMException; JSON.parse on a truncated
41
+ // frame throws a native one under the same name. Only the former means
42
+ // "the caller sent nonsense". A provider should still convert its own
43
+ // parse errors where they happen rather than let them reach here.
44
+ return isDomException
45
+ ? { kind: "invalid-input", detail, cause: error }
46
+ : { kind: "failed", detail, cause: error };
47
+ case "TypeError":
48
+ // No such split available: the spec throws a native TypeError too.
49
+ return { kind: "invalid-input", detail, cause: error };
50
+ default:
51
+ return { kind: "unknown", cause: error };
52
+ }
53
+ }
54
+ /**
55
+ * For the boundaries where throwing is unavoidable — React error boundaries,
56
+ * tests. Inside the adapter, failures travel as values.
57
+ */
58
+ export class AiError extends Error {
59
+ failure;
60
+ constructor(failure) {
61
+ // Only the kind: the details are in `failure`, and restating them as a
62
+ // string would create a second version of the truth.
63
+ super(failure.kind, { cause: failure.cause });
64
+ this.failure = failure;
65
+ this.name = "AiError";
66
+ }
67
+ }
68
+ //# sourceMappingURL=failures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"failures.js","sourceRoot":"","sources":["../../src/types/failures.ts"],"names":[],"mappings":"AAwEA;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAc;IAC7C,MAAM,SAAS,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3D,MAAM,MAAM,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACtE,MAAM,cAAc,GAClB,OAAO,YAAY,KAAK,WAAW,IAAI,KAAK,YAAY,YAAY,CAAC;IAEvE,QAAQ,SAAS,EAAE,CAAC;QAClB,KAAK,YAAY;YACf,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QAC3D,KAAK,mBAAmB;YACtB,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QACzD,KAAK,iBAAiB;YACpB,OAAO,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QAC/C,KAAK,mBAAmB;YACtB,mEAAmE;YACnE,yEAAyE;YACzE,oEAAoE;YACpE,OAAO,EAAE,IAAI,EAAE,oBAAoB,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QACrE,KAAK,gBAAgB;YACnB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QAClD,KAAK,oBAAoB;YACvB,uEAAuE;YACvE,mBAAmB;YACnB,OAAO;gBACL,IAAI,EAAE,kBAAkB;gBACxB,KAAK,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE;gBAC1B,KAAK,EAAE,KAAK;aACb,CAAC;QACJ,KAAK,aAAa;YAChB,sEAAsE;YACtE,uEAAuE;YACvE,sEAAsE;YACtE,kEAAkE;YAClE,OAAO,cAAc;gBACnB,CAAC,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE;gBACjD,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QAC/C,KAAK,WAAW;YACd,mEAAmE;YACnE,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;QACzD;YACE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IAC7C,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,OAAQ,SAAQ,KAAK;IACX;IAArB,YAAqB,OAAkB;QACrC,uEAAuE;QACvE,qDAAqD;QACrD,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QAH3B,YAAO,GAAP,OAAO,CAAW;QAIrC,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC;IACxB,CAAC;CACF"}
@@ -0,0 +1,35 @@
1
+ declare const brand: unique symbol;
2
+ type Brand<T, Name extends string> = T & {
3
+ readonly [brand]: Name;
4
+ };
5
+ /** Non-negative integer count of context tokens. */
6
+ export type Tokens = Brand<number, "Tokens">;
7
+ export declare function tokens(value: number): Tokens | null;
8
+ /**
9
+ * 0..1, for download progress. A brand of its own, not `Tokens`: a ratio and a
10
+ * percentage (0.5 against 50) are both numbers, and swapping them is silent.
11
+ */
12
+ export type Fraction = Brand<number, "Fraction">;
13
+ export declare function fraction(value: number): Fraction | null;
14
+ /** A plain object: not an array, `Date` or function. */
15
+ export type JsonSchema = Brand<Record<string, unknown>, "JsonSchema">;
16
+ export declare function jsonSchema(value: unknown): JsonSchema | null;
17
+ /**
18
+ * Success or failure as one value.
19
+ *
20
+ * The failure path is visible in the signature and cannot be skipped: until
21
+ * `ok` is checked, `value` is not in the type. The cost is that `Result`
22
+ * spreads, so keep it at the adapter boundary — inside a provider's
23
+ * implementation, exceptions are fine.
24
+ */
25
+ export type Result<T, E> = {
26
+ readonly ok: true;
27
+ readonly value: T;
28
+ } | {
29
+ readonly ok: false;
30
+ readonly error: E;
31
+ };
32
+ export declare const ok: <T>(value: T) => Result<T, never>;
33
+ export declare const err: <E>(error: E) => Result<never, E>;
34
+ export {};
35
+ //# sourceMappingURL=foundations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"foundations.d.ts","sourceRoot":"","sources":["../../src/types/foundations.ts"],"names":[],"mappings":"AAGA,OAAO,CAAC,MAAM,KAAK,EAAE,OAAO,MAAM,CAAC;AACnC,KAAK,KAAK,CAAC,CAAC,EAAE,IAAI,SAAS,MAAM,IAAI,CAAC,GAAG;IAAE,QAAQ,CAAC,CAAC,KAAK,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC;AAEpE,oDAAoD;AACpD,MAAM,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AAI7C,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAInD;AAED;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAEjD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAEvD;AAED,wDAAwD;AACxD,MAAM,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,CAAC,CAAC;AAKtE,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,GAAG,IAAI,CAU5D;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,MAAM,CAAC,CAAC,EAAE,CAAC,IACnB;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GACxC;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAA;CAAE,CAAC;AAI9C,eAAO,MAAM,EAAE,GAAI,CAAC,EAAE,OAAO,CAAC,KAAG,MAAM,CAAC,CAAC,EAAE,KAAK,CAA0B,CAAC;AAC3E,eAAO,MAAM,GAAG,GAAI,CAAC,EAAE,OAAO,CAAC,KAAG,MAAM,CAAC,KAAK,EAAE,CAAC,CAA2B,CAAC"}
@@ -0,0 +1,30 @@
1
+ // Returns null rather than throwing: the input comes from someone else's API,
2
+ // and the provider has to decide what to do with junk.
3
+ export function tokens(value) {
4
+ // The condition to its left is what makes this `as` honest, and the same
5
+ // holds for every other cast in this file.
6
+ return Number.isInteger(value) && value >= 0 ? value : null;
7
+ }
8
+ export function fraction(value) {
9
+ return value >= 0 && value <= 1 ? value : null;
10
+ }
11
+ // Narrower than `object`, which in TypeScript covers arrays, `Date` and
12
+ // functions — all of which reach `responseConstraint` and fail deep inside the
13
+ // browser. `unknown` in: a schema almost always arrives from JSON.parse.
14
+ export function jsonSchema(value) {
15
+ if (typeof value !== "object" || value === null)
16
+ return null;
17
+ // The prototype, not `Array.isArray`: `Date`, `Map` and `RegExp` are objects
18
+ // that pass every shape check and only fail once the browser tries to read
19
+ // them as a schema. What JSON.parse builds carries `Object.prototype`, and
20
+ // `Object.create(null)` carries none.
21
+ const prototype = Object.getPrototypeOf(value);
22
+ return prototype === Object.prototype || prototype === null
23
+ ? value
24
+ : null;
25
+ }
26
+ // `never` for the other side means "cannot happen here", which lets the value
27
+ // slot into any Result<T, E> without naming E at the call site.
28
+ export const ok = (value) => ({ ok: true, value });
29
+ export const err = (error) => ({ ok: false, error });
30
+ //# sourceMappingURL=foundations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"foundations.js","sourceRoot":"","sources":["../../src/types/foundations.ts"],"names":[],"mappings":"AASA,8EAA8E;AAC9E,uDAAuD;AACvD,MAAM,UAAU,MAAM,CAAC,KAAa;IAClC,yEAAyE;IACzE,2CAA2C;IAC3C,OAAO,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,CAAE,KAAgB,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1E,CAAC;AAQD,MAAM,UAAU,QAAQ,CAAC,KAAa;IACpC,OAAO,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,CAAE,KAAkB,CAAC,CAAC,CAAC,IAAI,CAAC;AAC/D,CAAC;AAKD,wEAAwE;AACxE,+EAA+E;AAC/E,yEAAyE;AACzE,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC7D,6EAA6E;IAC7E,2EAA2E;IAC3E,2EAA2E;IAC3E,sCAAsC;IACtC,MAAM,SAAS,GAAY,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IACxD,OAAO,SAAS,KAAK,MAAM,CAAC,SAAS,IAAI,SAAS,KAAK,IAAI;QACzD,CAAC,CAAE,KAAoB;QACvB,CAAC,CAAC,IAAI,CAAC;AACX,CAAC;AAcD,8EAA8E;AAC9E,gEAAgE;AAChE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAI,KAAQ,EAAoB,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAC3E,MAAM,CAAC,MAAM,GAAG,GAAG,CAAI,KAAQ,EAAoB,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * `system` is missing on purpose. The spec accepts a system instruction only
3
+ * as the very first message and throws `TypeError` otherwise; here the system
4
+ * text is a session option instead, so a misplaced one cannot be written.
5
+ */
6
+ export type AiRole = "user" | "assistant";
7
+ export interface AiMessage {
8
+ readonly role: AiRole;
9
+ readonly content: string;
10
+ }
11
+ /** Text only for now; the other two gate model selection, not message content. */
12
+ export type Modality = "text" | "image" | "audio";
13
+ export interface ModalityExpectation {
14
+ readonly type: Modality;
15
+ /** BCP-47 tags. */
16
+ readonly languages?: readonly string[];
17
+ }
18
+ /**
19
+ * What the caller needs from the model. Availability depends on the request,
20
+ * not only on the environment — an image input or an unsupported language can
21
+ * make an otherwise present model unavailable — so it is asked for up front.
22
+ *
23
+ * Tools are absent because the contract has no tool support; when they arrive
24
+ * they belong here too, since they take part in choosing the model.
25
+ */
26
+ export interface ModelRequest {
27
+ readonly inputs?: readonly ModalityExpectation[];
28
+ readonly outputs?: readonly ModalityExpectation[];
29
+ }
30
+ //# sourceMappingURL=messages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/types/messages.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG,WAAW,CAAC;AAE1C,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAGtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,kFAAkF;AAClF,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAElD,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,mBAAmB;IACnB,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAC;IACjD,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,mBAAmB,EAAE,CAAC;CACnD"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/types/messages.ts"],"names":[],"mappings":""}
@@ -0,0 +1,22 @@
1
+ import type { ModelRequest } from "./messages.js";
2
+ import type { ModelAccess } from "./session.js";
3
+ /**
4
+ * Free-form, because a backend written outside this package names itself and
5
+ * the package cannot know that name in advance.
6
+ *
7
+ * The exhaustive switch a persisted choice needs did not disappear, it moved:
8
+ * an app's registry is the only place the full set is known, and
9
+ * `src/providers/registry.ts` derives the union from it. `src/types.test-d.ts`
10
+ * holds one — add a member without a branch and the build breaks.
11
+ */
12
+ export type ProviderName = string;
13
+ export interface AiProvider {
14
+ readonly name: ProviderName;
15
+ /**
16
+ * The single entry point; everything else hangs off the returned access.
17
+ * The request travels with it because it decides the answer: asking whether
18
+ * a model is available without saying what for is meaningless.
19
+ */
20
+ readonly access: (request?: ModelRequest) => Promise<ModelAccess>;
21
+ }
22
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/types/provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC;AAElC,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,YAAY,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;CACnE"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/types/provider.ts"],"names":[],"mappings":""}
@@ -0,0 +1,156 @@
1
+ import type { JsonSchema, Result } from "./foundations.js";
2
+ import type { AiMessage } from "./messages.js";
3
+ import type { ContextUsage } from "./usage.js";
4
+ import type { AiFailure } from "./failures.js";
5
+ /**
6
+ * Per-session options. Modalities and tools are deliberately not here: they
7
+ * belong to `ModelRequest`, because they decide which model gets loaded rather
8
+ * than how one session behaves.
9
+ */
10
+ export interface SessionOptions {
11
+ /** A field rather than a leading history entry, which is what makes a misplaced system turn unwritable. */
12
+ readonly system?: string;
13
+ readonly history?: readonly AiMessage[];
14
+ /** Aborts the create, including a model download in progress. */
15
+ readonly signal?: AbortSignal;
16
+ }
17
+ export interface GenerateOptions {
18
+ readonly signal?: AbortSignal;
19
+ /**
20
+ * `responseConstraint`: it constrains decoding, so the model cannot emit
21
+ * another shape.
22
+ *
23
+ * Honoured or refused, never ignored. A provider that cannot constrain
24
+ * decoding fails the call; answering anyway would hand back prose where the
25
+ * caller is about to run `JSON.parse`, and nothing in the Result says the
26
+ * constraint was dropped.
27
+ */
28
+ readonly schema?: JsonSchema;
29
+ }
30
+ /**
31
+ * One generation at a time: while a `prompt` or a `promptStream` is running,
32
+ * both calls fail with `busy`. A session is one transcript, and interleaving
33
+ * two turns into it produces a history no caller can repair; the refusal
34
+ * itself is `src/helpers/lifetime.ts`. Like the guarantee on `close`, this one is
35
+ * runtime: a type cannot say "not while that promise is pending".
36
+ *
37
+ * Callbacks are fields, not methods: `strictFunctionTypes` checks argument
38
+ * types strictly only on function-typed properties, while method syntax stays
39
+ * bivariant and would let an implementation narrow an argument.
40
+ */
41
+ export interface AiSession extends EventTarget {
42
+ /**
43
+ * The transcript has outgrown the window, and the oldest turns are being
44
+ * dropped to fit the newest — silently, wherever the backend does it. Fires
45
+ * once: the window does not un-overflow, and every turn after the first is
46
+ * over the same line.
47
+ *
48
+ * An `onevent` property rather than a typed `addEventListener`, for the
49
+ * reason spelled out on `DownloadMonitor`. The event carries nothing: what a
50
+ * listener wants next is `usage()`, which is current by the time this fires.
51
+ */
52
+ oncontextoverflow: ((event: Event) => void) | null;
53
+ readonly prompt: (input: string, options?: GenerateOptions) => Promise<Result<string, AiFailure>>;
54
+ /**
55
+ * A `ReadableStream`, not a bare async iterable, so the caller keeps `tee()`,
56
+ * `pipeThrough()` and `cancel()`.
57
+ *
58
+ * Consume it with a reader loop: async iteration of a stream is missing in
59
+ * Safari, and `dom.asynciterable` is deliberately left out of `lib` so the
60
+ * compiler rejects `for await` here. It also hides a trap — leaving such a
61
+ * loop with `break` cancels generation unless you iterate
62
+ * `stream.values({ preventCancel: true })`.
63
+ *
64
+ * Only a failure to start lands in Result; a break after the first chunk
65
+ * surfaces as a stream error, and that error is an `AiError` — a reader has
66
+ * nowhere to put a Result, so this is the one place a failure is thrown.
67
+ */
68
+ readonly promptStream: (input: string, options?: GenerateOptions) => Promise<Result<ReadableStream<string>, AiFailure>>;
69
+ readonly usage: () => ContextUsage;
70
+ /**
71
+ * The conversation so far: what was handed at open, then every completed
72
+ * turn. An aborted or cancelled turn adds nothing, and nothing is ever
73
+ * dropped — `src/helpers/transcript.ts` holds both rules.
74
+ *
75
+ * It lives as long as the session does, which is one tab. Storing it is the
76
+ * app's, and handing it back to `open` continues the conversation where it
77
+ * was — that round trip is how a reload is survived, not this array. Each
78
+ * call is a snapshot: later turns do not write into one already handed out.
79
+ */
80
+ readonly history: () => readonly AiMessage[];
81
+ /**
82
+ * In-flight and later calls fail with `aborted`, matching `destroy()`.
83
+ *
84
+ * Types cannot carry that guarantee — a value cannot be spent in TypeScript,
85
+ * so the old reference stays valid. Stating it as a runtime guarantee is
86
+ * honest; pretending the types hold it would not be.
87
+ */
88
+ readonly close: () => void;
89
+ }
90
+ type OpenSession = (options?: SessionOptions) => Promise<Result<AiSession, AiFailure>>;
91
+ /**
92
+ * The download monitor, shaped after the spec's `CreateMonitor`: an
93
+ * `EventTarget` firing `downloadprogress` with a `ProgressEvent`. The browser's
94
+ * own monitor satisfies this too, so a Prompt API provider can hand its one
95
+ * through — `src/types.test-d.ts` checks that against the ambient declaration.
96
+ * `src/helpers/monitor.ts` is the implementation for a provider with no browser
97
+ * behind it.
98
+ *
99
+ * `ondownloadprogress` is the whole surface because it is the only typed way
100
+ * in. An `addEventListener` overload narrowed to `downloadprogress` does not
101
+ * survive extending `EventTarget`: the inherited listener parameter is
102
+ * `EventListener`, a listener taking a `ProgressEvent` is not one, and neither
103
+ * this interface's implementations nor the ambient `CreateMonitor` then satisfy
104
+ * it (TS2420 and TS2322, measured). A caller wanting several listeners uses
105
+ * `addEventListener` and casts the event.
106
+ *
107
+ * What a provider owes a listener: `loaded` is 0..1, never decreasing, and
108
+ * `total` is 1 — see `DownloadProgressEvent` for where that normalization
109
+ * comes from, and `Fraction` for what carries it.
110
+ */
111
+ export interface DownloadMonitor extends EventTarget {
112
+ ondownloadprogress: ((event: ProgressEvent) => void) | null;
113
+ }
114
+ /**
115
+ * Availability and session creation are one value, not two calls. `open`
116
+ * exists only on the variants where opening can work, so "create a session
117
+ * from an unavailable model" is not a mistake to guard against — it is
118
+ * unwritable.
119
+ */
120
+ export type ModelAccess = {
121
+ readonly kind: "unavailable";
122
+ readonly reason: AiFailure;
123
+ } | {
124
+ readonly kind: "ready";
125
+ readonly open: OpenSession;
126
+ } | {
127
+ readonly kind: "needs-download";
128
+ /** True when another caller already started the fetch (`downloading` in the spec). */
129
+ readonly started: boolean;
130
+ /**
131
+ * The monitor is the first argument and required: this is the one branch
132
+ * where weights are certainly being fetched, and a silent download of
133
+ * hundreds of megabytes reads as a frozen UI.
134
+ *
135
+ * A callback handed the monitor, not a monitor handed back, and it runs
136
+ * before `open` awaits anything — subscribing after the promise settles
137
+ * is subscribing after the download. The spec's `monitor` option works
138
+ * the same way and for the same reason.
139
+ */
140
+ readonly open: (monitor: (monitor: DownloadMonitor) => void, options?: SessionOptions) => Promise<Result<AiSession, AiFailure>>;
141
+ };
142
+ /**
143
+ * The three answers, under a name — for a `Record` keyed by them, or a
144
+ * signature that takes the answer without the payload. Derived, so a fourth
145
+ * variant extends it rather than being forgotten here.
146
+ *
147
+ * A type and not an enum, which is the point: `"ready"` still passes wherever
148
+ * one of these is asked for, so a caller keeps writing literals and importing
149
+ * nothing, and none of it reaches their bundle. An enum would be a value, and
150
+ * a nominal one — a caller's own `"ready"` would stop being assignable.
151
+ * `FailureKind` and `UsageKind` are the same for the other two unions worth
152
+ * switching on.
153
+ */
154
+ export type AccessKind = ModelAccess["kind"];
155
+ export {};
156
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/types/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAE/C;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,2GAA2G;IAC3G,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAGzB,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,SAAS,EAAE,CAAC;IACxC,iEAAiE;IACjE,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;IAC9B;;;;;;;;OAQG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC;CAC9B;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,SAAU,SAAQ,WAAW;IAC5C;;;;;;;;;OASG;IACH,iBAAiB,EAAE,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;IACnD,QAAQ,CAAC,MAAM,EAAE,CACf,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,eAAe,KACtB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;IACxC;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,YAAY,EAAE,CACrB,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,eAAe,KACtB,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;IACxD,QAAQ,CAAC,KAAK,EAAE,MAAM,YAAY,CAAC;IACnC;;;;;;;;;OASG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,SAAS,SAAS,EAAE,CAAC;IAC7C;;;;;;OAMG;IACH,QAAQ,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC;CAC5B;AAED,KAAK,WAAW,GAAG,CACjB,OAAO,CAAC,EAAE,cAAc,KACrB,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,eAAgB,SAAQ,WAAW;IAClD,kBAAkB,EAAE,CAAC,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC;CAC7D;AAED;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAGnB;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAA;CAAE,GAC5D;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;CAAE,GACtD;IACE,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,sFAAsF;IACtF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B;;;;;;;;;OASG;IACH,QAAQ,CAAC,IAAI,EAAE,CACb,OAAO,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,EAC3C,OAAO,CAAC,EAAE,cAAc,KACrB,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC;CAC5C,CAAC;AAEN;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/types/session.ts"],"names":[],"mappings":""}
@@ -0,0 +1,25 @@
1
+ import { type Tokens } from "./foundations.js";
2
+ /**
3
+ * Context accounting in tokens.
4
+ *
5
+ * `unbounded` is a separate variant because the Prompt API reports `Infinity`
6
+ * there; as a plain number it would flow into `total - used` and produce a
7
+ * `remaining` that looks meaningful. `unknown` covers providers that expose no
8
+ * budget at all, which is cheaper than a `null` case at every call site.
9
+ */
10
+ export type ContextUsage = {
11
+ readonly kind: "unknown";
12
+ } | {
13
+ readonly kind: "unbounded";
14
+ readonly used: Tokens;
15
+ } | {
16
+ readonly kind: "bounded";
17
+ readonly used: Tokens;
18
+ readonly total: Tokens;
19
+ readonly remaining: Tokens;
20
+ };
21
+ /** Which of the three a measurement is, under a name; as `AccessKind` in `./session.ts`. */
22
+ export type UsageKind = ContextUsage["kind"];
23
+ /** The checked way to build one: no fractional total, no negative `remaining`. */
24
+ export declare function contextUsage(used: Tokens, total: number): ContextUsage;
25
+ //# sourceMappingURL=usage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage.d.ts","sourceRoot":"","sources":["../../src/types/usage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAEvD;;;;;;;GAOG;AACH,MAAM,MAAM,YAAY,GACpB;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GAC5B;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACrD;IACE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEN,4FAA4F;AAC5F,MAAM,MAAM,SAAS,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;AAE7C,kFAAkF;AAClF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,CAUtE"}
@@ -0,0 +1,16 @@
1
+ import { tokens } from "./foundations.js";
2
+ /** The checked way to build one: no fractional total, no negative `remaining`. */
3
+ export function contextUsage(used, total) {
4
+ if (total === Infinity)
5
+ return { kind: "unbounded", used };
6
+ // Usage is allowed to pass the window — that is what fires `contextoverflow`
7
+ // — but "minus three tokens left" is not a value worth carrying.
8
+ const remaining = tokens(Math.max(0, total - used));
9
+ const totalTokens = tokens(total);
10
+ // A fractional or negative total means the API returned something
11
+ // unexpected; saying "unknown" beats inventing a number.
12
+ if (totalTokens === null || remaining === null)
13
+ return { kind: "unknown" };
14
+ return { kind: "bounded", used, total: totalTokens, remaining };
15
+ }
16
+ //# sourceMappingURL=usage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usage.js","sourceRoot":"","sources":["../../src/types/usage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAe,MAAM,kBAAkB,CAAC;AAuBvD,kFAAkF;AAClF,MAAM,UAAU,YAAY,CAAC,IAAY,EAAE,KAAa;IACtD,IAAI,KAAK,KAAK,QAAQ;QAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IAC3D,6EAA6E;IAC7E,iEAAiE;IACjE,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC;IACpD,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAClC,kEAAkE;IAClE,yDAAyD;IACzD,IAAI,WAAW,KAAK,IAAI,IAAI,SAAS,KAAK,IAAI;QAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;IAC3E,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,CAAC;AAClE,CAAC"}