@mindees/ai 0.1.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.
Files changed (53) hide show
  1. package/LICENSE +31 -0
  2. package/README.md +57 -0
  3. package/dist/contract.d.ts +113 -0
  4. package/dist/contract.d.ts.map +1 -0
  5. package/dist/contract.js +18 -0
  6. package/dist/contract.js.map +1 -0
  7. package/dist/devtools.d.ts +43 -0
  8. package/dist/devtools.d.ts.map +1 -0
  9. package/dist/devtools.js +77 -0
  10. package/dist/devtools.js.map +1 -0
  11. package/dist/errors.d.ts +21 -0
  12. package/dist/errors.d.ts.map +1 -0
  13. package/dist/errors.js +18 -0
  14. package/dist/errors.js.map +1 -0
  15. package/dist/index.d.ts +26 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +29 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/json.d.ts +76 -0
  20. package/dist/json.d.ts.map +1 -0
  21. package/dist/json.js +256 -0
  22. package/dist/json.js.map +1 -0
  23. package/dist/mappers.d.ts +52 -0
  24. package/dist/mappers.d.ts.map +1 -0
  25. package/dist/mappers.js +312 -0
  26. package/dist/mappers.js.map +1 -0
  27. package/dist/mock.d.ts +26 -0
  28. package/dist/mock.d.ts.map +1 -0
  29. package/dist/mock.js +69 -0
  30. package/dist/mock.js.map +1 -0
  31. package/dist/object.d.ts +78 -0
  32. package/dist/object.d.ts.map +1 -0
  33. package/dist/object.js +140 -0
  34. package/dist/object.js.map +1 -0
  35. package/dist/on-device.d.ts +13 -0
  36. package/dist/on-device.d.ts.map +1 -0
  37. package/dist/on-device.js +33 -0
  38. package/dist/on-device.js.map +1 -0
  39. package/dist/server.d.ts +42 -0
  40. package/dist/server.d.ts.map +1 -0
  41. package/dist/server.js +64 -0
  42. package/dist/server.js.map +1 -0
  43. package/dist/sse.d.ts +24 -0
  44. package/dist/sse.d.ts.map +1 -0
  45. package/dist/sse.js +81 -0
  46. package/dist/sse.js.map +1 -0
  47. package/dist/standard-schema.d.ts +89 -0
  48. package/dist/standard-schema.d.ts.map +1 -0
  49. package/dist/tools.d.ts +61 -0
  50. package/dist/tools.d.ts.map +1 -0
  51. package/dist/tools.js +195 -0
  52. package/dist/tools.js.map +1 -0
  53. package/package.json +40 -0
