sygnal 6.2.0 → 6.3.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 (76) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/README.md +3 -1
  3. package/dist/ai.cjs.js +36 -0
  4. package/dist/ai.cjs.js.map +1 -1
  5. package/dist/ai.esm.js +1 -1
  6. package/dist/astro/server.cjs.js +3 -3
  7. package/dist/astro/server.cjs.js.map +1 -1
  8. package/dist/astro/server.mjs +3 -3
  9. package/dist/astro/server.mjs.map +1 -1
  10. package/dist/diagnostics.cjs.js +164 -3
  11. package/dist/diagnostics.cjs.js.map +1 -1
  12. package/dist/diagnostics.esm.js +164 -3
  13. package/dist/diagnostics.esm.js.map +1 -1
  14. package/dist/guide/agent.md +7 -1
  15. package/dist/guide/ai-cards.md +319 -0
  16. package/dist/guide/ai-chat.md +4 -0
  17. package/dist/guide/ai-compose.md +881 -0
  18. package/dist/guide/ai-decisions.md +16 -0
  19. package/dist/guide/http.md +1 -0
  20. package/dist/index.cjs.js +5695 -347
  21. package/dist/index.cjs.js.map +1 -1
  22. package/dist/index.d.ts +638 -8
  23. package/dist/index.esm.js +5685 -347
  24. package/dist/index.esm.js.map +1 -1
  25. package/dist/sygnal.min.js +1 -1
  26. package/dist/sygnal.min.js.map +1 -1
  27. package/dist/vike/config/package.json +1 -1
  28. package/dist/vike/onRenderClient.cjs.js +3 -2
  29. package/dist/vike/onRenderClient.cjs.js.map +1 -1
  30. package/dist/vike/onRenderClient.mjs +3 -2
  31. package/dist/vike/onRenderClient.mjs.map +1 -1
  32. package/llms.txt +6 -4
  33. package/package.json +2 -1
  34. package/src/ai.d.ts +642 -7
  35. package/src/ai.ts +6 -0
  36. package/src/collection.ts +2 -1
  37. package/src/core/instance.ts +6 -3
  38. package/src/core/markers/lazy.ts +7 -3
  39. package/src/core/markers/portal.ts +5 -2
  40. package/src/core/markers/suspense.ts +3 -2
  41. package/src/core/markers/transition.ts +6 -3
  42. package/src/core/registry.ts +9 -5
  43. package/src/cycle/dom/IsolateModule.ts +25 -22
  44. package/src/extra/ai/chat/behavior.ts +354 -81
  45. package/src/extra/ai/chat/driver.ts +7 -4
  46. package/src/extra/ai/compose/agent.ts +55 -0
  47. package/src/extra/ai/compose/ask.ts +147 -0
  48. package/src/extra/ai/compose/behavior.ts +183 -0
  49. package/src/extra/ai/compose/catalog.ts +211 -0
  50. package/src/extra/ai/compose/composed.ts +61 -0
  51. package/src/extra/ai/compose/decisions.ts +1649 -0
  52. package/src/extra/ai/compose/diag.ts +33 -0
  53. package/src/extra/ai/compose/index.ts +23 -0
  54. package/src/extra/ai/compose/layout.ts +441 -0
  55. package/src/extra/ai/compose/llm.ts +1120 -0
  56. package/src/extra/ai/compose/render.ts +159 -0
  57. package/src/extra/ai/compose/testing.ts +71 -0
  58. package/src/extra/ai/compose/types.ts +159 -0
  59. package/src/extra/ai/decide.ts +97 -1
  60. package/src/extra/ai/index.ts +6 -1
  61. package/src/extra/diagnostics/checks/next.ts +86 -2
  62. package/src/extra/diagnostics/codes.ts +54 -0
  63. package/src/extra/fetchDriver.ts +58 -27
  64. package/src/extra/lens.ts +15 -0
  65. package/src/extra/ssr.ts +3 -3
  66. package/src/extra/testing.ts +2 -0
  67. package/src/extra/widget.ts +3 -3
  68. package/src/index.d.ts +41 -0
  69. package/src/index.ts +8 -0
  70. package/src/lazy.ts +3 -2
  71. package/src/portal.ts +5 -2
  72. package/src/pragma/index.ts +2 -1
  73. package/src/suspense.ts +5 -2
  74. package/src/switchable.ts +2 -1
  75. package/src/transition.ts +5 -2
  76. package/src/vike/onRenderClient.ts +3 -2
