@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.
- package/LICENSE +31 -0
- package/README.md +57 -0
- package/dist/contract.d.ts +113 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +18 -0
- package/dist/contract.js.map +1 -0
- package/dist/devtools.d.ts +43 -0
- package/dist/devtools.d.ts.map +1 -0
- package/dist/devtools.js +77 -0
- package/dist/devtools.js.map +1 -0
- package/dist/errors.d.ts +21 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +18 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/json.d.ts +76 -0
- package/dist/json.d.ts.map +1 -0
- package/dist/json.js +256 -0
- package/dist/json.js.map +1 -0
- package/dist/mappers.d.ts +52 -0
- package/dist/mappers.d.ts.map +1 -0
- package/dist/mappers.js +312 -0
- package/dist/mappers.js.map +1 -0
- package/dist/mock.d.ts +26 -0
- package/dist/mock.d.ts.map +1 -0
- package/dist/mock.js +69 -0
- package/dist/mock.js.map +1 -0
- package/dist/object.d.ts +78 -0
- package/dist/object.d.ts.map +1 -0
- package/dist/object.js +140 -0
- package/dist/object.js.map +1 -0
- package/dist/on-device.d.ts +13 -0
- package/dist/on-device.d.ts.map +1 -0
- package/dist/on-device.js +33 -0
- package/dist/on-device.js.map +1 -0
- package/dist/server.d.ts +42 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +64 -0
- package/dist/server.js.map +1 -0
- package/dist/sse.d.ts +24 -0
- package/dist/sse.d.ts.map +1 -0
- package/dist/sse.js +81 -0
- package/dist/sse.js.map +1 -0
- package/dist/standard-schema.d.ts +89 -0
- package/dist/standard-schema.d.ts.map +1 -0
- package/dist/tools.d.ts +61 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +195 -0
- package/dist/tools.js.map +1 -0
- 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"}
|
package/dist/contract.js
ADDED
|
@@ -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"}
|
package/dist/devtools.js
ADDED
|
@@ -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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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"}
|