package/LICENSE ADDED
@@ -0,0 +1,31 @@
1
+ # License
2
+
3
+ MindeesNative is dual-licensed under either of:
4
+
5
+ - **Apache License, Version 2.0** ([LICENSE-APACHE](./LICENSE-APACHE) or
6
+ <https://www.apache.org/licenses/LICENSE-2.0>)
7
+ - **MIT license** ([LICENSE-MIT](./LICENSE-MIT) or
8
+ <https://opensource.org/licenses/MIT>)
9
+
10
+ at your option.
11
+
12
+ This `MIT OR Apache-2.0` dual-license is the same model used by the Rust
13
+ ecosystem and many modern open-source projects. It gives downstream users
14
+ maximum flexibility: the MIT option is short and permissive, while the Apache
15
+ option adds an explicit patent grant.
16
+
17
+ ## SPDX identifier
18
+
19
+ ```
20
+ SPDX-License-Identifier: MIT OR Apache-2.0
21
+ ```
22
+
23
+ ## Contribution
24
+
25
+ Unless you explicitly state otherwise, any contribution intentionally
26
+ submitted for inclusion in the work by you, as defined in the Apache-2.0
27
+ license, shall be dual-licensed as above, without any additional terms or
28
+ conditions.
29
+
30
+ Contributions are accepted under the **Developer Certificate of Origin (DCO)**.
31
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) for details on signing off your commits.
package/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # @mindees/ai
2
+
3
+ **Synapse** โ€” provider-agnostic AI + dev-time intelligence for MindeesNative.
4
+
5
+ > Status: ๐Ÿงช **Experimental** โ€” Phase 11 (Synapse) is complete in its current scope.
6
+ > Implemented and tested: the provider-agnostic AI contract, deterministic mock backend,
7
+ > injected-`fetch` server/HTTP backend, Standard-Schema structured output, bounded tool
8
+ > calling, and dev-time error explainer. **On-device LLM inference is inherently native**
9
+ > (Apple Foundation Models, Android AICore/Gemini Nano, ExecuTorch, llama.rn) or web-only
10
+ > (WebGPU/WASM), so it is a ๐Ÿ”ฌ **research track** โ€” `createOnDeviceBackend()` throws
11
+ > `NotImplementedError`; the mock/server backends are the working fallback. See the
12
+ > repository [STATUS.md](../../STATUS.md).
13
+
14
+ ## What works today
15
+
16
+ A provider-agnostic contract every backend implements (`@mindees/core` only, zero
17
+ third-party deps):
18
+
19
+ - **`createAi({ backend })`** โ†’ `ai.generate(req)` (one-shot) and `ai.stream(req)`
20
+ (streamed). Streaming is an **`AsyncIterable`** (no Web/Node streams), so it runs on
21
+ Node, browsers, and Hermes/RN. Cancellation via a structural `AbortLike`.
22
+ - **`createMockBackend({ reply | script, chunkSize })`** โ€” deterministic, no network, no
23
+ keys: powers tests and offline apps.
24
+ - **`createOnDeviceBackend()`** โ€” the research-track seam (same interface, throws).
25
+ - Stable `AiError` codes; `Message`/`Part` types whose `tool-call`/`tool-result` parts
26
+ back the (11C) tool loop.
27
+ - **`@mindees/ai/server`** โ€” `createServerBackend({ fetch, baseUrl, model, ... })`
28
+ with OpenAI/Anthropic mappers and a pure-TS SSE parser, tested with fixture fetches
29
+ and no real network.
30
+ - **Structured output** โ€” `generateObject` / `streamObject` validate model JSON against
31
+ any Standard Schema, with bounded repair and sanitize-before-validate guards.
32
+ - **Tool calling** โ€” `runTools` validates args before execution, deduplicates identical
33
+ calls, supports parallel execution, and preserves an immutable transcript.
34
+ - **`@mindees/ai/devtools`** โ€” `explainError` and terminal formatting for build/dev tools;
35
+ surfaced by the CLI as `mindees ai explain <error>`.
36
+
37
+ ```ts
38
+ import { createAi, createMockBackend } from '@mindees/ai'
39
+
40
+ const ai = createAi({ backend: createMockBackend({ reply: 'Hello from Synapse' }) })
41
+
42
+ console.log((await ai.generate({ messages: [{ role: 'user', content: 'hi' }] })).text)
43
+
44
+ for await (const chunk of ai.stream({ messages: [{ role: 'user', content: 'hi' }] })) {
45
+ if (chunk.type === 'text-delta') process.stdout.write(chunk.delta)
46
+ }
47
+ ```
48
+
49
+ Design rationale: [ADR-0017](../../docs/adr/0017-synapse-ai-contract.md),
50
+ [ADR-0018](../../docs/adr/0018-synapse-server-backend.md),
51
+ [ADR-0019](../../docs/adr/0019-synapse-structured-output.md),
52
+ [ADR-0020](../../docs/adr/0020-synapse-tool-calling.md), and
53
+ [ADR-0021](../../docs/adr/0021-synapse-devtools.md).
54
+
55
+ ## License
56
+
57
+ `MIT OR Apache-2.0`
@@ -0,0 +1,113 @@
1
+ //#region src/contract.d.ts
2
+ /**
3
+ * The Synapse provider-agnostic AI contract โ€” a small, hand-rolled, pure-TS surface
4
+ * every backend (mock, server, on-device) implements. Streaming is `AsyncIterable`
5
+ * only (no Web `ReadableStream`, no Node streams), so it runs on Node, browsers, and
6
+ * Hermes/RN. See `docs/adr/0017-synapse-ai-contract.md`.
7
+ *
8
+ * @module
9
+ */
10
+ /** Who authored a message. */
11
+ type Role = 'system' | 'user' | 'assistant' | 'tool';
12
+ /** A plain text part. */
13
+ interface TextPart {
14
+ readonly type: 'text';
15
+ readonly text: string;
16
+ }
17
+ /** A model's request to call a tool (the args are model-produced โ‡’ untrusted). */
18
+ interface ToolCallPart {
19
+ readonly type: 'tool-call';
20
+ readonly id: string;
21
+ readonly name: string;
22
+ readonly args: unknown;
23
+ }
24
+ /** The result of running a tool, fed back to the model. */
25
+ interface ToolResultPart {
26
+ readonly type: 'tool-result';
27
+ readonly id: string;
28
+ readonly name: string;
29
+ readonly result: unknown;
30
+ }
31
+ /** A piece of message content. */
32
+ type Part = TextPart | ToolCallPart | ToolResultPart;
33
+ /** One message in a conversation. */
34
+ interface Message {
35
+ readonly role: Role;
36
+ readonly content: string | readonly Part[];
37
+ }
38
+ /** Why a generation stopped. */
39
+ type FinishReason = 'stop' | 'length' | 'tool-calls' | 'error';
40
+ /** Token usage, when the backend reports it. */
41
+ interface Usage {
42
+ readonly inputTokens?: number;
43
+ readonly outputTokens?: number;
44
+ }
45
+ /** Minimal cancellation signal โ€” a real `AbortSignal` is structurally compatible. */
46
+ interface AbortLike {
47
+ readonly aborted: boolean;
48
+ }
49
+ /**
50
+ * A tool the model may call, as sent to the backend (the **wire** shape โ€” no `execute`).
51
+ * `parameters` is a provider-native JSON Schema sent verbatim; the runtime-validating
52
+ * {@link import('./tools').Tool} adds `execute` (+ an optional Standard Schema) on top.
53
+ */
54
+ interface ToolDefinition {
55
+ readonly name: string;
56
+ readonly description?: string;
57
+ /** JSON Schema describing the arguments (sent to the provider as-is). */
58
+ readonly parameters?: Record<string, unknown>;
59
+ }
60
+ /** A one-shot or streaming generation request. */
61
+ interface GenerateRequest {
62
+ readonly messages: readonly Message[];
63
+ /** 0โ€“2-ish sampling temperature (backend-defined default). */
64
+ readonly temperature?: number;
65
+ /** Cap on output tokens (backend-defined default). */
66
+ readonly maxOutputTokens?: number;
67
+ /** Tools the model may call. A backend that can't express tools must throw, not drop them. */
68
+ readonly tools?: readonly ToolDefinition[];
69
+ /** Cancellation. */
70
+ readonly signal?: AbortLike;
71
+ }
72
+ /** The result of {@link AiBackend.generate}. */
73
+ interface AiResult {
74
+ readonly text: string;
75
+ readonly toolCalls?: readonly ToolCallPart[];
76
+ readonly finishReason: FinishReason;
77
+ readonly usage?: Usage;
78
+ }
79
+ /** One chunk of a streamed generation. */
80
+ type AiChunk = {
81
+ readonly type: 'text-delta';
82
+ readonly delta: string;
83
+ } | {
84
+ readonly type: 'tool-call';
85
+ readonly id: string;
86
+ readonly name: string;
87
+ readonly args: unknown;
88
+ } | {
89
+ readonly type: 'finish';
90
+ readonly finishReason: FinishReason;
91
+ readonly usage?: Usage;
92
+ };
93
+ /** The single seam every backend implements (mock, server, on-device). */
94
+ interface AiBackend {
95
+ /** One-shot generation. */
96
+ generate(request: GenerateRequest): Promise<AiResult>;
97
+ /** Streamed generation as an async iterable (throws `AiError` on failure). */
98
+ stream(request: GenerateRequest): AsyncIterable<AiChunk>;
99
+ }
100
+ /** The app-facing AI handle. */
101
+ interface Ai {
102
+ generate(request: GenerateRequest): Promise<AiResult>;
103
+ stream(request: GenerateRequest): AsyncIterable<AiChunk>;
104
+ }
105
+ /** Wrap a {@link AiBackend} in the app-facing {@link Ai} handle. */
106
+ declare function createAi(options: {
107
+ readonly backend: AiBackend;
108
+ }): Ai;
109
+ /** Flatten a message's content to plain text (ignores non-text parts). */
110
+ declare function messageText(message: Message): string;
111
+ //#endregion
112
+ export { AbortLike, Ai, AiBackend, AiChunk, AiResult, FinishReason, GenerateRequest, Message, Part, Role, TextPart, ToolCallPart, ToolDefinition, ToolResultPart, Usage, createAi, messageText };
113
+ //# sourceMappingURL=contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.d.ts","names":[],"sources":["../src/contract.ts"],"mappings":";;AAUA;;;;AAAgB;AAGhB;;;KAHY,IAAA;AAKG;AAAA,UAFE,QAAA;EAAA,SACN,IAAA;EAAA,SACA,IAAI;AAAA;;UAIE,YAAA;EAAA,SACN,IAAA;EAAA,SACA,EAAA;EAAA,SACA,IAAA;EAAA,SACA,IAAA;AAAA;;UAIM,cAAA;EAAA,SACN,IAAA;EAAA,SACA,EAAA;EAAA,SACA,IAAA;EAAA,SACA,MAAA;AAAA;;KAIC,IAAA,GAAO,QAAA,GAAW,YAAA,GAAe,cAAA;AAA7C;AAAA,UAGiB,OAAA;EAAA,SACN,IAAA,EAAM,IAAA;EAAA,SACN,OAAA,oBAA2B,IAAI;AAAA;;KAI9B,YAAA;;UAGK,KAAA;EAAA,SACN,WAAA;EAAA,SACA,YAAY;AAAA;AAdoC;AAAA,UAkB1C,SAAA;EAAA,SACN,OAAO;AAAA;;;;;;UAQD,cAAA;EAAA,SACN,IAAA;EAAA,SACA,WAAA;EApBa;EAAA,SAsBb,UAAA,GAAa,MAAM;AAAA;AAtBN;AAAA,UA0BP,eAAA;EAAA,SACN,QAAA,WAAmB,OAAA;;WAEnB,WAAA;EAxBY;EAAA,SA0BZ,eAAA;EAtBe;EAAA,SAwBf,KAAA,YAAiB,cAAA;EAvBV;EAAA,SAyBP,MAAA,GAAS,SAAA;AAAA;;UAIH,QAAA;EAAA,SACN,IAAA;EAAA,SACA,SAAA,YAAqB,YAAA;EAAA,SACrB,YAAA,EAAc,YAAA;EAAA,SACd,KAAA,GAAQ,KAAA;AAAA;;KAIP,OAAA;EAAA,SACG,IAAA;EAAA,SAA6B,KAAA;AAAA;EAAA,SAE7B,IAAA;EAAA,SACA,EAAA;EAAA,SACA,IAAA;EAAA,SACA,IAAA;AAAA;EAAA,SAEA,IAAA;EAAA,SAAyB,YAAA,EAAc,YAAA;EAAA,SAAuB,KAAA,GAAQ,KAAK;AAAA;;UAGzE,SAAA;EAvBN;EAyBT,QAAA,CAAS,OAAA,EAAS,eAAA,GAAkB,OAAA,CAAQ,QAAA;EAzBjB;EA2B3B,MAAA,CAAO,OAAA,EAAS,eAAA,GAAkB,aAAA,CAAc,OAAA;AAAA;;UAIjC,EAAA;EACf,QAAA,CAAS,OAAA,EAAS,eAAA,GAAkB,OAAA,CAAQ,QAAA;EAC5C,MAAA,CAAO,OAAA,EAAS,eAAA,GAAkB,aAAA,CAAc,OAAA;AAAA;;iBAIlC,QAAA,CAAS,OAAA;EAAA,SAAoB,OAAA,EAAS,SAAA;AAAA,IAAc,EAAE;;iBAStD,WAAA,CAAY,OAAgB,EAAP,OAAO"}
@@ -0,0 +1,18 @@
1
+ //#region src/contract.ts
2
+ /** Wrap a {@link AiBackend} in the app-facing {@link Ai} handle. */
3
+ function createAi(options) {
4
+ const { backend } = options;
5
+ return {
6
+ generate: (request) => backend.generate(request),
7
+ stream: (request) => backend.stream(request)
8
+ };
9
+ }
10
+ /** Flatten a message's content to plain text (ignores non-text parts). */
11
+ function messageText(message) {
12
+ if (typeof message.content === "string") return message.content;
13
+ return message.content.filter((part) => part.type === "text").map((part) => part.text).join("");
14
+ }
15
+ //#endregion
16
+ export { createAi, messageText };
17
+
18
+ //# sourceMappingURL=contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.js","names":[],"sources":["../src/contract.ts"],"sourcesContent":["/**\n * The Synapse provider-agnostic AI contract โ€” a small, hand-rolled, pure-TS surface\n * every backend (mock, server, on-device) implements. Streaming is `AsyncIterable`\n * only (no Web `ReadableStream`, no Node streams), so it runs on Node, browsers, and\n * Hermes/RN. See `docs/adr/0017-synapse-ai-contract.md`.\n *\n * @module\n */\n\n/** Who authored a message. */\nexport type Role = 'system' | 'user' | 'assistant' | 'tool'\n\n/** A plain text part. */\nexport interface TextPart {\n readonly type: 'text'\n readonly text: string\n}\n\n/** A model's request to call a tool (the args are model-produced โ‡’ untrusted). */\nexport interface ToolCallPart {\n readonly type: 'tool-call'\n readonly id: string\n readonly name: string\n readonly args: unknown\n}\n\n/** The result of running a tool, fed back to the model. */\nexport interface ToolResultPart {\n readonly type: 'tool-result'\n readonly id: string\n readonly name: string\n readonly result: unknown\n}\n\n/** A piece of message content. */\nexport type Part = TextPart | ToolCallPart | ToolResultPart\n\n/** One message in a conversation. */\nexport interface Message {\n readonly role: Role\n readonly content: string | readonly Part[]\n}\n\n/** Why a generation stopped. */\nexport type FinishReason = 'stop' | 'length' | 'tool-calls' | 'error'\n\n/** Token usage, when the backend reports it. */\nexport interface Usage {\n readonly inputTokens?: number\n readonly outputTokens?: number\n}\n\n/** Minimal cancellation signal โ€” a real `AbortSignal` is structurally compatible. */\nexport interface AbortLike {\n readonly aborted: boolean\n}\n\n/**\n * A tool the model may call, as sent to the backend (the **wire** shape โ€” no `execute`).\n * `parameters` is a provider-native JSON Schema sent verbatim; the runtime-validating\n * {@link import('./tools').Tool} adds `execute` (+ an optional Standard Schema) on top.\n */\nexport interface ToolDefinition {\n readonly name: string\n readonly description?: string\n /** JSON Schema describing the arguments (sent to the provider as-is). */\n readonly parameters?: Record<string, unknown>\n}\n\n/** A one-shot or streaming generation request. */\nexport interface GenerateRequest {\n readonly messages: readonly Message[]\n /** 0โ€“2-ish sampling temperature (backend-defined default). */\n readonly temperature?: number\n /** Cap on output tokens (backend-defined default). */\n readonly maxOutputTokens?: number\n /** Tools the model may call. A backend that can't express tools must throw, not drop them. */\n readonly tools?: readonly ToolDefinition[]\n /** Cancellation. */\n readonly signal?: AbortLike\n}\n\n/** The result of {@link AiBackend.generate}. */\nexport interface AiResult {\n readonly text: string\n readonly toolCalls?: readonly ToolCallPart[]\n readonly finishReason: FinishReason\n readonly usage?: Usage\n}\n\n/** One chunk of a streamed generation. */\nexport type AiChunk =\n | { readonly type: 'text-delta'; readonly delta: string }\n | {\n readonly type: 'tool-call'\n readonly id: string\n readonly name: string\n readonly args: unknown\n }\n | { readonly type: 'finish'; readonly finishReason: FinishReason; readonly usage?: Usage }\n\n/** The single seam every backend implements (mock, server, on-device). */\nexport interface AiBackend {\n /** One-shot generation. */\n generate(request: GenerateRequest): Promise<AiResult>\n /** Streamed generation as an async iterable (throws `AiError` on failure). */\n stream(request: GenerateRequest): AsyncIterable<AiChunk>\n}\n\n/** The app-facing AI handle. */\nexport interface Ai {\n generate(request: GenerateRequest): Promise<AiResult>\n stream(request: GenerateRequest): AsyncIterable<AiChunk>\n}\n\n/** Wrap a {@link AiBackend} in the app-facing {@link Ai} handle. */\nexport function createAi(options: { readonly backend: AiBackend }): Ai {\n const { backend } = options\n return {\n generate: (request) => backend.generate(request),\n stream: (request) => backend.stream(request),\n }\n}\n\n/** Flatten a message's content to plain text (ignores non-text parts). */\nexport function messageText(message: Message): string {\n if (typeof message.content === 'string') return message.content\n return message.content\n .filter((part): part is TextPart => part.type === 'text')\n .map((part) => part.text)\n .join('')\n}\n"],"mappings":";;AAoHA,SAAgB,SAAS,SAA8C;CACrE,MAAM,EAAE,YAAY;CACpB,OAAO;EACL,WAAW,YAAY,QAAQ,SAAS,OAAO;EAC/C,SAAS,YAAY,QAAQ,OAAO,OAAO;CAC7C;AACF;;AAGA,SAAgB,YAAY,SAA0B;CACpD,IAAI,OAAO,QAAQ,YAAY,UAAU,OAAO,QAAQ;CACxD,OAAO,QAAQ,QACZ,QAAQ,SAA2B,KAAK,SAAS,MAAM,EACvD,KAAK,SAAS,KAAK,IAAI,EACvB,KAAK,EAAE;AACZ"}
@@ -0,0 +1,43 @@
1
+ import { AbortLike, AiBackend } from "./contract.js";
2
+
3
+ //#region src/devtools.d.ts
4
+ /** A normalized error to explain (a plain object, or pass an `Error` directly). */
5
+ interface ExplainInput {
6
+ readonly message: string;
7
+ readonly stack?: string;
8
+ readonly code?: string | number;
9
+ }
10
+ /** Options for {@link explainError}. */
11
+ interface ExplainOptions {
12
+ /** Language/runtime hint, e.g. `'TypeScript'` / `'Node 24'`. */
13
+ readonly language?: string;
14
+ /** Relevant source around the error, included verbatim in the prompt. */
15
+ readonly codeContext?: string;
16
+ /** Repair re-asks if the model's JSON is malformed (default `1`). */
17
+ readonly maxRepairs?: number;
18
+ /** Cancellation. */
19
+ readonly signal?: AbortLike;
20
+ }
21
+ /** A structured explanation of an error. */
22
+ interface ErrorExplanation {
23
+ /** One- or two-sentence plain-language summary of what went wrong. */
24
+ readonly summary: string;
25
+ /** Most likely causes, most-likely first. */
26
+ readonly likelyCauses: readonly string[];
27
+ /** Concrete, ordered suggested fixes. */
28
+ readonly suggestedFixes: readonly string[];
29
+ }
30
+ /**
31
+ * Explain an error using the model behind `backend`, returning a validated
32
+ * {@link ErrorExplanation}. Built on {@link generateObject} (prompt โ†’ extract โ†’ sanitize โ†’
33
+ * validate โ†’ bounded repair), so it runs offline against the mock and live against a server.
34
+ *
35
+ * @throws AiError `INVALID_OBJECT` if the model can't produce a usable explanation in the
36
+ * repair budget, or `ABORTED` on cancellation.
37
+ */
38
+ declare function explainError(backend: Pick<AiBackend, 'generate'>, error: Error | ExplainInput, options?: ExplainOptions): Promise<ErrorExplanation>;
39
+ /** Render an {@link ErrorExplanation} as readable terminal text. */
40
+ declare function formatExplanation(explanation: ErrorExplanation): string;
41
+ //#endregion
42
+ export { ErrorExplanation, ExplainInput, ExplainOptions, explainError, formatExplanation };
43
+ //# sourceMappingURL=devtools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devtools.d.ts","names":[],"sources":["../src/devtools.ts"],"mappings":";;;AAmBe;AAAA,UAHE,YAAA;EAAA,SACN,OAAA;EAAA,SACA,KAAA;EAAA,SACA,IAAA;AAAA;;UAIM,cAAA;EAQN;EAAA,SANA,QAAA;EAMkB;EAAA,SAJlB,WAAA;EAQM;EAAA,SANN,UAAA;;WAEA,MAAA,GAAS,SAAS;AAAA;;UAIZ,gBAAA;EAMQ;EAAA,SAJd,OAAA;EAuDW;EAAA,SArDX,YAAA;;WAEA,cAAA;AAAA;;;;;;;;;iBAmDW,YAAA,CACpB,OAAA,EAAS,IAAA,CAAK,SAAA,eACd,KAAA,EAAO,KAAA,GAAQ,YAAA,EACf,OAAA,GAAS,cAAA,GACR,OAAA,CAAQ,gBAAA;;iBA2BK,iBAAA,CAAkB,WAA6B,EAAhB,gBAAgB"}
@@ -0,0 +1,77 @@
1
+ import { generateObject } from "./object.js";
2
+ //#region src/devtools.ts
3
+ function toStringArray(value) {
4
+ return Array.isArray(value) ? value.filter((x) => typeof x === "string") : [];
5
+ }
6
+ const explanationSchema = { "~standard": {
7
+ version: 1,
8
+ vendor: "mindees-devtools",
9
+ validate: (value) => {
10
+ const o = typeof value === "object" && value !== null ? value : void 0;
11
+ const summary = typeof o?.summary === "string" && o.summary.length > 0 ? o.summary : void 0;
12
+ if (summary === void 0) return { issues: [{
13
+ message: "summary must be a non-empty string",
14
+ path: ["summary"]
15
+ }] };
16
+ return { value: {
17
+ summary,
18
+ likelyCauses: toStringArray(o?.likelyCauses),
19
+ suggestedFixes: toStringArray(o?.suggestedFixes)
20
+ } };
21
+ }
22
+ } };
23
+ function normalize(error) {
24
+ if (error instanceof Error) {
25
+ const code = error.code;
26
+ return {
27
+ message: error.message,
28
+ ...error.stack ? { stack: error.stack } : {},
29
+ ...typeof code === "string" || typeof code === "number" ? { code } : {}
30
+ };
31
+ }
32
+ return error;
33
+ }
34
+ /**
35
+ * Explain an error using the model behind `backend`, returning a validated
36
+ * {@link ErrorExplanation}. Built on {@link generateObject} (prompt โ†’ extract โ†’ sanitize โ†’
37
+ * validate โ†’ bounded repair), so it runs offline against the mock and live against a server.
38
+ *
39
+ * @throws AiError `INVALID_OBJECT` if the model can't produce a usable explanation in the
40
+ * repair budget, or `ABORTED` on cancellation.
41
+ */
42
+ async function explainError(backend, error, options = {}) {
43
+ const input = normalize(error);
44
+ return (await generateObject(backend, {
45
+ messages: [{
46
+ role: "user",
47
+ content: [
48
+ "You are a senior debugging assistant for TypeScript/JavaScript applications.",
49
+ "Explain the error below and how to fix it โ€” be concrete and concise.",
50
+ options.language ? `Language/runtime: ${options.language}.` : "",
51
+ `Error message: ${input.message}`,
52
+ input.code !== void 0 ? `Error code: ${input.code}` : "",
53
+ input.stack ? `Stack trace:\n${input.stack}` : "",
54
+ options.codeContext ? `Relevant code:\n${options.codeContext}` : "",
55
+ "Reply as JSON: { \"summary\": string, \"likelyCauses\": string[], \"suggestedFixes\": string[] }."
56
+ ].filter(Boolean).join("\n")
57
+ }],
58
+ ...options.signal ? { signal: options.signal } : {}
59
+ }, explanationSchema, { maxRepairs: options.maxRepairs ?? 1 })).object;
60
+ }
61
+ /** Render an {@link ErrorExplanation} as readable terminal text. */
62
+ function formatExplanation(explanation) {
63
+ const lines = [`Summary: ${explanation.summary}`];
64
+ if (explanation.likelyCauses.length > 0) {
65
+ lines.push("", "Likely causes:");
66
+ for (const cause of explanation.likelyCauses) lines.push(` โ€ข ${cause}`);
67
+ }
68
+ if (explanation.suggestedFixes.length > 0) {
69
+ lines.push("", "Suggested fixes:");
70
+ for (const fix of explanation.suggestedFixes) lines.push(` โ€ข ${fix}`);
71
+ }
72
+ return lines.join("\n");
73
+ }
74
+ //#endregion
75
+ export { explainError, formatExplanation };
76
+
77
+ //# sourceMappingURL=devtools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devtools.js","names":[],"sources":["../src/devtools.ts"],"sourcesContent":["/**\n * Synapse dev-time intelligence โ€” a **build/dev-only** error explainer over the same AI\n * contract. `explainError` turns a thrown error into a structured, actionable explanation via\n * {@link generateObject}, so it works against any backend (the deterministic mock in tests, a\n * server backend in a CLI). Exported from the `@mindees/ai/devtools` subpath to signal intent:\n * this is for your toolchain, not the device bundle (don't ship it into a running app). See\n * `docs/adr/0021-synapse-devtools.md`.\n *\n * @module\n */\n\nimport type { AbortLike, AiBackend, GenerateRequest, Message } from './contract'\nimport { generateObject } from './object'\nimport type { StandardSchemaV1 } from './standard-schema'\n\n/** A normalized error to explain (a plain object, or pass an `Error` directly). */\nexport interface ExplainInput {\n readonly message: string\n readonly stack?: string\n readonly code?: string | number\n}\n\n/** Options for {@link explainError}. */\nexport interface ExplainOptions {\n /** Language/runtime hint, e.g. `'TypeScript'` / `'Node 24'`. */\n readonly language?: string\n /** Relevant source around the error, included verbatim in the prompt. */\n readonly codeContext?: string\n /** Repair re-asks if the model's JSON is malformed (default `1`). */\n readonly maxRepairs?: number\n /** Cancellation. */\n readonly signal?: AbortLike\n}\n\n/** A structured explanation of an error. */\nexport interface ErrorExplanation {\n /** One- or two-sentence plain-language summary of what went wrong. */\n readonly summary: string\n /** Most likely causes, most-likely first. */\n readonly likelyCauses: readonly string[]\n /** Concrete, ordered suggested fixes. */\n readonly suggestedFixes: readonly string[]\n}\n\nfunction toStringArray(value: unknown): string[] {\n return Array.isArray(value) ? value.filter((x): x is string => typeof x === 'string') : []\n}\n\n// A lenient Standard Schema: requires a `summary` string; coerces the two arrays (a model may\n// omit them). Validated through the same sanitizeโ†’validate pipeline as any structured output.\nconst explanationSchema: StandardSchemaV1<unknown, ErrorExplanation> = {\n '~standard': {\n version: 1,\n vendor: 'mindees-devtools',\n validate: (value) => {\n const o =\n typeof value === 'object' && value !== null ? (value as Record<string, unknown>) : undefined\n const summary = typeof o?.summary === 'string' && o.summary.length > 0 ? o.summary : undefined\n if (summary === undefined) {\n return { issues: [{ message: 'summary must be a non-empty string', path: ['summary'] }] }\n }\n return {\n value: {\n summary,\n likelyCauses: toStringArray(o?.likelyCauses),\n suggestedFixes: toStringArray(o?.suggestedFixes),\n },\n }\n },\n },\n}\n\nfunction normalize(error: Error | ExplainInput): ExplainInput {\n if (error instanceof Error) {\n const code = (error as { code?: unknown }).code\n return {\n message: error.message,\n ...(error.stack ? { stack: error.stack } : {}),\n ...(typeof code === 'string' || typeof code === 'number' ? { code } : {}),\n }\n }\n return error\n}\n\n/**\n * Explain an error using the model behind `backend`, returning a validated\n * {@link ErrorExplanation}. Built on {@link generateObject} (prompt โ†’ extract โ†’ sanitize โ†’\n * validate โ†’ bounded repair), so it runs offline against the mock and live against a server.\n *\n * @throws AiError `INVALID_OBJECT` if the model can't produce a usable explanation in the\n * repair budget, or `ABORTED` on cancellation.\n */\nexport async function explainError(\n backend: Pick<AiBackend, 'generate'>,\n error: Error | ExplainInput,\n options: ExplainOptions = {},\n): Promise<ErrorExplanation> {\n const input = normalize(error)\n const prompt = [\n 'You are a senior debugging assistant for TypeScript/JavaScript applications.',\n 'Explain the error below and how to fix it โ€” be concrete and concise.',\n options.language ? `Language/runtime: ${options.language}.` : '',\n `Error message: ${input.message}`,\n input.code !== undefined ? `Error code: ${input.code}` : '',\n input.stack ? `Stack trace:\\n${input.stack}` : '',\n options.codeContext ? `Relevant code:\\n${options.codeContext}` : '',\n 'Reply as JSON: { \"summary\": string, \"likelyCauses\": string[], \"suggestedFixes\": string[] }.',\n ]\n .filter(Boolean)\n .join('\\n')\n\n const message: Message = { role: 'user', content: prompt }\n const request: GenerateRequest = {\n messages: [message],\n ...(options.signal ? { signal: options.signal } : {}),\n }\n const result = await generateObject(backend, request, explanationSchema, {\n maxRepairs: options.maxRepairs ?? 1,\n })\n return result.object\n}\n\n/** Render an {@link ErrorExplanation} as readable terminal text. */\nexport function formatExplanation(explanation: ErrorExplanation): string {\n const lines: string[] = [`Summary: ${explanation.summary}`]\n if (explanation.likelyCauses.length > 0) {\n lines.push('', 'Likely causes:')\n for (const cause of explanation.likelyCauses) lines.push(` โ€ข ${cause}`)\n }\n if (explanation.suggestedFixes.length > 0) {\n lines.push('', 'Suggested fixes:')\n for (const fix of explanation.suggestedFixes) lines.push(` โ€ข ${fix}`)\n }\n return lines.join('\\n')\n}\n"],"mappings":";;AA4CA,SAAS,cAAc,OAA0B;CAC/C,OAAO,MAAM,QAAQ,KAAK,IAAI,MAAM,QAAQ,MAAmB,OAAO,MAAM,QAAQ,IAAI,CAAC;AAC3F;AAIA,MAAM,oBAAiE,EACrE,aAAa;CACX,SAAS;CACT,QAAQ;CACR,WAAW,UAAU;EACnB,MAAM,IACJ,OAAO,UAAU,YAAY,UAAU,OAAQ,QAAoC,KAAA;EACrF,MAAM,UAAU,OAAO,GAAG,YAAY,YAAY,EAAE,QAAQ,SAAS,IAAI,EAAE,UAAU,KAAA;EACrF,IAAI,YAAY,KAAA,GACd,OAAO,EAAE,QAAQ,CAAC;GAAE,SAAS;GAAsC,MAAM,CAAC,SAAS;EAAE,CAAC,EAAE;EAE1F,OAAO,EACL,OAAO;GACL;GACA,cAAc,cAAc,GAAG,YAAY;GAC3C,gBAAgB,cAAc,GAAG,cAAc;EACjD,EACF;CACF;AACF,EACF;AAEA,SAAS,UAAU,OAA2C;CAC5D,IAAI,iBAAiB,OAAO;EAC1B,MAAM,OAAQ,MAA6B;EAC3C,OAAO;GACL,SAAS,MAAM;GACf,GAAI,MAAM,QAAQ,EAAE,OAAO,MAAM,MAAM,IAAI,CAAC;GAC5C,GAAI,OAAO,SAAS,YAAY,OAAO,SAAS,WAAW,EAAE,KAAK,IAAI,CAAC;EACzE;CACF;CACA,OAAO;AACT;;;;;;;;;AAUA,eAAsB,aACpB,SACA,OACA,UAA0B,CAAC,GACA;CAC3B,MAAM,QAAQ,UAAU,KAAK;CAsB7B,QAAO,MAHc,eAAe,SAAS;EAH3C,UAAU,CAAC;GAFc,MAAM;GAAQ,SAb1B;IACb;IACA;IACA,QAAQ,WAAW,qBAAqB,QAAQ,SAAS,KAAK;IAC9D,kBAAkB,MAAM;IACxB,MAAM,SAAS,KAAA,IAAY,eAAe,MAAM,SAAS;IACzD,MAAM,QAAQ,iBAAiB,MAAM,UAAU;IAC/C,QAAQ,cAAc,mBAAmB,QAAQ,gBAAgB;IACjE;GACF,EACG,OAAO,OAAO,EACd,KAAK,IAE+C;EAEpC,CAAC;EAClB,GAAI,QAAQ,SAAS,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;CAEF,GAAG,mBAAmB,EACvE,YAAY,QAAQ,cAAc,EACpC,CAAC,GACa;AAChB;;AAGA,SAAgB,kBAAkB,aAAuC;CACvE,MAAM,QAAkB,CAAC,YAAY,YAAY,SAAS;CAC1D,IAAI,YAAY,aAAa,SAAS,GAAG;EACvC,MAAM,KAAK,IAAI,gBAAgB;EAC/B,KAAK,MAAM,SAAS,YAAY,cAAc,MAAM,KAAK,OAAO,OAAO;CACzE;CACA,IAAI,YAAY,eAAe,SAAS,GAAG;EACzC,MAAM,KAAK,IAAI,kBAAkB;EACjC,KAAK,MAAM,OAAO,YAAY,gBAAgB,MAAM,KAAK,OAAO,KAAK;CACvE;CACA,OAAO,MAAM,KAAK,IAAI;AACxB"}
@@ -0,0 +1,21 @@
1
+ import { StandardSchemaV1 } from "./standard-schema.js";
2
+
3
+ //#region src/errors.d.ts
4
+ /** Stable code identifying why an AI operation failed. */
5
+ type AiErrorCode = /** No transport/fetch was provided to a backend that needs one. */'NO_TRANSPORT' /** The server returned a non-2xx status. */ | 'HTTP_STATUS' /** A streaming response could not be parsed. */ | 'STREAM_PARSE' /** Structured output failed schema validation (after repair attempts). */ | 'INVALID_OBJECT' /** A tool-calling loop exceeded its step ceiling. */ | 'MAX_STEPS' /** A tool handler threw. */ | 'TOOL_FAILED' /** The request was aborted. */ | 'ABORTED' /** An on-device / research-track capability is not implemented. */ | 'NOT_IMPLEMENTED';
6
+ /** Optional structured detail attached to an {@link AiError}. */
7
+ interface AiErrorOptions {
8
+ /** Standard Schema issues โ€” set on `INVALID_OBJECT` from schema validation. */
9
+ readonly issues?: ReadonlyArray<StandardSchemaV1.Issue>;
10
+ }
11
+ /** An AI error carrying a stable {@link AiErrorCode}. */
12
+ declare class AiError extends Error {
13
+ /** Stable, machine-readable cause. */
14
+ readonly code: AiErrorCode;
15
+ /** Validation issues, when the failure came from schema validation. */
16
+ readonly issues?: ReadonlyArray<StandardSchemaV1.Issue>;
17
+ constructor(code: AiErrorCode, message: string, options?: AiErrorOptions);
18
+ }
19
+ //#endregion
20
+ export { AiError, AiErrorCode, AiErrorOptions };
21
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","names":[],"sources":["../src/errors.ts"],"mappings":";;;AAUuB;AAAA,KAAX,WAAA,kTAqB4C;;UAFvC,cAAA;EAMiB;EAAA,SAJvB,MAAA,GAAS,aAAa,CAAC,gBAAA,CAAiB,KAAA;AAAA;;cAItC,OAAA,SAAgB,KAAA;EAIlB;EAAA,SAFA,IAAA,EAAM,WAAA;EAEiB;EAAA,SAAvB,MAAA,GAAS,aAAA,CAAc,gBAAA,CAAiB,KAAA;cAErC,IAAA,EAAM,WAAA,EAAa,OAAA,UAAiB,OAAA,GAAU,cAAA;AAAA"}
package/dist/errors.js ADDED
@@ -0,0 +1,18 @@
1
+ //#region src/errors.ts
2
+ /** An AI error carrying a stable {@link AiErrorCode}. */
3
+ var AiError = class extends Error {
4
+ /** Stable, machine-readable cause. */
5
+ code;
6
+ /** Validation issues, when the failure came from schema validation. */
7
+ issues;
8
+ constructor(code, message, options) {
9
+ super(message);
10
+ this.name = "AiError";
11
+ this.code = code;
12
+ if (options?.issues) this.issues = options.issues;
13
+ }
14
+ };
15
+ //#endregion
16
+ export { AiError };
17
+
18
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["/**\n * Errors for `@mindees/ai` (Synapse). Every failure carries a stable\n * {@link AiErrorCode} so callers can branch on the cause without string-matching.\n *\n * @module\n */\n\nimport type { StandardSchemaV1 } from './standard-schema'\n\n/** Stable code identifying why an AI operation failed. */\nexport type AiErrorCode =\n /** No transport/fetch was provided to a backend that needs one. */\n | 'NO_TRANSPORT'\n /** The server returned a non-2xx status. */\n | 'HTTP_STATUS'\n /** A streaming response could not be parsed. */\n | 'STREAM_PARSE'\n /** Structured output failed schema validation (after repair attempts). */\n | 'INVALID_OBJECT'\n /** A tool-calling loop exceeded its step ceiling. */\n | 'MAX_STEPS'\n /** A tool handler threw. */\n | 'TOOL_FAILED'\n /** The request was aborted. */\n | 'ABORTED'\n /** An on-device / research-track capability is not implemented. */\n | 'NOT_IMPLEMENTED'\n\n/** Optional structured detail attached to an {@link AiError}. */\nexport interface AiErrorOptions {\n /** Standard Schema issues โ€” set on `INVALID_OBJECT` from schema validation. */\n readonly issues?: ReadonlyArray<StandardSchemaV1.Issue>\n}\n\n/** An AI error carrying a stable {@link AiErrorCode}. */\nexport class AiError extends Error {\n /** Stable, machine-readable cause. */\n readonly code: AiErrorCode\n /** Validation issues, when the failure came from schema validation. */\n readonly issues?: ReadonlyArray<StandardSchemaV1.Issue>\n\n constructor(code: AiErrorCode, message: string, options?: AiErrorOptions) {\n super(message)\n this.name = 'AiError'\n this.code = code\n // Set only when present (exactOptionalPropertyTypes-safe).\n if (options?.issues) this.issues = options.issues\n }\n}\n"],"mappings":";;AAmCA,IAAa,UAAb,cAA6B,MAAM;;CAEjC;;CAEA;CAEA,YAAY,MAAmB,SAAiB,SAA0B;EACxE,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,OAAO;EAEZ,IAAI,SAAS,QAAQ,KAAK,SAAS,QAAQ;CAC7C;AACF"}
@@ -0,0 +1,26 @@
1
+ import { AbortLike, Ai, AiBackend, AiChunk, AiResult, FinishReason, GenerateRequest, Message, Part, Role, TextPart, ToolCallPart, ToolDefinition, ToolResultPart, Usage, createAi, messageText } from "./contract.js";
2
+ import { StandardSchemaV1 } from "./standard-schema.js";
3
+ import { AiError, AiErrorCode, AiErrorOptions } from "./errors.js";
4
+ import { DEFAULT_MAX_INPUT_CHARS, ExtractResult, SanitizeLimits, ValidationOutcome, containsForbiddenKey, extractJson, formatIssues, lenientParseJson, sanitizeJson, validateStandard } from "./json.js";
5
+ import { MockBackendOptions, MockReply, MockResponse, createMockBackend } from "./mock.js";
6
+ import { GenerateObjectOptions, GenerateObjectResult, GeneratingBackend, StreamObjectChunk, StreamObjectOptions, StreamingBackend, generateObject, streamObject } from "./object.js";
7
+ import { createOnDeviceBackend } from "./on-device.js";
8
+ import { RunToolsOptions, RunToolsResult, Tool, ToolContext, runTools } from "./tools.js";
9
+ import { Maturity, NotImplementedError, PackageInfo, notImplemented } from "@mindees/core";
10
+
11
+ //#region src/index.d.ts
12
+ /** The npm package name. */
13
+ declare const name = "@mindees/ai";
14
+ /** The package version. All `@mindees/*` packages share one locked version line. */
15
+ declare const VERSION = "0.1.0";
16
+ /** Current maturity of this package. See the repository `STATUS.md`. */
17
+ declare const maturity: Maturity;
18
+ /**
19
+ * Static identity + maturity metadata for this package. Frozen so the
20
+ * self-reported identity tooling introspects cannot be mutated at runtime,
21
+ * matching the `readonly` fields of {@link PackageInfo}.
22
+ */
23
+ declare const info: PackageInfo;
24
+ //#endregion
25
+ export { type AbortLike, type Ai, type AiBackend, type AiChunk, AiError, type AiErrorCode, type AiErrorOptions, type AiResult, DEFAULT_MAX_INPUT_CHARS, type ExtractResult, type FinishReason, type GenerateObjectOptions, type GenerateObjectResult, type GenerateRequest, type GeneratingBackend, type Maturity, type Message, type MockBackendOptions, type MockReply, type MockResponse, NotImplementedError, type PackageInfo, type Part, type Role, type RunToolsOptions, type RunToolsResult, type SanitizeLimits, type StandardSchemaV1, type StreamObjectChunk, type StreamObjectOptions, type StreamingBackend, type TextPart, type Tool, type ToolCallPart, type ToolContext, type ToolDefinition, type ToolResultPart, type Usage, VERSION, type ValidationOutcome, containsForbiddenKey, createAi, createMockBackend, createOnDeviceBackend, extractJson, formatIssues, generateObject, info, lenientParseJson, maturity, messageText, name, notImplemented, runTools, sanitizeJson, streamObject, validateStandard };
26
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;;;;;;;cAoBa,IAAA;;cAGA,OAAA;AAGb;AAAA,cAAa,QAAA,EAAU,QAAyB;;;AAAA;AAOhD;;cAAa,IAAA,EAAM,WAAiE"}
package/dist/index.js ADDED
@@ -0,0 +1,29 @@
1
+ import { createAi, messageText } from "./contract.js";
2
+ import { AiError } from "./errors.js";
3
+ import { DEFAULT_MAX_INPUT_CHARS, containsForbiddenKey, extractJson, formatIssues, lenientParseJson, sanitizeJson, validateStandard } from "./json.js";
4
+ import { createMockBackend } from "./mock.js";
5
+ import { generateObject, streamObject } from "./object.js";
6
+ import { createOnDeviceBackend } from "./on-device.js";
7
+ import { runTools } from "./tools.js";
8
+ import { NotImplementedError, notImplemented } from "@mindees/core";
9
+ //#region src/index.ts
10
+ /** The npm package name. */
11
+ const name = "@mindees/ai";
12
+ /** The package version. All `@mindees/*` packages share one locked version line. */
13
+ const VERSION = "0.1.0";
14
+ /** Current maturity of this package. See the repository `STATUS.md`. */
15
+ const maturity = "experimental";
16
+ /**
17
+ * Static identity + maturity metadata for this package. Frozen so the
18
+ * self-reported identity tooling introspects cannot be mutated at runtime,
19
+ * matching the `readonly` fields of {@link PackageInfo}.
20
+ */
21
+ const info = Object.freeze({
22
+ name,
23
+ version: VERSION,
24
+ maturity
25
+ });
26
+ //#endregion
27
+ export { AiError, DEFAULT_MAX_INPUT_CHARS, NotImplementedError, VERSION, containsForbiddenKey, createAi, createMockBackend, createOnDeviceBackend, extractJson, formatIssues, generateObject, info, lenientParseJson, maturity, messageText, name, notImplemented, runTools, sanitizeJson, streamObject, validateStandard };
28
+
29
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * `@mindees/ai` (Synapse) โ€” provider-agnostic AI + dev-time intelligence.\n *\n * Phase 11 ships the **contract** ({@link createAi}, {@link AiBackend}, messages,\n * {@link GenerateRequest}/{@link AiResult}/{@link AiChunk}, {@link AiError}) with\n * streaming as `AsyncIterable` only (Node/browser/Hermes-safe), a deterministic\n * {@link createMockBackend mock backend} (the working, offline, no-keys fallback),\n * Standard-Schema structured output, bounded tool calling, an inject-`fetch` server\n * backend on the `@mindees/ai/server` subpath, and a dev-time error explainer on the\n * `@mindees/ai/devtools` subpath. The {@link createOnDeviceBackend on-device seam}\n * throws because on-device LLM inference is inherently native and stays a ๐Ÿ”ฌ research\n * track.\n *\n * @module\n */\n\nimport type { Maturity, PackageInfo } from '@mindees/core'\nimport { NotImplementedError, notImplemented } from '@mindees/core'\n\n/** The npm package name. */\nexport const name = '@mindees/ai'\n\n/** The package version. All `@mindees/*` packages share one locked version line. */\nexport const VERSION = '0.1.0'\n\n/** Current maturity of this package. See the repository `STATUS.md`. */\nexport const maturity: Maturity = 'experimental'\n\n/**\n * Static identity + maturity metadata for this package. Frozen so the\n * self-reported identity tooling introspects cannot be mutated at runtime,\n * matching the `readonly` fields of {@link PackageInfo}.\n */\nexport const info: PackageInfo = Object.freeze({ name, version: VERSION, maturity })\n\nexport {\n type AbortLike,\n type Ai,\n type AiBackend,\n type AiChunk,\n type AiResult,\n createAi,\n type FinishReason,\n type GenerateRequest,\n type Message,\n messageText,\n type Part,\n type Role,\n type TextPart,\n type ToolCallPart,\n type ToolDefinition,\n type ToolResultPart,\n type Usage,\n} from './contract'\nexport { AiError, type AiErrorCode, type AiErrorOptions } from './errors'\nexport {\n containsForbiddenKey,\n DEFAULT_MAX_INPUT_CHARS,\n type ExtractResult,\n extractJson,\n formatIssues,\n lenientParseJson,\n type SanitizeLimits,\n sanitizeJson,\n type ValidationOutcome,\n validateStandard,\n} from './json'\nexport {\n createMockBackend,\n type MockBackendOptions,\n type MockReply,\n type MockResponse,\n} from './mock'\nexport {\n type GenerateObjectOptions,\n type GenerateObjectResult,\n type GeneratingBackend,\n generateObject,\n type StreamingBackend,\n type StreamObjectChunk,\n type StreamObjectOptions,\n streamObject,\n} from './object'\nexport { createOnDeviceBackend } from './on-device'\nexport type { StandardSchemaV1 } from './standard-schema'\nexport {\n type RunToolsOptions,\n type RunToolsResult,\n runTools,\n type Tool,\n type ToolContext,\n} from './tools'\n\nexport type { Maturity, PackageInfo }\nexport { NotImplementedError, notImplemented }\n"],"mappings":";;;;;;;;;;AAoBA,MAAa,OAAO;;AAGpB,MAAa,UAAU;;AAGvB,MAAa,WAAqB;;;;;;AAOlC,MAAa,OAAoB,OAAO,OAAO;CAAE;CAAM,SAAS;CAAS;AAAS,CAAC"}
package/dist/json.d.ts ADDED
@@ -0,0 +1,76 @@
1
+ import { StandardSchemaV1 } from "./standard-schema.js";
2
+
3
+ //#region src/json.d.ts
4
+ /**
5
+ * Max characters of untrusted model text handed to `JSON.parse`. A size gate enforced BEFORE
6
+ * parsing (mirrors the SSE backend's `MAX_SSE_BUFFER`) so a hostile payload can't be fully
7
+ * allocated before the post-parse limits in {@link sanitizeJson} would reject it. ~8M chars.
8
+ */
9
+ declare const DEFAULT_MAX_INPUT_CHARS: number;
10
+ /** Limits that bound a sanitized value so hostile model JSON can't exhaust memory. */
11
+ interface SanitizeLimits {
12
+ /** Max nesting depth. */
13
+ readonly maxDepth: number;
14
+ /** Max total nodes (objects + arrays + primitives) across the whole value. */
15
+ readonly maxNodes: number;
16
+ /** Max length of any single string. */
17
+ readonly maxStringLength: number;
18
+ /** Max own keys on any single object. */
19
+ readonly maxProps: number;
20
+ }
21
+ /** The outcome of {@link extractJson}. */
22
+ type ExtractResult = {
23
+ readonly ok: true;
24
+ readonly value: unknown;
25
+ } | {
26
+ readonly ok: false;
27
+ readonly reason: string;
28
+ };
29
+ /**
30
+ * Extract a JSON value from model text, trying in a fixed, testable order:
31
+ * 1. parse the whole (trimmed) text; 2. parse the first fenced code block; 3. parse the
32
+ * first balanced brace/bracket span. Never uses a capturing regex or `eval`.
33
+ */
34
+ declare function extractJson(text: string, maxChars?: number): ExtractResult;
35
+ /**
36
+ * Best-effort parse of *incomplete* JSON for streaming previews: closes any open string and
37
+ * unbalanced brackets, drops a dangling `,`/`:`, and parses. Returns `undefined` whenever it
38
+ * can't safely produce a value (never throws, never guesses a value token). The result is
39
+ * **unvalidated and unsanitized** โ€” callers must treat it as an untyped preview only.
40
+ */
41
+ declare function lenientParseJson(text: string, maxChars?: number): unknown;
42
+ /**
43
+ * Deep-clone untrusted parsed JSON into a fresh value, throwing `AiError('INVALID_OBJECT')`
44
+ * on any prototype-pollution key (`__proto__`/`constructor`/`prototype`) at any depth or on
45
+ * any limit breach. Run this BEFORE handing a value to a Standard Schema validator โ€” a
46
+ * validator can itself pollute the prototype by touching a poisoned object.
47
+ */
48
+ declare function sanitizeJson(value: unknown, limits?: SanitizeLimits): unknown;
49
+ /**
50
+ * Cheap recursive check for any prototype-pollution own key, WITHOUT cloning. Used to skip
51
+ * emitting an unsanitized streaming preview that carries a poison key (so a consumer who
52
+ * naively deep-merges the preview can't be tricked into polluting the prototype). Depth-capped
53
+ * and fail-closed: a value nested past `maxDepth` is treated as forbidden (skip the preview)
54
+ * rather than risking a stack overflow on a deeply-nested untrusted payload.
55
+ */
56
+ declare function containsForbiddenKey(value: unknown, maxDepth?: number, depth?: number): boolean;
57
+ /** Render Standard Schema issues into one readable line: `path: message; path2: message2`. */
58
+ declare function formatIssues(issues: ReadonlyArray<StandardSchemaV1.Issue>): string;
59
+ /** A normalized validation outcome (sync โ€” the Promise has already been awaited). */
60
+ type ValidationOutcome<T> = {
61
+ readonly ok: true;
62
+ readonly value: T;
63
+ } | {
64
+ readonly ok: false;
65
+ readonly issues: ReadonlyArray<StandardSchemaV1.Issue>;
66
+ };
67
+ /**
68
+ * Validate a (already-sanitized) value through a Standard Schema, awaiting an async validator.
69
+ * Defensively narrows a malformed validator result (non-array `issues`) into a failure rather
70
+ * than crashing. Discriminates on `issues` truthiness โ€” a valid output may legitimately be
71
+ * `undefined`, so `value` is not a reliable discriminant.
72
+ */
73
+ declare function validateStandard<S extends StandardSchemaV1>(schema: S, value: unknown): Promise<ValidationOutcome<StandardSchemaV1.InferOutput<S>>>;
74
+ //#endregion
75
+ export { DEFAULT_MAX_INPUT_CHARS, ExtractResult, SanitizeLimits, ValidationOutcome, containsForbiddenKey, extractJson, formatIssues, lenientParseJson, sanitizeJson, validateStandard };
76
+ //# sourceMappingURL=json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.d.ts","names":[],"sources":["../src/json.ts"],"mappings":";;;;;;;;cAqBa,uBAAA;;UAGI,cAAA;EAwBL;EAAA,SAtBD,QAAA;;WAEA,QAAA;EAqBI;EAAA,SAnBJ,eAAA;EAqBI;EAAA,SAnBJ,QAAA;AAAA;;KAgBC,aAAA;EAAA,SACG,EAAA;EAAA,SAAmB,KAAA;AAAA;EAAA,SAEnB,EAAA;EAAA,SACA,MAAA;AAAA;AAsGf;;;;AAE4C;AAF5C,iBAhCgB,WAAA,CACd,IAAA,UACA,QAAA,YACC,aAAa;;;;;;;iBA6BA,gBAAA,CACd,IAAA,UACA,QAA0C;AAyDM;AAgDlD;;;;;AAhDkD,iBAFlC,YAAA,CACd,KAAA,WACA,MAAA,GAAQ,cAAwC;;;AAmDvC;AAcX;;;;iBAjBgB,oBAAA,CACd,KAAA,WACA,QAAA,WACA,KAAA;;iBAcc,YAAA,CAAa,MAAA,EAAQ,aAAa,CAAC,gBAAA,CAAiB,KAAA;;KAiBxD,iBAAA;EAAA,SACG,EAAA;EAAA,SAAmB,KAAA,EAAO,CAAA;AAAA;EAAA,SAC1B,EAAA;EAAA,SAAoB,MAAA,EAAQ,aAAA,CAAc,gBAAA,CAAiB,KAAA;AAAA;;;;;;;iBAQpD,gBAAA,WAA2B,gBAAA,EAC/C,MAAA,EAAQ,CAAA,EACR,KAAA,YACC,OAAA,CAAQ,iBAAA,CAAkB,gBAAA,CAAiB,WAAA,CAAY,CAAA"}