@jxsuite/ai 0.36.4 → 0.36.5

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 (2) hide show
  1. package/README.md +35 -35
  2. package/package.json +4 -4
package/README.md CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  The provider-agnostic substrate Jx Studio's assistant is built on: a streaming LLM client
4
4
  abstraction that normalizes provider SSE into six event shapes, a tool registry that speaks OpenAI
5
- function-calling, and a reactive chat store. It has no Jx-domain dependencies — only a type-only
6
- import of `ProblemDetails` from `@jxsuite/protocol` and `@vue/reactivity` — and its only platform
5
+ function-calling, and a reactive chat store. It has no Jx-domain dependencies (just a type-only
6
+ import of `ProblemDetails` from `@jxsuite/protocol`, plus `@vue/reactivity`), and its only platform
7
7
  APIs are `fetch`, `TextDecoder` and `AbortSignal`, so the same modules run in the browser and in
8
8
  Bun/Node.
9
9
 
@@ -17,8 +17,8 @@ documentation for the Studio feature lives at [`docs/studio/ai.md`](../../docs/s
17
17
  | `@jxsuite/ai/tools` | `createToolDefinition`, `createToolRegistry`, `toolSuccess`, `toolError`, and their types |
18
18
  | `@jxsuite/ai/chat-state` | `createChatState`, `ChatStore`, `Message`, `ToolCallRecord`, `ChatState`, `MessageRole` |
19
19
 
20
- `toolSuccess` / `toolError` are **not** re-exported by the barrel — import them from
21
- `@jxsuite/ai/tools`. Importing the subpaths rather than the root is also what makes the package
20
+ `toolSuccess` / `toolError` are **not** re-exported by the barrel. Import them from
21
+ `@jxsuite/ai/tools`. Importing the subpaths instead of the root is also what makes the package
22
22
  tree-shakeable.
23
23
 
24
24
  ## The streaming client
@@ -30,13 +30,13 @@ streamChat(messages: object[], tools: object[], systemPrompt: string, signal: Ab
30
30
  : AsyncGenerator<StreamEvent>;
31
31
  ```
32
32
 
33
- Every implementation yields the same discriminated union — `delta {content}`,
34
- `tool_call_start {id, name}`, `tool_call_delta {id, args}`, `tool_call_end {id}`,
35
- `done {stopReason}`, `error {message, code?, problem?}` — whose type strings are also published as
36
- `STREAM_EVENT_TYPES`. This union is the contract a third-party Studio backend must satisfy for the
37
- `ai/chat` route (see [`packages/protocol/README.md`](../protocol/README.md)); `@jxsuite/server`
38
- emits the same format without depending on this package. `StreamEvent` and each member are
39
- exported, so an implementer can import the type rather than reconstruct it.
33
+ Every implementation yields the same discriminated union, whose type strings are also published as
34
+ `STREAM_EVENT_TYPES`: `delta {content}`, `tool_call_start {id, name}`, `tool_call_delta {id, args}`,
35
+ `tool_call_end {id}`, `done {stopReason}`, `error {message, code?, problem?}`. This union is the
36
+ contract a third-party Studio backend must satisfy for the `ai/chat` route (see
37
+ [`packages/protocol/README.md`](../protocol/README.md)); `@jxsuite/server` emits the same format
38
+ without depending on this package. `StreamEvent` and each member are exported, so an implementer can
39
+ import the type instead of reconstructing it.
40
40
 
41
41
  ```ts
42
42
  import { createProxyStreamingClient } from "@jxsuite/ai";
@@ -57,7 +57,7 @@ for await (const event of client.streamChat(messages, tools, systemPrompt, signa
57
57
  `stream_options: { include_usage: true }`, and `{ role: "system", content: systemPrompt }`
58
58
  prepended to your messages. A non-empty `tools` array also sets `tool_choice: "auto"` and
59
59
  `parallel_tool_calls: true`; an empty one sends no `tools` key. `temperature` is forwarded **only
60
- when defined**, because reasoning models (GPT-5.x, o-series) reject a custom temperature — a
60
+ when defined**, because reasoning models (GPT-5.x, o-series) reject a custom temperature. That is a
61
61
  provider behaviour recorded in the source comment, not covered by a test here. It does
62
62
  its own SSE framing and reassembles fragmented tool-call argument JSON keyed by the provider's
63
63
  `delta.tool_calls[].index`.
@@ -66,7 +66,7 @@ for await (const event of client.streamChat(messages, tools, systemPrompt, signa
66
66
  ships (`specs/ai.md` §2).
67
67
  - **`createProxyStreamingClient({ chatUrl, model = "gpt-4o", apiKey, baseUrl })`** POSTs
68
68
  `{ messages, tools, systemPrompt, model }` to an endpoint that already speaks the normalized
69
- format — the dev/desktop server's `/__studio/ai/chat`. It handles no credentials of its own;
69
+ format (the dev/desktop server's `/__studio/ai/chat`). It handles no credentials of its own;
70
70
  `apiKey` and `baseUrl` are passed through as the `X-Api-Key` and `X-Api-Base-URL` headers only
71
71
  when truthy, and the proxy owns the provider key. It is a pass-through, **not** a translator: it
72
72
  re-yields whatever parses out of each `data:` line and returns at the first `done` or `error`, so
@@ -85,8 +85,8 @@ Three behaviours are easy to get wrong when consuming the stream:
85
85
  `done` frame ends its generator with no `done` at all.
86
86
  3. **`error` frames may carry an RFC 9457 `problem` beside the human `message`,** because the
87
87
  response has already begun with a 200 and the status can no longer change (a committed standards
88
- row in `specs/ai.md` §5). The field is optional and no client in this package sets it —
89
- `@jxsuite/server`'s proxy is what populates it and `createProxyStreamingClient` passes it
88
+ row in `specs/ai.md` §5). The field is optional and no client in this package sets it.
89
+ `@jxsuite/server`'s proxy is what populates it, and `createProxyStreamingClient` passes it
90
90
  through. Read `problem.type` for machine dispatch when it is present; keep showing `message`.
91
91
 
92
92
  ## The tool registry
@@ -117,38 +117,38 @@ registry.register(
117
117
  registry's own argument validation. `llmStrict` (default `false`) is the only one that reaches the
118
118
  wire, adding OpenAI's `strict: true` to the emitted function schema. OpenAI strict mode demands
119
119
  `additionalProperties: false` and every property in `required`, which Jx's own tools deliberately
120
- violate — GPT-5.x rejects such a request outright (again, committed rationale rather than a test).
120
+ violate, so GPT-5.x rejects such a request outright (again, committed rationale and no test).
121
121
  - **`validate()` is a lightweight structural check, not a JSON Schema validator.** It reads only
122
122
  `type`, `properties` and `required`: nested schemas, `enum`, `pattern`, `minimum` and every other
123
123
  keyword are ignored. Unknown properties are ignored too (the model may send extra); a required key
124
124
  that is present but `null`/`undefined` counts as missing; numeric strings coerce, so `"42"`
125
125
  validates against `number`. `strict: false`, or a schema without `properties`, skips it entirely.
126
126
  - **`execute()` never throws.** An unknown tool, a validation failure, and an exception inside the
127
- tool all return `{ success: false, error }` — which is what lets an agent loop feed the failure
127
+ tool all return `{ success: false, error }`, which is what lets an agent loop feed the failure
128
128
  back to the model and self-correct. It calls `this.validate(...)`, so it is `this`-bound: do not
129
129
  destructure it off the registry.
130
130
  - Re-registering a name `console.warn`s and overwrites.
131
131
 
132
- To vary the tool set per turn, wrap rather than mutate — Studio's `createGatedToolRegistry` returns
132
+ To vary the tool set per turn, wrap rather than mutate. Studio's `createGatedToolRegistry` returns
133
133
  a `ToolRegistry` that filters `list()`/`listForLLM()` through per-tool predicates
134
134
  (`packages/studio/src/services/gated-registry.ts`).
135
135
 
136
136
  ## The reactive chat store
137
137
 
138
- `createChatState(options?: { model?: string })` returns a `@vue/reactivity` `reactive()` store —
139
- `messages`, `status`, `streamingContent`, `pendingToolCalls`, `error`, `model`, `tokenCount`,
140
- `contextWarning` — with its mutators `Object.assign`ed onto the same proxy, so the returned value is
141
- both the state and the API. The model falls back to `"gpt-4o"` via `||`, so an empty string takes
142
- the default too. `status` is `"idle" | "streaming" | "error"`; `toMessagesArray()`
143
- serializes back to OpenAI wire shape (assistant `tool_calls`, `tool` messages with `tool_call_id`,
144
- everything else `{ role, content }`).
138
+ `createChatState(options?: { model?: string })` returns a `@vue/reactivity` `reactive()` store with
139
+ its mutators `Object.assign`ed onto the same proxy, so the returned value is both the state and the
140
+ API. The store holds `messages`, `status`, `streamingContent`, `pendingToolCalls`, `error`, `model`,
141
+ `tokenCount` and `contextWarning`. The model falls back to `"gpt-4o"` via `||`, so an empty string
142
+ takes the default too. `status` is `"idle" | "streaming" | "error"`; `toMessagesArray()` serializes
143
+ back to OpenAI wire shape (assistant `tool_calls`, `tool` messages with `tool_call_id`, everything
144
+ else `{ role, content }`).
145
145
 
146
146
  Two ordering rules inside `beginAssistantTurn` are load-bearing, and any reimplementation must keep
147
147
  them: `status` is set to `"streaming"` **before** the placeholder message is pushed (the push itself
148
148
  triggers effects, and a reader seeing `"idle"` renders the empty placeholder as a finished message),
149
- and the pushed object is immediately re-read back through the proxy (`store.messages.at(-1)`) —
150
- mutating the raw object would notify nothing. A regression test asserts that an `effect()` reading
151
- `messages.at(-1).content` re-runs on every `appendDelta`.
149
+ and the pushed object is immediately re-read back through the proxy (`store.messages.at(-1)`),
150
+ because mutating the raw object would notify nothing. A regression test asserts that an `effect()`
151
+ reading `messages.at(-1).content` re-runs on every `appendDelta`.
152
152
 
153
153
  Further surprises:
154
154
 
@@ -168,10 +168,10 @@ Further surprises:
168
168
 
169
169
  ## Versioning
170
170
 
171
- Published to npm as `@jxsuite/ai` — TypeScript source, like every published `@jxsuite` library,
172
- following the monorepo's release train; every `exports` subpath resolves to `./src/*.ts`, so a
173
- consumer must be
174
- able to compile TypeScript out of `node_modules`. Within the monorepo, `@jxsuite/studio` depends on
175
- it via `workspace:^` and is its only consumer. `@vue/reactivity` is pinned to an **exact** version,
176
- matching `@jxsuite/studio`, `@jxsuite/runtime` and `@jxsuite/compiler`: reactive proxies from one
177
- copy do not track in effects from another, and the failure is silent.
171
+ Published to npm as `@jxsuite/ai`, following the monorepo's release train. Like every published
172
+ `@jxsuite` library, it ships TypeScript source: every `exports` subpath resolves to `./src/*.ts`, so
173
+ a consumer must be able to compile TypeScript out of `node_modules`. Within the monorepo,
174
+ `@jxsuite/studio` depends on it via `workspace:^` and is its only consumer. `@vue/reactivity` is
175
+ pinned to an **exact** version, matching `@jxsuite/studio`, `@jxsuite/runtime` and
176
+ `@jxsuite/compiler`: reactive proxies from one copy do not track in effects from another, and the
177
+ failure is silent.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jxsuite/ai",
3
- "version": "0.36.4",
3
+ "version": "0.36.5",
4
4
  "description": "AI infrastructure for Jx Suite — streaming LLM client abstraction, tool registry, and reactive chat state",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -25,13 +25,13 @@
25
25
  "scripts": {
26
26
  "build": "bun build ./src/index.ts --outdir dist --target browser --sourcemap=linked",
27
27
  "test": "bun test",
28
- "upgrade": "bunx npm-check-updates -u && bun install"
28
+ "upgrade": "bun update --latest && bun install"
29
29
  },
30
30
  "dependencies": {
31
- "@jxsuite/protocol": "^2.0.0",
31
+ "@jxsuite/protocol": "^2.1.0",
32
32
  "@vue/reactivity": "3.5.41"
33
33
  },
34
34
  "devDependencies": {
35
- "@happy-dom/global-registrator": "^20.11.6"
35
+ "@happy-dom/global-registrator": "^20.11.8"
36
36
  }
37
37
  }