package/CHANGELOG.md CHANGED
@@ -2,6 +2,67 @@
2
2
 
3
3
  All notable changes to Sygnal are listed here. Versions follow [semantic versioning](https://semver.org). Releases before 5.4.0 are described in the [GitHub releases](https://github.com/tpresley/sygnal/releases) and tags.
4
4
 
5
+ ## 6.3.0 — 2026-10-11
6
+
7
+ Composing UI with a model (PLAN-9). An app declares its parts once in a catalog, the user describes a page or an edit in words, and a model builds it from those parts: a decision model answering multiple-choice questions (TypeSafe Jev, Ollama's `nimble`) or an LLM with structured output, with the same catalog, app code and tests for both. The composition is plain JSON in state, each part is an ordinary child component, and follow-up requests edit the page while keeping the state of the parts they don't touch. The same catalog lets a chat assistant ask the user with a card instead of in text (`chat({ ask })`), on top of client tools for the `chat` behavior. Around it: `decide()` splits large question sets, `byId()` lenses, and a component's `key` is now its identity.
8
+
9
+ Everything is additive, and `sygnal/ai` stays tree-shaken from apps that don't import it. The lowercase marker tags (`<collection>`, `<switchable>`, ...) are deprecated (they keep working in 6.x; `sygnal-check --fix` migrates them).
10
+
11
+ **Measured impact.**
12
+ - Live accuracy (E-3, `npm run test:compose-live`, every PLAN-9 gate passing): Jev structure 61–65/70, follow-up edits 22/22 tuned and 15/16 held out, keys 100%, about 0.35 s per call; `qwen3:8b` with a schema 11/11 flat dashboards and 15/15 dashboards, 0 invalid; `nimble` with `profile: 'small'` structure 29/70 with 0 invalid layouts (was 5 before the `decide()` split, G-757).
13
+ - Size: the kanban gate went down, (a) 42,504 B with `nativeGlobalThis: false` (was 42,690 B; budget 42,700 B) and (b) 38,485 B by default (was 38,679 B): the lazy marker handlers (G-751) save 223 B, `key` identity and the DOM move fixes add 28 B. `sygnal/ai` is tree-shaken from apps that don't import it; `<Composed>` adds about 0.5 KB gzipped over `catalog.render()` when used, and the fetch driver's `batch` about 0.3 KB.
14
+
15
+ **Companion packages.**
16
+ - **`sygnal-check` 0.6.0:** catalog rules (SYG161, SYG162, SYG250, SYG450 and SYG680 where a literal `defineCatalog({...})` shows them; new SYG253 misspelled declaration keys with a rename fix, SYG254 description lint), the `chat` / `compose` behaviors' keys and actions, and SYG426 for the deprecated lowercase marker tags with `--fix`. Its caret range doesn't reach 0.6.0 from `^0.5.0`, so update the dev dependency.
17
+ - **`create-sygnal-app` 2.4.0:** every template depends on `sygnal` ^6.3.0 and `sygnal-check` ^0.6.0.
18
+ - **VS Code extension 0.3.0:** bundles `sygnal-check` 0.6.0 (the new rules and quick fixes in the editor). The extension itself doesn't change.
19
+ - **Claude Code plugin 0.1.2:** the updated `sygnal-dev` skill (composed UI and chat cards, `key` identity).
20
+
21
+ ### Added
22
+
23
+ - **Large question sets in `decide()`.** A set over `maxQuestions` (default 64, Ollama's limit) or an estimated `maxPromptTokens` goes as parallel requests whose replies arrive as one (`answers` merged, `usage` summed), and a request a server rejects as too large (HTTP 400/413 naming tokens, fields or a limit) is halved and sent again, at most 3 deep; `split: false` turns both off. The fetch driver does it for any request through the new `batch`, `combine` and `split` keys; `renderComponent`'s fake answers such a request as one. Fixes `compose.decisions({ profile: 'small' })` on Ollama, whose layout step for 6–7 elements got HTTP 400 (G-757). [Large question sets](https://sygnal.js.org/guide/ai-decisions/#large-question-sets)
24
+ - **Composing UI with a model** (`sygnal/ai`, PLAN-9). The user describes a page in words, and a model builds it from parts the app prepared: it picks parts, their params and their places, and never writes markup, state paths or action names. `defineCatalog({ parts, containers, guidance, limits, examples })` declares the parts once for both kinds of model: each part is a Sygnal component (or a `render` view function) with a `description` (all a model sees of it), `params` (options, numbers with `bins`, LLM-only `text` / `list`, and records: `from: (state) => records`, one node per record), `state` (a key, a lens, or `(node) => lens`) or `local` state, and placement fields (`in`, `group`, `maxUses`, `required`, `pin`, `companions`). Containers are view functions with named slots (tabs, sections; a built-in `row`), never components, and the model never chooses one. The `compose(catalog, { backend, form, prompt })` behavior keeps the composition as plain JSON in the host's state (`status`, `layout`, `preview`, `decisions`, `error`) with the actions `COMPOSE`, `SET_LAYOUT`, `REMOVE`, `CONTROL` and `DONE`; `catalog.render(state.page, state)` renders it in the host's view, each part a child of the host keyed by its identity, so lenses, `CHILD.select`, `EVENTS`, context and drivers work as for any child. A follow-up `COMPOSE` edits the page (remove, change a setting, add, move, swap) and keeps the instances and state of the parts it doesn't touch; `{ prompt, fresh: true }` starts over. Guide: [Composing UI with a Model](https://sygnal.js.org/guide/ai-compose/), with live demos.
25
+ - **Two backends, one catalog.** `compose.decisions({ model })` asks a decision model (TypeSafe Jev, Ollama's `nimble`) multiple-choice questions through `decide()` on the `HTTP` sink, in at most two calls, posting to `/api/decide` by default so the API key stays on your server (options `url`, `rowMean`, `prefilter` (`'auto'` by default: on for large catalogs with categories), `maxOptions`, `maxParts`, `profile: 'small'` for small local models, and `maxQuestions` / `maxPromptTokens` / `split` for `decide()`). `compose.llm()` sends one structured-output request with the catalog's JSON Schema on the `LLM` sink, streamed as a preview, with a repair step; a reply still invalid after it is an error, never a layout. Measured on the research catalogs: Jev 9/11 flat dashboards, 191/210 structure cases and 57/60 enum params at about 0.35 s; `qwen3:8b` with a schema 11/11 at about 3.4 s (the guide's table). Switching backends changes no app code and no test.
26
+ - **`<Composed catalog={catalog} from="page" />`**: `catalog.render()` as a tag (from `sygnal/ai`, also `sygnal`), reading the composition from the host's state at a key or a path; a container view that throws renders the host's `onError` (SYG408). An app that never renders it ships none of it (about 0.5 KB gzipped when used).
27
+ - **Composing from an agent**: `actions: { ...compose.action('page') }` on an `agent` declaration offers `<agent>_page_compose({ prompt, fresh? })`, which answers once the composition is done; `compose.summary(slice)` is the page as an agent's `read` shows it.
28
+ - **Cards in a chat: `chat({ ask: { catalog } })`.** The model gets an `ask_user({ need })` tool; the app composes a card from the catalog (`compose.llm()` on the chat's sink by default, or `compose.decisions()`; `direct: true` takes the card as the tool input for providers that enforce tool schemas), keeps it on the tool part as `part.ui` (status, layout, answer, local state, the backend's decisions; persisted with the chat), and `askCard(catalog, state, part)` renders it. A part with `result: true` answers with `compose.answer(props, value)` to `PARENT`; the call closes with `{ part, answer }` and the chat goes on. STOP and a new message cancel open cards; a card that fails closes the call with the reason, so the model can ask in text. Guide: [Cards in a Chat](https://sygnal.js.org/guide/ai-cards/), with a live demo.
29
+ - **Client tools on the `chat` behavior**: `clientTools: { name: { description, inputSchema } }` declares tools the app answers itself (the AI SDK's `onToolCall` / `addToolOutput`): their calls stay open until `'assistant.TOOL_OUTPUT'` (`{ toolCallId, output }` or `{ toolCallId, errorText }`) answers them, a host entry `'assistant.TOOL_CALL'` runs as each opens, and `openToolCalls(state.assistant)` lists them. A Standard Schema input is validated first. The chat driver's `abort` takes a list of keys, and a chat request with `output` and `parse: false` sends the schema but leaves the reply's `text` for the caller to parse (`compose.llm()` parses leniently).
30
+ - **`chat({ model: (state) => id })`** and `compose.llm({ model: (state) => id })`: the model as a function of the host's state, resolved per request, so one chat behavior follows an app's model picker.
31
+ - **`composeWith(t, catalog, tree)`** for tests: it answers the pending composition, a page or a card, so that it becomes `tree`, through the pending backend's own fake reply, so one test runs against both backends; it throws when the backend can't reach the tree. Also `fromTree`, `toTree` and `removeNode` for layouts.
32
+ - **`byId(field, id)`** (from `sygnal`): a `{ get, set }` lens to the array element with that id, for a catalog's record parts or any child (`<TodoItem state={byId('todos', 3)} />`).
33
+ - **A component's identity is its `key`** (PLAN-9 D470): a component tag's instance belongs to the component plus its `key`, else its `id` prop, else its position in the view (before, `key` was ignored on component tags). A new key disposes the instance and starts a new one, which is how to reset a child; a `key` on `<Collection>` re-creates its items. New **SYG453** (dev only): two tags of one component with the same identity in one view share one instance. +6 B gzipped.
34
+ - **New diagnostic codes** for catalogs, compositions and cards, at run time in development and, where a literal `defineCatalog({...})` shows them, in `sygnal-check`: SYG160 (a result part outside a card), SYG161 (a container that is a component), SYG162 (a part with neither `component` nor `render`), SYG250–SYG252 (unknown part or slot, invalid param value, a record `from()` that returns no `{ value }` array), SYG450–SYG452 (an `in` naming an unknown slot, `<Composed>` misuse, a node listed twice), SYG680–SYG683 (an LLM-only part under a decision backend, a backend option problem, an answer outside the criteria, an LLM layout invalid after repair), SYG684–SYG686 (a client tool shadowing an agent tool, an answer for a call that isn't open, an `ask` catalog without a result part).
35
+ - **`sygnal-check`: catalog rules.** New **SYG253** (warn: a misspelled catalog declaration key, with a rename quick fix) and **SYG254** (info: a description lint: two parts outside one group sharing a phrase, a description naming its own param's value, options that overlap between a part's params); the `chat` and `compose` behaviors' keys and actions are known to the checker (no false SYG127 for `ask`). New **SYG426** for the deprecated lowercase marker tags (below), with `--fix`.
36
+ - **Agent context**: `llms.txt` (now up to 322 lines, D461) and the `sygnal-dev` skill describe catalogs, `compose`, `<Composed>`, `composeWith`, chat cards, client tools and `key` identity; `dist/guide` ships `ai-compose.md` and `ai-cards.md`. Examples: `examples/ai-compose` (both backends, a model picker, an optional dev proxy for Jev that keeps the key in an env file outside the repo) and `examples/ai-ask` (an assistant that asks with cards).
37
+
38
+ ### Changed
39
+
40
+ - **`examples/ai-panel-spa` runs on `sygnal/ai`** against a local Ollama: `makeChatDriver` over `openResponses()` with structured output and streamed turns (it had a hand-written driver with an API key in the browser), model pickers read from Ollama's installed models, a decision model that picks each speaker through `decide()`, `persist()` and the browser driver's clipboard instead of custom drivers, and canonical forms throughout. No library change.
41
+
42
+ ### Deprecated
43
+
44
+ - **The lowercase marker tags** (PLAN-9 D471): `<collection>` and `<switchable>`, which worked without an import, and the undocumented `<clientonly>`, `<portal>`, `<transition>` and `<suspense>`. They keep working in 6.x and will be removed in the next major, where they render unknown elements; the Collection and Switchable handlers will then load only in apps that import them (measured on a prepared patch: about 2.9 KB gzipped less for an app that uses neither, about 0.7 KB for kanban). To migrate, `import { Collection } from 'sygnal'` and write `<Collection …>` (`Switchable`, `Portal`, `Transition`, `Suspense` from `'sygnal'`, `ClientOnly` from `'sygnal/vike'`); `sygnal-check --fix` renames the tags and adds the import. New code SYG426: the dev diagnostics warn once per tag for `<collection>` / `<switchable>` (0 production bytes), `sygnal-check` reports every lowercase marker tag, and the JSX types mark the two intrinsic elements `@deprecated`. A plain `<slot>` is an HTML element, as before (G-480). `examples/todomvc` uses `<Collection>`.
45
+
46
+ ### Fixed
47
+
48
+ - **Real DOM: a keyed component that moves to another parent element in one patch** (G-750, G-753). Moving an instance to an earlier parent element threw `NotFoundError` in the DOM driver and stopped the app's DOM updates, and moving one into a parent element created in the same patch (a new row) left the DOM at the old arrangement, silently. Both are fixed in the DOM driver's isolation module; the mock DOM of `renderComponent` never showed them. Browser tests on Chromium, Firefox and WebKit.
49
+ - **The `chat` behavior after a reload.** A chat slice persisted while a reply streamed came back with `status: 'streaming'`, and Send did nothing until the page was cleared, in every app that persisted a chat. It now comes back `'ready'`, and tool calls left open are closed.
50
+ - **`Portal`, `Transition`, `Suspense` and `lazy` are tree-shaken** (G-751): they were registered when the main entry loaded, so every app carried them; they now register when first rendered. Kanban is 223 B gzipped smaller.
51
+
52
+ ## sygnal-check 0.5.1, VS Code extension 0.2.1 — 2026-10-11
53
+
54
+ A patch for the editor tooling; `sygnal` itself is unchanged (6.2.0). Reported against extension 0.2.0: opening `examples/todomvc` (code at the top level, no `src/`), or the repository root, underlined nothing and said nothing.
55
+
56
+ ### Fixed
57
+
58
+ - **`sygnal-check lsp` finds nested projects.** Each file is checked as part of its project root: the nearest folder, up to its workspace folder, with a `sygnal-check.config.json` or a `package.json` that depends on `sygnal` / `sygnal-check` (else the workspace folder, as before). Each root has its own config, `paths` (relative to the root), cache and settings (`workspace/configuration` per root). Opening a repository or monorepo root checks each app as its own project, from the first file you open in it; a workspace folder that only holds projects below it isn't checked as one.
59
+ - **`sygnal-check lsp` says when a file isn't checked.** A one-time message when an open Sygnal file is outside its project's `paths`, or when `paths` match no source files (`window/showMessage`, and the log).
60
+ - **VS Code extension:** it starts in larger monorepos (it looks through up to 500 `package.json` files for one that uses Sygnal, was 50).
61
+
62
+ ### Changed
63
+
64
+ - **Every app in `examples/` has a `sygnal-check.config.json`** (`strict: true`, as the templates; `todomvc` adds `paths: ["."]`, `vike` `paths: ["pages"]`), so the CLI, the dev overlay and the editor check them by default.
65
+
5
66
  ## 6.2.0 — 2026-10-10
6
67
 
7
68
  Two things since 6.1.1. Agents get read-only tools that take an input (`agent.queries`) and actions that answer once their result is in state (`until`). And the editor tooling (PLAN-7 Phase 4) grows from findings to navigation: the language server in `sygnal-check` 0.5.0 adds completions, go to definition / references, code lenses and rename for the names Sygnal wires by string, in VS Code (extension 0.2.0), Neovim, Helix, JetBrains IDEs and any other LSP editor.
package/README.md CHANGED
@@ -515,7 +515,9 @@ h('div', [h('h1', 'Hello'), h('button.btn', 'Click')])
515
515
  | [Vike SSR](./examples/vike) | File-based routing with SSR, layouts, and data fetching |
516
516
  | [Advanced Features](./examples/advanced-feature-tests) | Portals, slots, disposal, suspense, lazy loading |
517
517
  | [TypeScript 2048](./examples/ts-example-2048) | Full game in TypeScript |
518
- | [AI Discussion Panel](./examples/ai-panel-spa) | Complex SPA with custom drivers |
518
+ | [AI Discussion Panel](./examples/ai-panel-spa) | `sygnal/ai` on a local Ollama: streaming chat, structured output, a decision model picking speakers |
519
+ | [Compose a Workspace](./examples/ai-compose) | A decision model (`decide()`, local Ollama) picks and arranges prepared components from a prompt: the json-render + Jev idea |
520
+ | [Ask with Cards](./examples/ai-ask) | A chat assistant on a local Ollama that asks with cards built from prepared parts (`chat({ ask })`) and continues with the answer |
519
521
  | [Sygnal ToDoMVC](https://github.com/tpresley/sygnal-todomvc) | [Live Demo](https://tpresley.github.io/sygnal-todomvc/) |
520
522
  | [Sygnal 2048](https://github.com/tpresley/sygnal-2048) | [Live Demo](https://tpresley.github.io/sygnal-2048/) |
521
523
  | [Sygnal Mahjong](https://github.com/tpresley/mahjong-trainer) | [Live Demo](https://tpresley.github.io/mahjong-trainer/) |
package/dist/ai.cjs.js CHANGED
@@ -4,6 +4,10 @@ var sygnal = require('sygnal');
4
4
 
5
5
 
6
6
 
7
+ Object.defineProperty(exports, 'Composed', {
8
+ enumerable: true,
9
+ get: function () { return sygnal.Composed; }
10
+ });
7
11
  Object.defineProperty(exports, 'agentTools', {
8
12
  enumerable: true,
9
13
  get: function () { return sygnal.agentTools; }
@@ -20,6 +24,10 @@ Object.defineProperty(exports, 'anthropicMessages', {
20
24
  enumerable: true,
21
25
  get: function () { return sygnal.anthropicMessages; }
22
26
  });
27
+ Object.defineProperty(exports, 'askCard', {
28
+ enumerable: true,
29
+ get: function () { return sygnal.askCard; }
30
+ });
23
31
  Object.defineProperty(exports, 'chat', {
24
32
  enumerable: true,
25
33
  get: function () { return sygnal.chat; }
@@ -40,10 +48,22 @@ Object.defineProperty(exports, 'commandBar', {
40
48
  enumerable: true,
41
49
  get: function () { return sygnal.commandBar; }
42
50
  });
51
+ Object.defineProperty(exports, 'compose', {
52
+ enumerable: true,
53
+ get: function () { return sygnal.compose; }
54
+ });
55
+ Object.defineProperty(exports, 'composeWith', {
56
+ enumerable: true,
57
+ get: function () { return sygnal.composeWith; }
58
+ });
43
59
  Object.defineProperty(exports, 'decide', {
44
60
  enumerable: true,
45
61
  get: function () { return sygnal.decide; }
46
62
  });
63
+ Object.defineProperty(exports, 'defineCatalog', {
64
+ enumerable: true,
65
+ get: function () { return sygnal.defineCatalog; }
66
+ });
47
67
  Object.defineProperty(exports, 'encodeOpenResponses', {
48
68
  enumerable: true,
49
69
  get: function () { return sygnal.encodeOpenResponses; }
@@ -60,6 +80,10 @@ Object.defineProperty(exports, 'fromAISDK', {
60
80
  enumerable: true,
61
81
  get: function () { return sygnal.fromAISDK; }
62
82
  });
83
+ Object.defineProperty(exports, 'fromTree', {
84
+ enumerable: true,
85
+ get: function () { return sygnal.fromTree; }
86
+ });
63
87
  Object.defineProperty(exports, 'jsonSchema', {
64
88
  enumerable: true,
65
89
  get: function () { return sygnal.jsonSchema; }
@@ -84,6 +108,10 @@ Object.defineProperty(exports, 'openResponses', {
84
108
  enumerable: true,
85
109
  get: function () { return sygnal.openResponses; }
86
110
  });
111
+ Object.defineProperty(exports, 'openToolCalls', {
112
+ enumerable: true,
113
+ get: function () { return sygnal.openToolCalls; }
114
+ });
87
115
  Object.defineProperty(exports, 'outputJsonSchema', {
88
116
  enumerable: true,
89
117
  get: function () { return sygnal.outputJsonSchema; }
@@ -92,6 +120,10 @@ Object.defineProperty(exports, 'parseInput', {
92
120
  enumerable: true,
93
121
  get: function () { return sygnal.parseInput; }
94
122
  });
123
+ Object.defineProperty(exports, 'removeNode', {
124
+ enumerable: true,
125
+ get: function () { return sygnal.removeNode; }
126
+ });
95
127
  Object.defineProperty(exports, 'score', {
96
128
  enumerable: true,
97
129
  get: function () { return sygnal.score; }
@@ -104,6 +136,10 @@ Object.defineProperty(exports, 'toJsonSchema', {
104
136
  enumerable: true,
105
137
  get: function () { return sygnal.toJsonSchema; }
106
138
  });
139
+ Object.defineProperty(exports, 'toTree', {
140
+ enumerable: true,
141
+ get: function () { return sygnal.toTree; }
142
+ });
107
143
  Object.defineProperty(exports, 'uiMessageStream', {
108
144
  enumerable: true,
109
145
  get: function () { return sygnal.uiMessageStream; }
@@ -1 +1 @@
1
- {"version":3,"file":"ai.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"ai.cjs.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/ai.esm.js CHANGED
@@ -1,2 +1,2 @@
1
- export { agentTools, agui, answers, anthropicMessages, chat, chatCompletions, choice, chromePrompt, commandBar, decide, encodeOpenResponses, experimentalExposeWebMcp, formTool, fromAISDK, jsonSchema, makeChatDriver, makeMcpAppDriver, messageText, noul, openResponses, outputJsonSchema, parseInput, score, strictSchemas, toJsonSchema, uiMessageStream, withToolResults } from 'sygnal';
1
+ export { Composed, agentTools, agui, answers, anthropicMessages, askCard, chat, chatCompletions, choice, chromePrompt, commandBar, compose, composeWith, decide, defineCatalog, encodeOpenResponses, experimentalExposeWebMcp, formTool, fromAISDK, fromTree, jsonSchema, makeChatDriver, makeMcpAppDriver, messageText, noul, openResponses, openToolCalls, outputJsonSchema, parseInput, removeNode, score, strictSchemas, toJsonSchema, toTree, uiMessageStream, withToolResults } from 'sygnal';
2
2
  //# sourceMappingURL=ai.esm.js.map
@@ -3082,14 +3082,14 @@ function withResources(def, state) {
3082
3082
  return out;
3083
3083
  }
3084
3084
  // PLAN-4 GS-9: uid(name?) as on the client: the root is 'u'; a child component adds its path in
3085
- // the parent's view (or its `id` prop), as getComponentIdFromElement and instantiateSubComponents
3085
+ // the parent's view (or its vnode `key`, else its `id` prop, D456), as getComponentIdFromElement and instantiateSubComponents
3086
3086
  // in the core do; a Collection item adds its key, a Switchable page its name; anything but
3087
3087
  // each part encoded by uidPart (G-214); in the root option anything but [A-Za-z0-9_-] becomes '_'
3088
3088
  // (as run() does). SSR ids = hydration ids
3089
3089
  const makeUid = (base) => (n) => n ? base + '-' + n : base;
3090
3090
  function childUid(uid, vnode, path) {
3091
- const id = vnode.data?.props?.id;
3092
- return uid + '-' + uidPart(('::' + ((id && JSON.stringify(id).replaceAll('"', '')) || path)).replace(/.*::(r\.)?/, ''));
3091
+ const id = vnode.key ?? vnode.data?.props?.id;
3092
+ return uid + '-' + uidPart(('::' + ((id != null && JSON.stringify(id).replaceAll('"', '')) || path)).replace(/.*::(r\.)?/, ''));
3093
3093
  }
3094
3094
  // G-567: a view that returns a string or a number renders it as text (core/instance.ts view());
3095
3095
  // another falsy value, an empty <div>