sygnal 5.4.0 → 6.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/CHANGELOG.md +716 -0
- package/README.md +24 -9
- package/dist/ai.cjs.js +115 -0
- package/dist/ai.cjs.js.map +1 -0
- package/dist/ai.esm.js +2 -0
- package/dist/ai.esm.js.map +1 -0
- package/dist/astro/client.cjs.js +16 -9
- package/dist/astro/client.cjs.js.map +1 -1
- package/dist/astro/client.mjs +16 -9
- package/dist/astro/client.mjs.map +1 -1
- package/dist/astro/index.cjs.js +958 -92
- package/dist/astro/index.cjs.js.map +1 -1
- package/dist/astro/index.mjs +958 -93
- package/dist/astro/index.mjs.map +1 -1
- package/dist/astro/server.cjs.js +3396 -165
- package/dist/astro/server.cjs.js.map +1 -1
- package/dist/astro/server.mjs +3396 -165
- package/dist/astro/server.mjs.map +1 -1
- package/dist/devtools.cjs.js +1840 -0
- package/dist/devtools.cjs.js.map +1 -0
- package/dist/devtools.esm.js +1827 -0
- package/dist/devtools.esm.js.map +1 -0
- package/dist/diagnostics.cjs.js +3190 -296
- package/dist/diagnostics.cjs.js.map +1 -1
- package/dist/diagnostics.esm.js +3190 -296
- package/dist/diagnostics.esm.js.map +1 -1
- package/dist/element.cjs.js +230 -0
- package/dist/element.cjs.js.map +1 -0
- package/dist/element.esm.js +228 -0
- package/dist/element.esm.js.map +1 -0
- package/dist/guide/accessibility.md +310 -0
- package/dist/guide/adapters.md +221 -0
- package/dist/guide/agent.md +588 -0
- package/dist/guide/ai-chat.md +693 -0
- package/dist/guide/ai-decisions.md +330 -0
- package/dist/guide/behaviors.md +312 -0
- package/dist/guide/browser-sources.md +203 -0
- package/dist/guide/drag-and-drop.md +342 -0
- package/dist/guide/element-commands.md +306 -0
- package/dist/guide/error-boundaries.md +169 -0
- package/dist/guide/forms-reference.md +409 -0
- package/dist/guide/forms.md +251 -0
- package/dist/guide/http.md +301 -0
- package/dist/guide/inputs.md +173 -0
- package/dist/guide/mcp-apps.md +272 -0
- package/dist/guide/persistence.md +245 -0
- package/dist/guide/recipes/ai-form-fill.md +163 -0
- package/dist/guide/recipes/ai-summarize.md +155 -0
- package/dist/guide/recipes/ai-support-inbox.md +190 -0
- package/dist/guide/recipes/carousel.md +155 -0
- package/dist/guide/recipes/charts.md +177 -0
- package/dist/guide/recipes/code-editor.md +135 -0
- package/dist/guide/recipes/data-grid.md +155 -0
- package/dist/guide/recipes/data-table.md +196 -0
- package/dist/guide/recipes/i18n.md +222 -0
- package/dist/guide/recipes/icons.md +115 -0
- package/dist/guide/recipes/overview.md +43 -0
- package/dist/guide/recipes/rich-text.md +154 -0
- package/dist/guide/resources.md +471 -0
- package/dist/guide/ssr.md +255 -0
- package/dist/guide/timers.md +178 -0
- package/dist/guide/ui/accordion.md +93 -0
- package/dist/guide/ui/combobox.md +144 -0
- package/dist/guide/ui/dialog.md +124 -0
- package/dist/guide/ui/disclosure.md +60 -0
- package/dist/guide/ui/menu.md +125 -0
- package/dist/guide/ui/overview.md +166 -0
- package/dist/guide/ui/popover.md +101 -0
- package/dist/guide/ui/select.md +114 -0
- package/dist/guide/ui/tabs.md +161 -0
- package/dist/guide/ui/toaster.md +152 -0
- package/dist/guide/ui/tooltip.md +103 -0
- package/dist/guide/undo.md +178 -0
- package/dist/guide/virtual-collections.md +183 -0
- package/dist/guide/web-components.md +179 -0
- package/dist/guide/webmcp.md +226 -0
- package/dist/guide/widgets.md +217 -0
- package/dist/index.cjs.js +19288 -7408
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +4466 -552
- package/dist/index.esm.js +19274 -7461
- package/dist/index.esm.js.map +1 -1
- package/dist/jsx-dev-runtime.cjs.js +26 -334
- package/dist/jsx-dev-runtime.cjs.js.map +1 -1
- package/dist/jsx-dev-runtime.esm.js +19 -327
- package/dist/jsx-dev-runtime.esm.js.map +1 -1
- package/dist/jsx-runtime.cjs.js +26 -334
- package/dist/jsx-runtime.cjs.js.map +1 -1
- package/dist/jsx-runtime.esm.js +19 -327
- package/dist/jsx-runtime.esm.js.map +1 -1
- package/dist/jsx.cjs.js +10 -314
- package/dist/jsx.cjs.js.map +1 -1
- package/dist/jsx.esm.js +8 -315
- package/dist/jsx.esm.js.map +1 -1
- package/dist/react.cjs.js +117 -0
- package/dist/react.cjs.js.map +1 -0
- package/dist/react.esm.js +115 -0
- package/dist/react.esm.js.map +1 -0
- package/dist/shims/globalthis.cjs +20 -0
- package/dist/sygnal.min.js +1 -1
- package/dist/sygnal.min.js.map +1 -1
- package/dist/ui-combobox.cjs.js +169 -0
- package/dist/ui-combobox.cjs.js.map +1 -0
- package/dist/ui-combobox.esm.js +148 -0
- package/dist/ui-combobox.esm.js.map +1 -0
- package/dist/ui-menu.cjs.js +87 -0
- package/dist/ui-menu.cjs.js.map +1 -0
- package/dist/ui-menu.esm.js +66 -0
- package/dist/ui-menu.esm.js.map +1 -0
- package/dist/ui-select.cjs.js +104 -0
- package/dist/ui-select.cjs.js.map +1 -0
- package/dist/ui-select.esm.js +83 -0
- package/dist/ui-select.esm.js.map +1 -0
- package/dist/ui.cjs.js +738 -0
- package/dist/ui.cjs.js.map +1 -0
- package/dist/ui.esm.js +727 -0
- package/dist/ui.esm.js.map +1 -0
- package/dist/vike/ClientOnly.cjs.js +4 -14
- package/dist/vike/ClientOnly.cjs.js.map +1 -1
- package/dist/vike/ClientOnly.mjs +4 -14
- package/dist/vike/ClientOnly.mjs.map +1 -1
- package/dist/vike/{+config.js → config/+config.js} +9 -1
- package/dist/vike/config/+config.js.map +1 -0
- package/dist/vike/config/package.json +6 -0
- package/dist/vike/onRenderClient.cjs.js +165 -95
- package/dist/vike/onRenderClient.cjs.js.map +1 -1
- package/dist/vike/onRenderClient.mjs +165 -95
- package/dist/vike/onRenderClient.mjs.map +1 -1
- package/dist/vike/onRenderHtml.cjs.js +59 -24
- package/dist/vike/onRenderHtml.cjs.js.map +1 -1
- package/dist/vike/onRenderHtml.mjs +60 -25
- package/dist/vike/onRenderHtml.mjs.map +1 -1
- package/dist/vite/plugin.cjs.js +919 -89
- package/dist/vite/plugin.cjs.js.map +1 -1
- package/dist/vite/plugin.mjs +919 -90
- package/dist/vite/plugin.mjs.map +1 -1
- package/dist/zag.cjs.js +408 -0
- package/dist/zag.cjs.js.map +1 -0
- package/dist/zag.esm.js +405 -0
- package/dist/zag.esm.js.map +1 -0
- package/llms.txt +172 -103
- package/package.json +112 -14
- package/src/ai.d.ts +1186 -0
- package/src/ai.ts +23 -0
- package/src/astro/client.ts +27 -16
- package/src/astro/index.d.ts +24 -0
- package/src/astro/index.ts +69 -3
- package/src/astro/server.ts +8 -2
- package/src/collection.ts +6 -93
- package/src/core/actions.ts +134 -0
- package/src/core/cell.ts +202 -0
- package/src/core/debug.ts +23 -0
- package/src/core/define.ts +242 -0
- package/src/core/hooks.ts +314 -0
- package/src/core/hosts/collection.ts +325 -0
- package/src/core/hosts/switchable.ts +146 -0
- package/src/core/instance.ts +592 -0
- package/src/core/markers/clientonly.ts +13 -0
- package/src/core/markers/lazy.ts +40 -0
- package/src/core/markers/portal.ts +96 -0
- package/src/core/markers/suspense.ts +41 -0
- package/src/core/markers/transition.ts +70 -0
- package/src/core/registry.ts +25 -0
- package/src/core/runtime.ts +573 -0
- package/src/core/statics.ts +104 -0
- package/src/core/teardown.ts +60 -0
- package/src/core/view.ts +37 -0
- package/src/cycle/dom/DocumentDOMSource.ts +13 -8
- package/src/cycle/dom/ElementFinder.ts +7 -9
- package/src/cycle/dom/EventDelegator.ts +174 -223
- package/src/cycle/dom/IsolateModule.ts +65 -35
- package/src/cycle/dom/MainDOMSource.ts +9 -2
- package/src/cycle/dom/PriorityQueue.ts +4 -2
- package/src/cycle/dom/SymbolTree.ts +18 -47
- package/src/cycle/dom/classNameModule.ts +9 -7
- package/src/cycle/dom/controlledInputModule.ts +23 -6
- package/src/cycle/dom/enrichEventStream.ts +25 -47
- package/src/cycle/dom/fragment.ts +7 -0
- package/src/cycle/dom/isolate.ts +30 -27
- package/src/cycle/dom/makeDOMDriver.ts +115 -56
- package/src/cycle/dom/mockDOMSource.ts +21 -1
- package/src/cycle/dom/modules.ts +16 -3
- package/src/cycle/dom/propsModule.ts +65 -0
- package/src/cycle/dom/selectModule.ts +22 -17
- package/src/cycle/dom/snabbdom.ts +1 -6
- package/src/cycle/dom/thunk.ts +3 -0
- package/src/cycle/dom/utils.ts +98 -0
- package/src/cycle/dom/viewTransition.ts +43 -0
- package/src/cycle/state/StateSource.ts +19 -3
- package/src/cycle/state/objIsEqual.ts +50 -0
- package/src/cycle/state/types.ts +0 -10
- package/src/defineComponent.ts +34 -0
- package/src/devtools.d.ts +170 -0
- package/src/devtools.ts +37 -0
- package/src/element.d.ts +59 -0
- package/src/element.ts +235 -0
- package/src/extra/ai/agent/index.ts +484 -0
- package/src/extra/ai/answers.ts +115 -0
- package/src/extra/ai/chat/behavior.ts +336 -0
- package/src/extra/ai/chat/driver.ts +271 -0
- package/src/extra/ai/chat/memoryTransport.ts +61 -0
- package/src/extra/ai/chat/output.ts +37 -0
- package/src/extra/ai/commandBar.ts +281 -0
- package/src/extra/ai/decide.ts +104 -0
- package/src/extra/ai/index.ts +42 -0
- package/src/extra/ai/link.ts +90 -0
- package/src/extra/ai/mcpApp.ts +314 -0
- package/src/extra/ai/messages.ts +51 -0
- package/src/extra/ai/schema/index.ts +166 -0
- package/src/extra/ai/schema/jsonSchema.ts +72 -0
- package/src/extra/ai/schema/strict.ts +152 -0
- package/src/extra/ai/transports/agui.ts +177 -0
- package/src/extra/ai/transports/anthropicMessages.ts +175 -0
- package/src/extra/ai/transports/chatCompletions.ts +105 -0
- package/src/extra/ai/transports/chromePrompt.ts +77 -0
- package/src/extra/ai/transports/encodeOpenResponses.ts +87 -0
- package/src/extra/ai/transports/fromAISDK.ts +126 -0
- package/src/extra/ai/transports/openResponses.ts +105 -0
- package/src/extra/ai/transports/shared.ts +125 -0
- package/src/extra/ai/transports/tools.ts +59 -0
- package/src/extra/ai/transports/uiMessageStream.ts +107 -0
- package/src/extra/ai/webmcp.ts +260 -0
- package/src/extra/backoff.ts +9 -0
- package/src/extra/behaviors.ts +169 -0
- package/src/extra/browserSignals.ts +23 -0
- package/src/extra/browserSources.ts +326 -0
- package/src/extra/command.ts +3 -3
- package/src/extra/controls.ts +47 -0
- package/src/extra/copyAsTest.ts +286 -0
- package/src/extra/devMcp.ts +386 -0
- package/src/extra/devtools.ts +101 -31
- package/src/extra/devtoolsActions.ts +379 -0
- package/src/extra/devtoolsHook.ts +9 -0
- package/src/extra/devtoolsNext.ts +83 -0
- package/src/extra/diagnostics/checks/actionLog.ts +108 -0
- package/src/extra/diagnostics/checks/behaviors.ts +49 -0
- package/src/extra/diagnostics/checks/browserSources.ts +96 -0
- package/src/extra/diagnostics/checks/chat.ts +71 -0
- package/src/extra/diagnostics/checks/collections.ts +1 -1
- package/src/extra/diagnostics/checks/controls.ts +148 -0
- package/src/extra/diagnostics/checks/dataset.ts +57 -0
- package/src/extra/diagnostics/checks/dom.ts +20 -11
- package/src/extra/diagnostics/checks/elementCommands.ts +175 -0
- package/src/extra/diagnostics/checks/events.ts +17 -2
- package/src/extra/diagnostics/checks/fetch.ts +135 -0
- package/src/extra/diagnostics/checks/forms.ts +171 -0
- package/src/extra/diagnostics/checks/index.ts +103 -4
- package/src/extra/diagnostics/checks/inspect.ts +140 -32
- package/src/extra/diagnostics/checks/next.ts +417 -0
- package/src/extra/diagnostics/checks/persist.ts +54 -0
- package/src/extra/diagnostics/checks/props.ts +10 -5
- package/src/extra/diagnostics/checks/public.d.ts +115 -7
- package/src/extra/diagnostics/checks/replies.ts +128 -0
- package/src/extra/diagnostics/checks/router.ts +100 -0
- package/src/extra/diagnostics/checks/rxjsHints.ts +1 -1
- package/src/extra/diagnostics/checks/shared.ts +86 -2
- package/src/extra/diagnostics/checks/shorthand.ts +115 -0
- package/src/extra/diagnostics/checks/sortable.ts +129 -0
- package/src/extra/diagnostics/checks/state.ts +100 -7
- package/src/extra/diagnostics/checks/statics.ts +48 -0
- package/src/extra/diagnostics/checks/strict.ts +10 -67
- package/src/extra/diagnostics/checks/timers.ts +80 -0
- package/src/extra/diagnostics/checks/viewTransitions.ts +47 -0
- package/src/extra/diagnostics/checks/virtual.ts +73 -0
- package/src/extra/diagnostics/checks/widgets.ts +109 -0
- package/src/extra/diagnostics/checks/wiring.ts +69 -10
- package/src/extra/diagnostics/codes.ts +335 -23
- package/src/extra/diagnostics/index.ts +34 -9
- package/src/extra/diagnostics/legacy.ts +17 -0
- package/src/extra/driverFactories.ts +83 -19
- package/src/extra/elementCommands.ts +53 -0
- package/src/extra/fetchDriver.ts +519 -0
- package/src/extra/focusWithin.ts +27 -0
- package/src/extra/form.ts +284 -0
- package/src/extra/formHelpers.ts +139 -0
- package/src/extra/formTool.ts +150 -0
- package/src/extra/head.ts +106 -0
- package/src/extra/hmr.ts +2 -3
- package/src/extra/owned.ts +24 -0
- package/src/extra/pager.ts +50 -0
- package/src/extra/persist.ts +133 -0
- package/src/extra/pwa.ts +1 -1
- package/src/extra/queryCache.ts +107 -0
- package/src/extra/reducers.ts +1 -1
- package/src/extra/reduxDevtools.ts +92 -0
- package/src/extra/replies.ts +54 -0
- package/src/extra/router.ts +323 -0
- package/src/extra/run.ts +90 -217
- package/src/extra/selection.ts +76 -0
- package/src/extra/socketDriver.ts +265 -0
- package/src/extra/sortable.ts +377 -0
- package/src/extra/ssr.ts +427 -160
- package/src/extra/standardSchema.ts +27 -0
- package/src/extra/testing.ts +2550 -175
- package/src/extra/timers.ts +95 -0
- package/src/extra/undo.ts +261 -0
- package/src/extra/viewTransitions.ts +38 -0
- package/src/extra/virtual.ts +513 -0
- package/src/extra/widget.ts +201 -0
- package/src/index.d.ts +2754 -127
- package/src/index.ts +45 -4
- package/src/jsx-runtime.ts +15 -1
- package/src/jsx.ts +2 -1
- package/src/lazy.ts +50 -7
- package/src/portal.ts +3 -1
- package/src/pragma/index.ts +256 -133
- package/src/react-peers.d.ts +5 -0
- package/src/react.d.ts +43 -0
- package/src/react.ts +110 -0
- package/src/shared.ts +57 -0
- package/src/slot.ts +1 -1
- package/src/suspense.ts +3 -1
- package/src/switchable.ts +6 -121
- package/src/transition.ts +3 -1
- package/src/ui/accordion.ts +64 -0
- package/src/ui/dialog.ts +129 -0
- package/src/ui/disclosure.ts +35 -0
- package/src/ui/popover.ts +45 -0
- package/src/ui/shared.ts +94 -0
- package/src/ui/tabs.ts +82 -0
- package/src/ui/toaster.ts +198 -0
- package/src/ui/tooltip.ts +70 -0
- package/src/ui/zag/combobox.ts +115 -0
- package/src/ui/zag/menu.ts +40 -0
- package/src/ui/zag/select.ts +54 -0
- package/src/ui/zag/shared.ts +43 -0
- package/src/ui-combobox.d.ts +20 -0
- package/src/ui-combobox.ts +6 -0
- package/src/ui-menu.d.ts +30 -0
- package/src/ui-menu.ts +6 -0
- package/src/ui-select.d.ts +17 -0
- package/src/ui-select.ts +6 -0
- package/src/ui-zag-types.d.ts +42 -0
- package/src/ui.d.ts +208 -0
- package/src/ui.ts +15 -0
- package/src/vike/+config.ts +9 -1
- package/src/vike/ClientOnly.ts +1 -1
- package/src/vike/onRenderClient.ts +144 -96
- package/src/vike/onRenderHtml.ts +69 -26
- package/src/vike/types.ts +7 -1
- package/src/vite/globalthis-shim.ts +18 -0
- package/src/vite/globalthis.ts +56 -0
- package/src/vite/mcp.ts +568 -0
- package/src/vite/plugin.d.ts +47 -3
- package/src/vite/plugin.ts +262 -25
- package/src/zag.d.ts +64 -0
- package/src/zag.ts +238 -0
- package/dist/vike/+config.cjs.js +0 -66
- package/dist/vike/+config.cjs.js.map +0 -1
- package/dist/vike/+config.js.map +0 -1
- package/src/component.ts +0 -2301
- package/src/cycle/isolate/index.ts +0 -196
- package/src/cycle/run/index.ts +0 -151
- package/src/cycle/run/internals.ts +0 -143
- package/src/cycle/state/Collection.ts +0 -184
- package/src/cycle/state/index.ts +0 -5
- package/src/cycle/state/pickCombine.ts +0 -201
- package/src/cycle/state/pickMerge.ts +0 -122
- package/src/cycle/state/withState.ts +0 -47
- package/src/pragma/fn.ts +0 -55
package/src/ai.d.ts
ADDED
|
@@ -0,0 +1,1186 @@
|
|
|
1
|
+
// Types for 'sygnal/ai' (PLAN-6). The implementation lives in src/extra/ai/ (D253).
|
|
2
|
+
import type { FetchRequest, Behavior, BehaviorTarget, FieldErrors } from 'sygnal'
|
|
3
|
+
import type { Stream } from 'xstream'
|
|
4
|
+
|
|
5
|
+
/** A part of a chat message (AI SDK UIMessage-shaped, D252) */
|
|
6
|
+
export type MessagePart =
|
|
7
|
+
| { type: 'text'; text: string }
|
|
8
|
+
| { type: 'reasoning'; text: string; /** provider data to send back with it (an Anthropic thinking signature) */ providerMetadata?: Record<string, Record<string, unknown>> }
|
|
9
|
+
| { type: `tool-${string}`; toolCallId: string; state: string; input?: unknown; output?: unknown; errorText?: string; approval?: { id: string; approved?: boolean; reason?: string; [key: string]: unknown }; providerExecuted?: boolean }
|
|
10
|
+
| { type: 'file'; mediaType: string; url: string; filename?: string }
|
|
11
|
+
| { type: `source-${string}`; [key: string]: unknown }
|
|
12
|
+
| { type: `data-${string}`; id?: string; data: unknown }
|
|
13
|
+
|
|
14
|
+
/** A chat message: parts, or a plain `content` string for messages written by hand */
|
|
15
|
+
export type Message =
|
|
16
|
+
| { id?: string; role: 'system' | 'user' | 'assistant'; parts: MessagePart[] }
|
|
17
|
+
| { id?: string; role: 'system' | 'user' | 'assistant'; content: string }
|
|
18
|
+
|
|
19
|
+
/** The text of a message: its text parts joined (or its `content`) */
|
|
20
|
+
export function messageText(message: Message | null | undefined): string
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The message with its open tool calls answered (the raw chat driver's tool loop): each
|
|
24
|
+
* `tool-<name>` part in `input-available` gets its result, `state: 'output-available'` with
|
|
25
|
+
* `output`, or `'output-error'` with `errorText` for an `Error` (returned or thrown). `results`: a
|
|
26
|
+
* function of the call, or an object keyed by the call's id, else by the tool's name; `undefined`
|
|
27
|
+
* leaves a part open. The `chat` behavior does this itself.
|
|
28
|
+
*/
|
|
29
|
+
export function withToolResults<M extends Message>(message: M, results: ((call: ChatCall) => unknown) | Record<string, unknown>): M
|
|
30
|
+
|
|
31
|
+
// ---- M-1: decisions -------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
/** A question's instructions: text, or an object / array holding the question and the data it names */
|
|
34
|
+
export type DecisionInstructions = string | Record<string, unknown> | readonly unknown[]
|
|
35
|
+
|
|
36
|
+
/** `choice(...)`: pick one option. `K`: the option names */
|
|
37
|
+
export interface ChoiceQuestion<K extends string = string> {
|
|
38
|
+
type: 'choice'
|
|
39
|
+
instructions: DecisionInstructions
|
|
40
|
+
/** Option name → description (or null) */
|
|
41
|
+
criteria: { [P in K]: string | null }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** `noul(...)`: the probability (0 to 1) that the answer is yes */
|
|
45
|
+
export interface NoulQuestion {
|
|
46
|
+
type: 'noul'
|
|
47
|
+
instructions: DecisionInstructions
|
|
48
|
+
/** What counts as true and as false (recommended: answers are better calibrated with them) */
|
|
49
|
+
criteria?: { true: string; false: string }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** `score(...)`: an ordered scale. `L`: the level descriptions, lowest first */
|
|
53
|
+
export interface ScoreQuestion<L extends string = string> {
|
|
54
|
+
type: 'score'
|
|
55
|
+
instructions: DecisionInstructions
|
|
56
|
+
/** Level descriptions, lowest first (the answer's `score` is the probability-weighted index) */
|
|
57
|
+
criteria: readonly L[]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export type Question = ChoiceQuestion<any> | NoulQuestion | ScoreQuestion<any>
|
|
61
|
+
/** Questions by name; the answers come back under the same names */
|
|
62
|
+
export type Questions = Record<string, Question>
|
|
63
|
+
|
|
64
|
+
/** The answer to a `choice` question */
|
|
65
|
+
export interface ChoiceAnswer<K extends string = string> {
|
|
66
|
+
type: 'choice'
|
|
67
|
+
/** The most likely option */
|
|
68
|
+
choice: K
|
|
69
|
+
/** Every option's probability (they sum to 1) */
|
|
70
|
+
probabilities: Record<K, number>
|
|
71
|
+
/** 0 to 1: how strongly the model favours one option (not a guarantee of correctness) */
|
|
72
|
+
confidence: number
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** The answer to a `noul` question */
|
|
76
|
+
export interface NoulAnswer {
|
|
77
|
+
type: 'noul'
|
|
78
|
+
/** The probability of yes, 0 to 1 */
|
|
79
|
+
noul: number
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** The answer to a `score` question */
|
|
83
|
+
export interface ScoreAnswer<L extends string = string> {
|
|
84
|
+
type: 'score'
|
|
85
|
+
/** The probability-weighted level index (0 = the first level); can fall between levels */
|
|
86
|
+
score: number
|
|
87
|
+
/** Level index ('0', '1', …) → its description */
|
|
88
|
+
legend: Record<string, L>
|
|
89
|
+
/** Level index ('0', '1', …) → its probability */
|
|
90
|
+
probabilities: Record<string, number>
|
|
91
|
+
confidence: number
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** `decide.openai()` only: OpenAI Decisions refused this question */
|
|
95
|
+
export interface RefusalAnswer {
|
|
96
|
+
type: 'refusal'
|
|
97
|
+
[key: string]: unknown
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** The answer type of a question */
|
|
101
|
+
export type Answer<Q> =
|
|
102
|
+
Q extends ChoiceQuestion<infer K> ? ChoiceAnswer<K>
|
|
103
|
+
: Q extends ScoreQuestion<infer L> ? ScoreAnswer<L>
|
|
104
|
+
: Q extends NoulQuestion ? NoulAnswer
|
|
105
|
+
: never
|
|
106
|
+
|
|
107
|
+
/** The answers to a set of questions, by name */
|
|
108
|
+
export type Answers<Q extends Questions> = { [N in keyof Q]: Answer<Q[N]> }
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* A decision reply (the `ok` action's data, a resource's `data`): `Decision<typeof questions>`.
|
|
112
|
+
* `decide.openai()` replies are `Decision<Q, RefusalAnswer>` (an answer can be a refusal)
|
|
113
|
+
*/
|
|
114
|
+
export interface Decision<Q extends Questions = Questions, R = never> {
|
|
115
|
+
model?: string
|
|
116
|
+
answers: { [N in keyof Q]: Answer<Q[N]> | R }
|
|
117
|
+
usage?: { input_tokens: number; output_tokens: number }
|
|
118
|
+
[key: string]: unknown
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
type FetchRequestObject = Extract<FetchRequest, { url: string }>
|
|
122
|
+
|
|
123
|
+
/** What `decide()` / `decide.openai()` take: the decision, plus any `makeFetchDriver` request keys */
|
|
124
|
+
export type DecideOptions<Q extends Questions> = Omit<FetchRequestObject, 'url' | 'json' | 'body'> & {
|
|
125
|
+
/** Default '/api/decide' (the app's own route, which holds the API key) */
|
|
126
|
+
url?: string
|
|
127
|
+
/** The decision model, e.g. 'jev-latest' (TypeSafe), 'nimble' (Ollama), 'gpt-6-luna' (OpenAI) */
|
|
128
|
+
model: string
|
|
129
|
+
/** What the questions are about: text, or an object / array (JSON) */
|
|
130
|
+
state: unknown
|
|
131
|
+
questions: Q
|
|
132
|
+
/** Base64 PNG / JPEG / WebP files shared by every question (models that read images only) */
|
|
133
|
+
images?: readonly string[]
|
|
134
|
+
/** As a resource: keep the previous `data` on a new request */
|
|
135
|
+
keepPrevious?: boolean
|
|
136
|
+
/** As a resource: refetch every this many ms */
|
|
137
|
+
refetchEvery?: number
|
|
138
|
+
/** As a resource: stay live in a hidden Switchable page */
|
|
139
|
+
background?: boolean
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** A decision as a `makeFetchDriver` request (POST JSON); its reply is a `Decision<Q, R>` */
|
|
143
|
+
export type DecideRequest<Q extends Questions = Questions, R = never> = Omit<DecideOptions<Q>, 'model' | 'state' | 'questions' | 'images' | 'url'> & {
|
|
144
|
+
url: string
|
|
145
|
+
method: 'POST'
|
|
146
|
+
json: Record<string, unknown>
|
|
147
|
+
/** Type only: the reply's shape (`Decision<Q, R>`); never set */
|
|
148
|
+
readonly __decision?: Decision<Q, R>
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* PLAN-6 M-1: a decision request for `makeFetchDriver`, in the dictionary form TypeSafe
|
|
153
|
+
* (`/v1/systemone`), Ollama ≥ 0.35 (`/v1/systemone`), OpenRouter and the AI Gateway take:
|
|
154
|
+
* `{ url, method: 'POST', json: { model, state, questions, images? }, ...requestKeys }`. Use it
|
|
155
|
+
* as a reply-action request (`{ HTTP: () => decide({ ..., ok: 'TRIAGED' }) }`) or as a
|
|
156
|
+
* `resources` entry; the reply is `{ model, answers: { name: answer }, usage }` (`Decision<Q>`).
|
|
157
|
+
* Any other key (`ok`, `error`, `key`, `latest`, `headers`, ...) passes through to the request.
|
|
158
|
+
*/
|
|
159
|
+
export const decide: {
|
|
160
|
+
<Q extends Questions>(options: DecideOptions<Q>): DecideRequest<Q>
|
|
161
|
+
/**
|
|
162
|
+
* The same questions as an OpenAI Decisions request (`{ model, input, questions: [...] }`,
|
|
163
|
+
* `predicate` / `choice` / `score`); its `parse` maps the reply's answer array back to the
|
|
164
|
+
* dictionary form, so reply actions and resources get a `Decision<Q, RefusalAnswer>`. A
|
|
165
|
+
* `noul` question's criteria are appended to the predicate's instructions. Point `url` at
|
|
166
|
+
* your server route that adds the key (OpenAI: `POST /v1/decisions`)
|
|
167
|
+
*/
|
|
168
|
+
openai<Q extends Questions>(options: DecideOptions<Q>): DecideRequest<Q, RefusalAnswer>
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** A `choice` question: the most likely of the options (`{ name: description | null }` or names) */
|
|
172
|
+
export function choice<K extends string>(instructions: DecisionInstructions, criteria: { [P in K]: string | null }): ChoiceQuestion<K>
|
|
173
|
+
export function choice<const K extends string>(instructions: DecisionInstructions, criteria: readonly K[]): ChoiceQuestion<K>
|
|
174
|
+
|
|
175
|
+
/** A `noul` question: the probability of yes; `criteria` describe what counts as true / false */
|
|
176
|
+
export function noul(instructions: DecisionInstructions, criteria?: { true: string; false: string }): NoulQuestion
|
|
177
|
+
|
|
178
|
+
/** A `score` question: an ordered scale of 2 or more level descriptions, lowest first */
|
|
179
|
+
export function score<const L extends string>(instructions: DecisionInstructions, levels: readonly [L, L, ...L[]]): ScoreQuestion<L>
|
|
180
|
+
// ---- M-2: decision reply fixtures (3-M) ----------------------------------------------------------
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* What `answers()` takes for one question:
|
|
184
|
+
* - choice: an option name, `{ choice?, confidence? }`, or `{ probabilities }` (the most likely wins);
|
|
185
|
+
* - noul: `true` (0.95), `false` (0.05) or the probability of yes;
|
|
186
|
+
* - score: a level index (fractional: between two levels), a level description, or `{ score, confidence? }`
|
|
187
|
+
*/
|
|
188
|
+
export type AnswerPick<Q> =
|
|
189
|
+
Q extends ChoiceQuestion<infer K> ? K | { choice?: K; confidence?: number } | { probabilities: Partial<Record<K, number>>; choice?: K; confidence?: number }
|
|
190
|
+
: Q extends ScoreQuestion<infer L> ? number | L | { score: number | L; confidence?: number }
|
|
191
|
+
: Q extends NoulQuestion ? boolean | number | { noul: number }
|
|
192
|
+
: never
|
|
193
|
+
|
|
194
|
+
/** The picks by question name (each optional) */
|
|
195
|
+
export type AnswerPicks<Q extends Questions> = { [N in keyof Q]?: AnswerPick<Q[N]> }
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* PLAN-6 M-2: a decision reply for tests, typed from the questions: what a decision model would
|
|
199
|
+
* send for `decide({ questions })`, with what the test doesn't pick filled in (a choice: the first
|
|
200
|
+
* option at confidence 0.9, the rest sharing the remaining probability; a noul: false; a score: 0).
|
|
201
|
+
* `confidence` is computed as TypeSafe / Ollama `nimble` report it (1 minus the normalized
|
|
202
|
+
* entropy), unless given. Unknown question names, options or levels throw.
|
|
203
|
+
*
|
|
204
|
+
* await t.respond('HTTP', answers(questions, { topic: { choice: 'billing', confidence: 0.35 } }), 'TRIAGED')
|
|
205
|
+
*/
|
|
206
|
+
export const answers: {
|
|
207
|
+
<Q extends Questions>(questions: Q, picks?: AnswerPicks<Q>, options?: { model?: string; usage?: { input_tokens: number; output_tokens: number } }): Decision<Q> & { model: string }
|
|
208
|
+
/** the same reply in OpenAI Decisions' array form, for a `decide.openai()` request (its `parse` maps it back) */
|
|
209
|
+
openai<Q extends Questions>(questions: Q, picks?: AnswerPicks<Q>, options?: { model?: string }): { model: string; answers: Array<Record<string, unknown> & { type: 'predicate' | 'choice' | 'score'; name: string }> }
|
|
210
|
+
}
|
|
211
|
+
// ------------------------------------------------------------------------------------------------
|
|
212
|
+
// A-1: schemas (the input contract) and the agent layer
|
|
213
|
+
|
|
214
|
+
/** A plain JSON Schema object (for `jsonSchema()`) */
|
|
215
|
+
export type JsonSchemaObject = {
|
|
216
|
+
readonly type?: string | readonly string[]
|
|
217
|
+
readonly properties?: Record<string, unknown>
|
|
218
|
+
readonly '~standard'?: never
|
|
219
|
+
readonly [keyword: string]: unknown
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* A schema for an agent action's `input` (or a chat request's `output`): a Standard Schema
|
|
224
|
+
* whose validated output is assignable to D, that also implements Standard JSON Schema (Zod
|
|
225
|
+
* 4.2+, ArkType 2.1.28+, Valibot through `toStandardJsonSchema()`), or `jsonSchema()`. Without a
|
|
226
|
+
* JSON Schema form the tool isn't offered (SYG240). Structural: no dependency on
|
|
227
|
+
* `@standard-schema/spec`.
|
|
228
|
+
*/
|
|
229
|
+
export type AgentSchema<D = unknown> = {
|
|
230
|
+
readonly '~standard': {
|
|
231
|
+
readonly version: 1
|
|
232
|
+
readonly vendor: string
|
|
233
|
+
readonly validate: (value: unknown, options?: any) => unknown
|
|
234
|
+
readonly types?: { readonly input: unknown; readonly output: D } | undefined
|
|
235
|
+
readonly jsonSchema?: { readonly input: (options: { target: string; libraryOptions?: Record<string, unknown> }) => Record<string, unknown> }
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Plain JSON Schema as an `input` / `output` (D262): the schema goes to the model as is
|
|
241
|
+
* (normalized), and a subset validator checks the value (type, enum, const, required,
|
|
242
|
+
* properties, additionalProperties, items, prefixItems, anyOf / oneOf, minimum / maximum,
|
|
243
|
+
* minLength / maxLength, minItems / maxItems, pattern, local `$ref`s; format, allOf, not,
|
|
244
|
+
* if / then / else, multipleOf and uniqueItems are not checked). `{ validate }` keeps a library's
|
|
245
|
+
* own validation: `jsonSchema(z.toJSONSchema(s), { validate: s })` for Zod Mini. T is asserted,
|
|
246
|
+
* not inferred.
|
|
247
|
+
*/
|
|
248
|
+
export function jsonSchema<T = unknown>(schema: JsonSchemaObject, options?: { validate?: { readonly '~standard': { readonly validate: (value: unknown) => unknown } } }): AgentSchema<T>
|
|
249
|
+
|
|
250
|
+
/** A schema converted for a model: the tool schema, or why there is none */
|
|
251
|
+
export interface ConvertedSchema {
|
|
252
|
+
/** the normalized, input-side JSON Schema (draft 2020-12); always an object root */
|
|
253
|
+
schema?: Record<string, any>
|
|
254
|
+
/** a non-object root was wrapped as `{ value }` (the unwrap also takes bare arguments) */
|
|
255
|
+
wrapped?: boolean
|
|
256
|
+
/** a lossy conversion (SYG243): what the model doesn't see (it is still validated) */
|
|
257
|
+
lossy?: string
|
|
258
|
+
/** no JSON Schema form (SYG240) */
|
|
259
|
+
error?: string
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* The JSON Schema a model gets for a schema: the input side, normalized for every provider
|
|
264
|
+
* (`$schema` dropped, `oneOf` -> `anyOf`, Zod's safe-integer bounds and a tuple's `items: false`
|
|
265
|
+
* dropped, a root `"$ref": "#"` moved into `$defs`), a non-object root wrapped as `{ value }`.
|
|
266
|
+
* Cached by schema object.
|
|
267
|
+
*/
|
|
268
|
+
export function toJsonSchema(schema: unknown): ConvertedSchema
|
|
269
|
+
|
|
270
|
+
/** The validated output type of a schema */
|
|
271
|
+
export type SchemaOutput<S> = S extends { readonly '~standard': { readonly types?: infer T } } ? NonNullable<T> extends { readonly output: infer O } ? O : unknown : unknown
|
|
272
|
+
|
|
273
|
+
/** One validation failure */
|
|
274
|
+
export interface AgentIssue { message: string; path?: PropertyKey[] }
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* A model's arguments for a schema -> the validated value: the `{ value }` unwrap (bare
|
|
278
|
+
* arguments accepted), `repair` (numeric and boolean strings where the schema says number /
|
|
279
|
+
* integer / boolean; `repair: false` turns it off, D264), then the schema's validation.
|
|
280
|
+
*/
|
|
281
|
+
export function parseInput<S extends AgentSchema<any>>(schema: S, args: unknown, options?: { repair?: boolean }): Promise<
|
|
282
|
+
| { value: SchemaOutput<S>; issues?: undefined; error?: undefined }
|
|
283
|
+
| { value?: undefined; issues: AgentIssue[]; error?: undefined }
|
|
284
|
+
| { value?: undefined; issues?: undefined; error: string }
|
|
285
|
+
>
|
|
286
|
+
|
|
287
|
+
type AgentNoData<D> = 0 extends 1 & D ? false : [D] extends [undefined | void | null] ? true : [D] extends [Event] ? true : false
|
|
288
|
+
type AgentIsAny<D> = 0 extends 1 & D ? true : false
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* One entry of `agent.actions`, typed by the model's data type D for that action: no data
|
|
292
|
+
* (undefined, void, null, or the DOM event a click delivers): no `input`; an untyped component:
|
|
293
|
+
* `input` optional; otherwise `input` is required and its schema's output must be assignable to D.
|
|
294
|
+
*/
|
|
295
|
+
export type AgentAction<STATE, D> = {
|
|
296
|
+
/** What the action does, for the model (WebMCP: at most 500 characters) */
|
|
297
|
+
description: string
|
|
298
|
+
/** Ask the user before running it (the chat behavior's `pending`, WebMCP's confirm) */
|
|
299
|
+
consequential?: boolean
|
|
300
|
+
/** Offered only while this is true (cached by state identity) */
|
|
301
|
+
when?: (state: STATE) => boolean
|
|
302
|
+
/** A call that changes nothing is a success (`{ ok: true, unchanged: true }`), not an error (D256) */
|
|
303
|
+
idempotent?: boolean
|
|
304
|
+
} & (AgentNoData<D> extends true ? { input?: never } : AgentIsAny<D> extends true ? { input?: AgentSchema<any> } : { input: AgentSchema<D> })
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* The `agent` static: what agents may do with a component and see of it. Only the listed actions
|
|
308
|
+
* are exposed, as tools `<name>_<action>` (snake case) plus `<name>_read`; a Collection item
|
|
309
|
+
* component's actions are one tool per action with an `id` parameter (`item` when the input has
|
|
310
|
+
* its own `id`).
|
|
311
|
+
*/
|
|
312
|
+
export type AgentDeclaration<STATE = any, ACTIONS = any> = {
|
|
313
|
+
/** Tool name prefix: `todos` -> todos_add, todos_read (letters, digits, _) */
|
|
314
|
+
name: string
|
|
315
|
+
description?: string
|
|
316
|
+
/** What the model sees of this component: in its context after every flush, and in call results */
|
|
317
|
+
read?: (state: STATE) => unknown
|
|
318
|
+
/**
|
|
319
|
+
* A Collection item's label for the model (D258). User text unless `untrusted: false` (G-644):
|
|
320
|
+
* then the key parameter lists ids only and the labels go with the data (the chat behavior's
|
|
321
|
+
* app state as `<name>_labels`, the command bar's decision `state.labels`)
|
|
322
|
+
*/
|
|
323
|
+
label?: (state: STATE) => string
|
|
324
|
+
/**
|
|
325
|
+
* `read` (and `label`) return user-entered strings (WebMCP's untrustedContentHint; the chat behavior names
|
|
326
|
+
* the declaration as untrusted in its app-state block); `false`: only the app's own text. Unset,
|
|
327
|
+
* it is inferred (WebMCP: SYG244): any string is user text except values under keys named `id`,
|
|
328
|
+
* `status`, `type` or `kind`, and values that are one of this declaration's input enum values (G-623)
|
|
329
|
+
*/
|
|
330
|
+
untrusted?: boolean
|
|
331
|
+
actions: keyof ACTIONS extends never
|
|
332
|
+
? { [action: string]: AgentAction<STATE, any> }
|
|
333
|
+
: { [K in keyof ACTIONS]?: AgentAction<STATE, ACTIONS[K]> }
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/** One tool, as an agent sees it */
|
|
337
|
+
export interface AgentTool {
|
|
338
|
+
name: string
|
|
339
|
+
description: string
|
|
340
|
+
/** the input JSON Schema (an object root) */
|
|
341
|
+
inputSchema: Record<string, any>
|
|
342
|
+
annotations: { readOnlyHint?: true; consequentialHint?: true; untrustedContentHint?: true }
|
|
343
|
+
/** a declared tool that isn't offered (SYG240); only in `list({ all: true })` and `t.tools()` */
|
|
344
|
+
error?: string
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/** What a consequential call asks the user about */
|
|
348
|
+
export interface AgentConfirmInfo {
|
|
349
|
+
tool: string
|
|
350
|
+
component: string
|
|
351
|
+
action: string
|
|
352
|
+
description: string
|
|
353
|
+
/** the validated input */
|
|
354
|
+
input: unknown
|
|
355
|
+
/** a Collection item's key and label */
|
|
356
|
+
key?: unknown
|
|
357
|
+
label?: string
|
|
358
|
+
/** The chat behavior's `pending` for a server tool's approval request (AI SDK `needsApproval`): its approval id (`component` is 'server') */
|
|
359
|
+
approvalId?: string
|
|
360
|
+
}
|
|
361
|
+
export type AgentConfirm = boolean | ((info: AgentConfirmInfo) => boolean | Promise<boolean>)
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* A tool call's result, for the model:
|
|
365
|
+
* - `{ ok: true, state }`: done; `state` is the nearest `read` projection (the item's, or its
|
|
366
|
+
* list's); `removed: true` when an item removed itself; `unchanged: true` for an idempotent no-op
|
|
367
|
+
* - `{ ok: false, error, state?, issues?, keys? }`: invalid input (`issues`), an unknown or hidden
|
|
368
|
+
* key (`keys`), not available (`when`), declined, refused (`abort(reason)`), threw, or changed
|
|
369
|
+
* nothing ("already so, or the input matched nothing")
|
|
370
|
+
*/
|
|
371
|
+
export type AgentResult =
|
|
372
|
+
| { ok: true; state?: unknown; removed?: true; unchanged?: true }
|
|
373
|
+
| { ok: false; error: string; state?: unknown; issues?: AgentIssue[]; keys?: unknown[] }
|
|
374
|
+
|
|
375
|
+
export interface AgentToolsOptions {
|
|
376
|
+
/** consequential calls: `true` runs them, a function asks (default: declined) */
|
|
377
|
+
confirm?: AgentConfirm
|
|
378
|
+
/** run calls one at a time, in call order (default true, D260) */
|
|
379
|
+
serial?: boolean
|
|
380
|
+
/** coerce numeric / boolean strings where the schema says so (default true, D264) */
|
|
381
|
+
repair?: boolean
|
|
382
|
+
/** only this instance (an InstanceView or its id) and its descendants */
|
|
383
|
+
from?: unknown
|
|
384
|
+
/** only these components' declarations */
|
|
385
|
+
components?: Function[]
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/** The live tool set `agentTools()` returns */
|
|
389
|
+
export interface AgentToolSet {
|
|
390
|
+
/** the tools offered now; `{ all: true }` adds declared tools that can't be offered, with `error` */
|
|
391
|
+
list(options?: { all?: boolean }): AgentTool[]
|
|
392
|
+
/** run a tool as an agent (cause 'agent'); resolves after the action's flush */
|
|
393
|
+
call(name: string, args?: unknown, options?: { confirm?: AgentConfirm }): Promise<AgentResult>
|
|
394
|
+
/** the `read` projections by declaration name (item declarations: arrays with `id`) */
|
|
395
|
+
context(): Record<string, unknown>
|
|
396
|
+
/** called with the tools and the context after each flush that changed either; returns unsubscribe */
|
|
397
|
+
subscribe(listener: (change: { tools: AgentTool[]; context: Record<string, unknown> }) => void): () => void
|
|
398
|
+
/** detach from the app */
|
|
399
|
+
stop(): void
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* The agent tools of an app's shown components (their `agent` statics): the building block of
|
|
404
|
+
* the chat behavior, WebMCP and `t.tools()`. Pass the `run()` result (followed through HMR) or a
|
|
405
|
+
* runtime (`app.__runtime`). Every call runs as an action with cause 'agent', through the same
|
|
406
|
+
* queue, reducers and flush as a click.
|
|
407
|
+
*/
|
|
408
|
+
export function agentTools(app: unknown, options?: AgentToolsOptions): AgentToolSet
|
|
409
|
+
// ---- L-1: the chat driver (1-L) -------------------------------------------------------------
|
|
410
|
+
|
|
411
|
+
/** A Standard Schema (https://standardschema.dev), as `output` takes it; Standard JSON Schema lets transports send it */
|
|
412
|
+
export type ChatOutputSchema<OUT = unknown> = {
|
|
413
|
+
readonly '~standard': {
|
|
414
|
+
validate: (value: unknown) => unknown;
|
|
415
|
+
readonly types?: { readonly input: unknown; readonly output: OUT };
|
|
416
|
+
[key: string]: any;
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/** The validated type of an `output` schema */
|
|
421
|
+
export type ChatOutputOf<S> = S extends { readonly '~standard': { readonly types?: infer T } }
|
|
422
|
+
? NonNullable<T> extends { readonly output: infer O } ? O : unknown
|
|
423
|
+
: unknown
|
|
424
|
+
|
|
425
|
+
/** A tool offered to the model (the chat behavior builds these from `agent` declarations) */
|
|
426
|
+
export type ChatTool = {
|
|
427
|
+
description?: string;
|
|
428
|
+
/** The input-side JSON Schema of the tool's arguments (an object schema) */
|
|
429
|
+
inputSchema?: Record<string, unknown>;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* A request to a makeChatDriver() sink. The driver passes it to the transport as is, so a
|
|
434
|
+
* transport's own options go here too. The reply keys name the sender's actions:
|
|
435
|
+
*
|
|
436
|
+
* SEND: { LLM: (state) => ({ messages: state.messages, key: 'reply', delta: 'DELTA', ok: 'DONE', error: 'FAILED' }) },
|
|
437
|
+
* DELTA: (state, { text }) => ({ ...state, draft: text }), // ChatDelta
|
|
438
|
+
* DONE: (state, { message }) => ({ ...state, messages: [...state.messages, message], draft: '' }), // ChatOk
|
|
439
|
+
* FAILED: (state, { error }) => ({ ...state, error: error.message }), // ChatError
|
|
440
|
+
* STOP: { LLM: () => ({ abort: 'reply' }) },
|
|
441
|
+
*/
|
|
442
|
+
export type ChatRequest<OUT = unknown> = {
|
|
443
|
+
/** The conversation so far */
|
|
444
|
+
messages: Message[];
|
|
445
|
+
/** System instructions */
|
|
446
|
+
instructions?: string;
|
|
447
|
+
/** Tools the model may call, by name */
|
|
448
|
+
tools?: Record<string, ChatTool>;
|
|
449
|
+
/** A model id, overriding the transport's */
|
|
450
|
+
model?: string;
|
|
451
|
+
/**
|
|
452
|
+
* Structured output: a Standard Schema with Standard JSON Schema (Zod 4.2+, ArkType, Valibot via
|
|
453
|
+
* toStandardJsonSchema()). The transport sends its input-side JSON Schema (`outputJsonSchema`);
|
|
454
|
+
* `ok` gets the validated `value`, a failure is the `error` reply with `issues`
|
|
455
|
+
*/
|
|
456
|
+
output?: ChatOutputSchema<OUT>;
|
|
457
|
+
/** The request's key for `latest` and `abort` (default: `ok`, else `error`) */
|
|
458
|
+
key?: string;
|
|
459
|
+
/** A new request aborts the sender's request under the same key (default true) */
|
|
460
|
+
latest?: boolean;
|
|
461
|
+
/** Action for streamed text and reasoning (ChatDelta), coalesced per `coalesce` */
|
|
462
|
+
delta?: string;
|
|
463
|
+
/** Action for the finished reply (ChatOk) */
|
|
464
|
+
ok?: string;
|
|
465
|
+
/** Action for a failure (ChatError). Without one a failure is logged (SYG678) */
|
|
466
|
+
error?: string;
|
|
467
|
+
/** Action for each completed tool call (ChatToolCall) */
|
|
468
|
+
tool?: string;
|
|
469
|
+
/**
|
|
470
|
+
* The reply continues the last message, an assistant message (after tool results or an approval,
|
|
471
|
+
* as the AI SDK does): `message` starts with its id and parts plus a `step-start` part; `text`,
|
|
472
|
+
* `toolCalls` and `delta` are the new step's (G-628)
|
|
473
|
+
*/
|
|
474
|
+
continue?: boolean;
|
|
475
|
+
/**
|
|
476
|
+
* How often `delta` fires: 'frame' (default: at most once per animation frame and per 15 ms,
|
|
477
|
+
* about once a second in a hidden tab), 'none' (once per streamed event) or a number of ms
|
|
478
|
+
*/
|
|
479
|
+
coalesce?: 'frame' | 'none' | number;
|
|
480
|
+
/** Not allowed: a `then` key makes the request a thenable (SYG610, not sent). Use `ok` */
|
|
481
|
+
then?: never;
|
|
482
|
+
/** Not allowed (SYG610, not sent). Use `error` */
|
|
483
|
+
catch?: never;
|
|
484
|
+
/** Transport options */
|
|
485
|
+
[option: string]: unknown;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/** Stops the sender's requests under a key (`{ abort: 'reply' }`, `{ abort: true, key: 'reply' }`), or all of them (`{ abort: true }`) */
|
|
489
|
+
export type ChatAbort = { abort: string | true; key?: string }
|
|
490
|
+
|
|
491
|
+
/** `delta` action data: the reply so far */
|
|
492
|
+
export type ChatDelta = {
|
|
493
|
+
key: string;
|
|
494
|
+
/** All the text so far */
|
|
495
|
+
text: string;
|
|
496
|
+
/** All the reasoning so far */
|
|
497
|
+
reasoning: string;
|
|
498
|
+
/** The text that is new since the previous delta ('' when only reasoning grew) */
|
|
499
|
+
delta: string;
|
|
500
|
+
/** The assistant message so far */
|
|
501
|
+
message: Message;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/** A completed tool call */
|
|
505
|
+
export type ChatCall = { id: string; name: string; input: any }
|
|
506
|
+
|
|
507
|
+
/** `tool` action data */
|
|
508
|
+
export type ChatToolCall = { key: string; call: ChatCall }
|
|
509
|
+
|
|
510
|
+
/** `ok` action data: the finished reply */
|
|
511
|
+
export type ChatOk<VALUE = unknown> = {
|
|
512
|
+
key: string;
|
|
513
|
+
/** The assistant message (append it to the conversation) */
|
|
514
|
+
message: Message;
|
|
515
|
+
/** Its text */
|
|
516
|
+
text: string;
|
|
517
|
+
/** The validated structured output (requests with `output`) */
|
|
518
|
+
value?: VALUE;
|
|
519
|
+
toolCalls: ChatCall[];
|
|
520
|
+
/** The transport's finish reason ('stop', 'length', 'tool-calls', 'content-filter', …); default 'stop', or 'tool-calls' after tool calls */
|
|
521
|
+
finishReason: string;
|
|
522
|
+
usage?: unknown;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/** `error` action data */
|
|
526
|
+
export type ChatError = {
|
|
527
|
+
key: string;
|
|
528
|
+
error: Error & { status?: number };
|
|
529
|
+
request: ChatRequest;
|
|
530
|
+
/** Structured output that failed validation (or wasn't JSON) */
|
|
531
|
+
issues?: ReadonlyArray<{ message: string; path?: ReadonlyArray<any> }>;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
/**
|
|
535
|
+
* What a transport yields, in order. Unknown types are ignored; a known type with the wrong shape
|
|
536
|
+
* is skipped (SYG673 in dev). Text and reasoning grow the message's trailing part of that type; a
|
|
537
|
+
* tool call adds a `tool-<name>` part (`state: 'input-available'`), which a later tool-result /
|
|
538
|
+
* tool-error with its id completes; `start` sets the message id.
|
|
539
|
+
*/
|
|
540
|
+
export type ChatEvent =
|
|
541
|
+
| { type: 'start'; id?: string }
|
|
542
|
+
| { type: 'text'; delta: string }
|
|
543
|
+
/** `providerMetadata` closes the reasoning part with it (L-2: an Anthropic thinking signature, to send back) */
|
|
544
|
+
| { type: 'reasoning'; delta: string; providerMetadata?: Record<string, Record<string, unknown>> }
|
|
545
|
+
| { type: 'tool-call'; id?: string; name: string; input?: unknown; /** the server runs it (L-2 uiMessageStream): a message part only, no `tool` reply */ executed?: boolean; providerExecuted?: boolean }
|
|
546
|
+
| { type: 'tool-result'; id: string; output: unknown }
|
|
547
|
+
| { type: 'tool-error'; id: string; error: string }
|
|
548
|
+
/** the server asks the user to approve the call (AI SDK `needsApproval`): the part goes to `approval-requested` */
|
|
549
|
+
| { type: 'tool-approval'; id: string; approval: { id: string; requestReason?: string; [key: string]: unknown } }
|
|
550
|
+
/** the server reports the call denied: the part goes to `output-denied` */
|
|
551
|
+
| { type: 'tool-denied'; id: string }
|
|
552
|
+
| { type: 'data'; name: string; data: unknown; id?: string }
|
|
553
|
+
| { type: `data-${string}`; data: unknown; id?: string }
|
|
554
|
+
| { type: 'file'; mediaType: string; url: string; filename?: string }
|
|
555
|
+
| { type: `source-${string}`; [key: string]: unknown }
|
|
556
|
+
| { type: 'finish'; reason?: string; usage?: unknown }
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* Speaks one wire protocol to the chat driver. `stream` is called once per request, synchronously
|
|
560
|
+
* when the request is sent; stop reading (and abort the fetch) when `signal` aborts. A throw, or a
|
|
561
|
+
* rejected iteration, is the request's failure.
|
|
562
|
+
*/
|
|
563
|
+
export interface ChatTransport {
|
|
564
|
+
stream(request: ChatRequest, signal: AbortSignal): AsyncIterable<ChatEvent>;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
export type ChatDriverOptions = {
|
|
568
|
+
transport: ChatTransport;
|
|
569
|
+
/** The default `coalesce` of requests (default 'frame') */
|
|
570
|
+
coalesce?: 'frame' | 'none' | number;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/** The chat source: reply actions only (no select()) */
|
|
574
|
+
export type ChatSource = { readonly __sygnalReplies: true; dispose(): void; [key: string]: any }
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* makeChatDriver({ transport }): an LLM chat driver with reply actions, like makeFetchDriver.
|
|
578
|
+
* Requests are ChatRequest / ChatAbort sink values; replies reach the sending component only.
|
|
579
|
+
* `latest` per (sender, key); a removed component's and the app's streams are aborted, and an
|
|
580
|
+
* aborted stream delivers nothing.
|
|
581
|
+
*/
|
|
582
|
+
export function makeChatDriver(options: ChatDriverOptions): (sink$: Stream<ChatRequest | ChatAbort>) => ChatSource
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* The JSON Schema a transport sends for a request's `output`: the input side of its Standard
|
|
586
|
+
* JSON Schema, a non-object root wrapped as `{ value }` (`wrapped: true`; the driver unwraps the
|
|
587
|
+
* reply). Undefined when the schema has no JSON Schema form.
|
|
588
|
+
*/
|
|
589
|
+
export function outputJsonSchema(output: ChatOutputSchema | undefined): { schema: Record<string, unknown>; wrapped: boolean } | undefined
|
|
590
|
+
|
|
591
|
+
// ---- A-2: WebMCP (2-W; experimental, D241: tracks the draft, outside semver) --------------------
|
|
592
|
+
|
|
593
|
+
export interface ExposeWebMcpOptions {
|
|
594
|
+
/** origins the tools are exposed to (passed to `registerTool` where the browser takes it) */
|
|
595
|
+
exposedTo?: string[]
|
|
596
|
+
/** consequential calls: `true` runs them, `false` declines, a function asks (default: a native modal `<dialog>`) */
|
|
597
|
+
confirm?: AgentConfirm
|
|
598
|
+
/** prepended to every tool name (`shop_` -> shop_todos_add) */
|
|
599
|
+
prefix?: string
|
|
600
|
+
/** the WebMCP context (default `document.modelContext ?? navigator.modelContext`) */
|
|
601
|
+
modelContext?: unknown
|
|
602
|
+
}
|
|
603
|
+
/** Stops exposing the tools (unregisters them); `available` is false where there is no WebMCP */
|
|
604
|
+
export type WebMcpHandle = (() => void) & { readonly available: boolean }
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Experimental: offer an app's agent tools (its components' `agent` statics, as `agentTools()`
|
|
608
|
+
* sees them) to the browser's agent through WebMCP (`document.modelContext`). Each tool is
|
|
609
|
+
* registered with `registerTool` and re-registered when it changes (a `when`, the live Collection
|
|
610
|
+
* keys, the state summary in its description); results are always objects within Chrome's
|
|
611
|
+
* budgets. A no-op where WebMCP is missing.
|
|
612
|
+
*/
|
|
613
|
+
export function experimentalExposeWebMcp(app: unknown, options?: ExposeWebMcpOptions): WebMcpHandle
|
|
614
|
+
|
|
615
|
+
// ---- A-3: form(…, { tool: formTool() }) (3-A3; experimental, D241; pay per use, D291) --------
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* **Experimental** (D241): a `form`'s declarative WebMCP tool, as `formTool()` takes it.
|
|
619
|
+
* Chrome's budgets: name ≤ 30 characters of `[A-Za-z0-9_.-]`, description ≤ 500.
|
|
620
|
+
*/
|
|
621
|
+
export interface FormTool {
|
|
622
|
+
/** The tool name (the `<form>`'s `toolname`) */
|
|
623
|
+
name: string
|
|
624
|
+
/** What the form does, for the agent (`tooldescription`) */
|
|
625
|
+
description: string
|
|
626
|
+
/**
|
|
627
|
+
* Submit when the agent calls the tool (`toolautosubmit`). Default false (D270): the call fills
|
|
628
|
+
* the fields and stays pending until the user submits the form; then the agent gets the answer
|
|
629
|
+
*/
|
|
630
|
+
autosubmit?: boolean
|
|
631
|
+
}
|
|
632
|
+
/** What `formTool()` returns: the spec plus the hook the form behavior calls (pass it as `tool`) */
|
|
633
|
+
export type FormToolHandle = Readonly<FormTool> & { readonly $: (behavior: unknown, result: unknown) => void }
|
|
634
|
+
/**
|
|
635
|
+
* **Experimental** (D241): what an agent's call of a `form` tool resolves with: the schema's
|
|
636
|
+
* output (what the host's submit action got; a submit sent as a request answers after
|
|
637
|
+
* `form.DONE`), the field errors by name (schema, check and server errors; `''` is form-level), or
|
|
638
|
+
* an `error` when a submit was already running or the form went away first.
|
|
639
|
+
*/
|
|
640
|
+
export type FormToolResult =
|
|
641
|
+
| { ok: true; values: any }
|
|
642
|
+
| { ok: false; errors: FieldErrors }
|
|
643
|
+
| { ok: false; error: string }
|
|
644
|
+
/**
|
|
645
|
+
* **Experimental** (PLAN-6 A-3, D241): the `tool` option of `form()`, which offers the form to
|
|
646
|
+
* the browser's agent as a declarative WebMCP tool:
|
|
647
|
+
*
|
|
648
|
+
* Signup.uses = { form: form(schema, { values, submit: 'SIGN_UP', tool: formTool({ name: 'sign_up', description: 'Create an account' }) }) }
|
|
649
|
+
*
|
|
650
|
+
* The tool code is in this function, so an app using `form` without it doesn't carry it (D291).
|
|
651
|
+
*/
|
|
652
|
+
export function formTool(tool: FormTool): FormToolHandle
|
|
653
|
+
// ---- L-3: the chat behavior (2-C) -----------------------------------------------------------
|
|
654
|
+
|
|
655
|
+
/** 'ready' | 'submitted' (sent, nothing received yet) | 'streaming' (receiving, or running tools) | 'error' (the AI SDK's names) */
|
|
656
|
+
export type ChatStatus = 'ready' | 'submitted' | 'streaming' | 'error'
|
|
657
|
+
|
|
658
|
+
/** The chat behavior's slice: `state.assistant` for `uses = { assistant: chat(...) }` */
|
|
659
|
+
export interface ChatState {
|
|
660
|
+
/** The conversation: the user's messages and the model's replies, with their `tool-<name>` parts */
|
|
661
|
+
messages: Message[]
|
|
662
|
+
/** The prompt field's value */
|
|
663
|
+
prompt: string
|
|
664
|
+
/** The text of the reply being streamed ('' otherwise) */
|
|
665
|
+
draft: string
|
|
666
|
+
/** The reasoning of the reply being streamed ('' otherwise; the finished reply keeps it as `reasoning` parts) */
|
|
667
|
+
draftReasoning: string
|
|
668
|
+
status: ChatStatus
|
|
669
|
+
/** A consequential tool call waiting for APPROVE / DENY (or a server tool's approval request: `approvalId`), else null */
|
|
670
|
+
pending: AgentConfirmInfo | null
|
|
671
|
+
/** The last failure's message, else null */
|
|
672
|
+
error: string | null
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
/** `DONE` action data: the turn is over */
|
|
676
|
+
export interface ChatDone {
|
|
677
|
+
/** The last reply */
|
|
678
|
+
message: Message
|
|
679
|
+
text: string
|
|
680
|
+
finishReason: string
|
|
681
|
+
usage?: unknown
|
|
682
|
+
/** Requests sent in the turn (at most `maxSteps`) */
|
|
683
|
+
steps: number
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/** The chat behavior's actions, as `'<key>.<ACTION>'` on the host */
|
|
687
|
+
export interface ChatActions {
|
|
688
|
+
/** Send the prompt (the form's submit), or the text given as data */
|
|
689
|
+
SEND: string | { text: string } | Event | undefined
|
|
690
|
+
/** Abort the reply being streamed (its text and reasoning so far are kept) and decline a waiting call; calls that already ran keep their results */
|
|
691
|
+
STOP: any
|
|
692
|
+
/** Send the last user message again, dropping the replies after it */
|
|
693
|
+
REGENERATE: any
|
|
694
|
+
/** Run the pending consequential call (a server approval: answered approved, and the conversation sent again) */
|
|
695
|
+
APPROVE: any
|
|
696
|
+
/** Decline the pending consequential call (the model is told the user declined; a server approval: answered not approved) */
|
|
697
|
+
DENY: any
|
|
698
|
+
/** The turn is over (a host entry `'assistant.DONE'` runs after the behavior's) */
|
|
699
|
+
DONE: ChatDone
|
|
700
|
+
/** Internal: the prompt field's input */
|
|
701
|
+
PROMPT: string
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
export interface ChatOptions {
|
|
705
|
+
/** The chat driver's sink (default 'LLM') */
|
|
706
|
+
sink?: string
|
|
707
|
+
/** The form element whose submit sends the prompt */
|
|
708
|
+
form?: BehaviorTarget
|
|
709
|
+
/** The prompt field (its input events set `prompt`) */
|
|
710
|
+
prompt?: BehaviorTarget
|
|
711
|
+
/** Its clicks STOP */
|
|
712
|
+
stop?: BehaviorTarget
|
|
713
|
+
/** Its clicks APPROVE the pending call */
|
|
714
|
+
approve?: BehaviorTarget
|
|
715
|
+
/** Its clicks DENY the pending call */
|
|
716
|
+
deny?: BehaviorTarget
|
|
717
|
+
/** Its clicks REGENERATE */
|
|
718
|
+
regenerate?: BehaviorTarget
|
|
719
|
+
/**
|
|
720
|
+
* System instructions. The tools' `read` projections are not appended: they go as app state, a
|
|
721
|
+
* user-role message of their own right before the last user message on every request (id
|
|
722
|
+
* 'sygnal-app-state', framed as data, not instructions, with the declarations holding
|
|
723
|
+
* user-entered text named; G-631)
|
|
724
|
+
*/
|
|
725
|
+
instructions?: string
|
|
726
|
+
/** A model id for the requests (overrides the transport's) */
|
|
727
|
+
model?: string
|
|
728
|
+
/**
|
|
729
|
+
* Whose `agent` declarations become tools: by default the host's and its shown descendants'
|
|
730
|
+
* (D249); `false`: none; `[Comp, …]`: only these components' (in the host's subtree)
|
|
731
|
+
*/
|
|
732
|
+
agent?: false | Function[]
|
|
733
|
+
/** The most requests per turn (default 8): the tool calls of a reply past it are not run */
|
|
734
|
+
maxSteps?: number
|
|
735
|
+
/** Extra request keys for the transport */
|
|
736
|
+
transportOptions?: Record<string, unknown>
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
/**
|
|
740
|
+
* PLAN-6 L-3: an in-app assistant as a behavior. `uses = { assistant: chat({ form: '.ask',
|
|
741
|
+
* prompt: '.prompt', stop: '.stop', approve: '.approve', deny: '.deny', instructions }) }` gives
|
|
742
|
+
* `state.assistant` (ChatState) and the actions 'assistant.SEND', 'assistant.STOP',
|
|
743
|
+
* 'assistant.REGENERATE', 'assistant.APPROVE', 'assistant.DENY' and 'assistant.DONE'. It sends
|
|
744
|
+
* requests to the chat driver (`sink`), runs the model's tool calls on the host's `agent` tools
|
|
745
|
+
* (and its descendants') through the agent layer (validation, no-op detection, cause 'agent'),
|
|
746
|
+
* asks before consequential ones (`pending`), and loops until a reply without tool calls or
|
|
747
|
+
* `maxSteps`. Its selectors are the host's own: render the markup in the host's view.
|
|
748
|
+
*/
|
|
749
|
+
export function chat(options?: ChatOptions): Behavior<ChatState, ChatActions, {}, ChatOptions>
|
|
750
|
+
// ---- L-2: transports, wave 1 (2-T) ---------------------------------------------------------------
|
|
751
|
+
|
|
752
|
+
/** `ok.usage` from the L-2 transports (AI SDK names) */
|
|
753
|
+
export type ChatUsage = { inputTokens?: number; outputTokens?: number; totalTokens?: number; reasoningTokens?: number }
|
|
754
|
+
|
|
755
|
+
/** Options every HTTP transport takes */
|
|
756
|
+
export interface HttpTransportOptions {
|
|
757
|
+
/** Request headers, or a function of the request (e.g. a fresh session token) */
|
|
758
|
+
headers?: Record<string, string> | ((request: ChatRequest) => Record<string, string> | Promise<Record<string, string>>);
|
|
759
|
+
/** The fetch to use (default: globalThis.fetch): a proxy, a test or demo server, SSR */
|
|
760
|
+
fetch?: (url: string, init: RequestInit) => Promise<Response>;
|
|
761
|
+
/** Extra JSON body fields (a request's own `body` is merged over them) */
|
|
762
|
+
body?: Record<string, unknown>;
|
|
763
|
+
/**
|
|
764
|
+
* In a browser, a request with an auth header (Authorization, x-api-key, ...) to a host that is
|
|
765
|
+
* neither local nor the page's origin is refused (SYG670): the key would ship to every visitor.
|
|
766
|
+
* `true` allows it, for a key the user typed in themselves
|
|
767
|
+
*/
|
|
768
|
+
dangerouslyAllowBrowser?: boolean;
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
export interface OpenResponsesOptions extends HttpTransportOptions {
|
|
772
|
+
/** The API root (default '/v1'); POSTs to `${baseURL}/responses`. Ollama: 'http://localhost:11434/v1' */
|
|
773
|
+
baseURL?: string;
|
|
774
|
+
/** The model (a request's `model` overrides it) */
|
|
775
|
+
model?: string;
|
|
776
|
+
/**
|
|
777
|
+
* OpenAI strict schemas for tools and `output` (default off, D266, D285): pass `strictSchemas`
|
|
778
|
+
* (imported from sygnal/ai, so apps that don't use strict mode don't carry it). Each schema is
|
|
779
|
+
* rewritten to the strict subset (all keys required, optional ones nullable, no extra keys), a
|
|
780
|
+
* schema with no strict form is sent non-strict (SYG675), the nulls are dropped again before
|
|
781
|
+
* validation. `true` is a dev error (SYG672): import strictSchemas
|
|
782
|
+
*/
|
|
783
|
+
strict?: StrictSchemas | false;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
export interface ChatCompletionsOptions extends HttpTransportOptions {
|
|
787
|
+
/** The API root (default '/v1'); POSTs to `${baseURL}/chat/completions` */
|
|
788
|
+
baseURL?: string;
|
|
789
|
+
model?: string;
|
|
790
|
+
/** As openResponses' `strict`: `strictSchemas` */
|
|
791
|
+
strict?: StrictSchemas | false;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
export interface UIMessageStreamOptions extends HttpTransportOptions {}
|
|
795
|
+
|
|
796
|
+
export type ChromePromptStatus = 'available' | 'downloadable' | 'downloading' | 'unavailable'
|
|
797
|
+
|
|
798
|
+
export interface ChromePromptOptions {
|
|
799
|
+
temperature?: number;
|
|
800
|
+
topK?: number;
|
|
801
|
+
expectedInputs?: unknown[];
|
|
802
|
+
expectedOutputs?: unknown[];
|
|
803
|
+
/** Sees the model download's progress (`downloadprogress` events) */
|
|
804
|
+
monitor?: (monitor: EventTarget) => void;
|
|
805
|
+
/** The Prompt API object (default: globalThis.LanguageModel; tests pass a stub) */
|
|
806
|
+
LanguageModel?: unknown;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
export interface ChromePromptTransport extends ChatTransport {
|
|
810
|
+
/**
|
|
811
|
+
* `LanguageModel.availability()`, or 'unavailable' without the API. A component reads it
|
|
812
|
+
* through a driver (G-647): `MODEL: driverFromAsync(transport.status)` in `run()`, and
|
|
813
|
+
* `MODEL: { ok: 'MODEL_STATUS' }` from the model (the reply action gets the status string)
|
|
814
|
+
*/
|
|
815
|
+
status(): Promise<ChromePromptStatus>;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
/**
|
|
819
|
+
* openResponses({ baseURL, model }): a transport for Open Responses / OpenAI Responses SSE
|
|
820
|
+
* (Ollama, vLLM, OpenRouter, OpenAI). Unknown events are ignored, as the spec requires.
|
|
821
|
+
*
|
|
822
|
+
* run(App, { LLM: makeChatDriver({ transport: openResponses({ baseURL: 'http://localhost:11434/v1', model: 'llama3.2' }) }) })
|
|
823
|
+
*/
|
|
824
|
+
export function openResponses(options?: OpenResponsesOptions): ChatTransport
|
|
825
|
+
|
|
826
|
+
/** chatCompletions({ baseURL, model }): a transport for Chat Completions SSE (older local servers) */
|
|
827
|
+
export function chatCompletions(options?: ChatCompletionsOptions): ChatTransport
|
|
828
|
+
|
|
829
|
+
/**
|
|
830
|
+
* uiMessageStream(url): a transport for an AI SDK 7 server route (`toUIMessageStreamResponse()`,
|
|
831
|
+
* UI message stream v1). Messages go as UIMessages; server-run tools, approvals and `data-*`
|
|
832
|
+
* parts come back as message parts. The production default: the provider key stays on the server.
|
|
833
|
+
*/
|
|
834
|
+
export function uiMessageStream(url: string, options?: UIMessageStreamOptions): ChatTransport
|
|
835
|
+
|
|
836
|
+
/**
|
|
837
|
+
* chromePrompt(): a transport for Chrome's on-device Prompt API (`LanguageModel`); `status()` says
|
|
838
|
+
* whether it can run. No tools (the Prompt API has none); `instructions` and every `system`
|
|
839
|
+
* message are joined into the session's one system prompt (G-630)
|
|
840
|
+
*/
|
|
841
|
+
export function chromePrompt(options?: ChromePromptOptions): ChromePromptTransport
|
|
842
|
+
|
|
843
|
+
/** One Open Responses SSE event (`event: type`, `data: JSON`) */
|
|
844
|
+
export interface OpenResponsesEvent { type: string; sequence_number: number; [key: string]: unknown }
|
|
845
|
+
|
|
846
|
+
/**
|
|
847
|
+
* The Open Responses events of a scripted reply (D273), for demo and test servers:
|
|
848
|
+
* chunks as `t.stream` takes them (strings, `{ reasoning }`, `{ toolCall }`, `{ finish }`) or ChatEvents.
|
|
849
|
+
*
|
|
850
|
+
* 'POST /v1/responses': () => ({ sse: encodeOpenResponses(['Hello ', 'there']) })
|
|
851
|
+
*/
|
|
852
|
+
export function encodeOpenResponses(
|
|
853
|
+
chunks: ReadonlyArray<string | { reasoning: string } | { toolCall: { id?: string; name: string; input?: unknown } } | { finish: string | { reason?: string; usage?: unknown } } | ChatEvent>,
|
|
854
|
+
options?: { id?: string; model?: string },
|
|
855
|
+
): OpenResponsesEvent[]
|
|
856
|
+
// ---- M-3: the command bar (3-M) -----------------------------------------------------------------
|
|
857
|
+
|
|
858
|
+
/** 'ready' | 'deciding' (the decision request is out) | 'running' (the action runs or waits for APPROVE / DENY) | 'error' */
|
|
859
|
+
export type CommandBarStatus = 'ready' | 'deciding' | 'running' | 'error'
|
|
860
|
+
|
|
861
|
+
/** Why a command wasn't run (`unsure`, or an escalated `result`) */
|
|
862
|
+
export interface CommandBarUnsure {
|
|
863
|
+
command: string
|
|
864
|
+
/**
|
|
865
|
+
* 'confidence': the action's (or the target's) confidence is below `below`; 'no-action': the
|
|
866
|
+
* model picked none of the actions; 'target': an item action with no clear item; 'input': the
|
|
867
|
+
* action needs an argument the bar can't fill (a chat model can)
|
|
868
|
+
*/
|
|
869
|
+
reason: 'confidence' | 'no-action' | 'target' | 'input'
|
|
870
|
+
/** The tool the model leaned to, and its description */
|
|
871
|
+
tool: string | null
|
|
872
|
+
description: string | null
|
|
873
|
+
/** The Collection item key it leaned to, and its `agent.label` */
|
|
874
|
+
target: unknown
|
|
875
|
+
label: string | null
|
|
876
|
+
confidence: number
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
/** The last outcome: a call's result, or the hand-over to the chat behavior */
|
|
880
|
+
export type CommandBarResult =
|
|
881
|
+
| ({ command: string; tool: string; input: unknown } & AgentResult)
|
|
882
|
+
| (CommandBarUnsure & { escalated: string })
|
|
883
|
+
|
|
884
|
+
/** The command bar's slice: `state.cmd` for `uses = { cmd: commandBar(...) }` */
|
|
885
|
+
export interface CommandBarState {
|
|
886
|
+
/** The input's value (cleared when a command ran) */
|
|
887
|
+
text: string
|
|
888
|
+
status: CommandBarStatus
|
|
889
|
+
/** The command being handled, else null */
|
|
890
|
+
command: string | null
|
|
891
|
+
/** A consequential action waiting for APPROVE / DENY, else null */
|
|
892
|
+
pending: AgentConfirmInfo | null
|
|
893
|
+
/** Why the last command wasn't run (without `escalate`), else null */
|
|
894
|
+
unsure: CommandBarUnsure | null
|
|
895
|
+
result: CommandBarResult | null
|
|
896
|
+
/** The decision request's failure, else null */
|
|
897
|
+
error: string | null
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/** The command bar's actions, as `'<key>.<ACTION>'` on the host */
|
|
901
|
+
export interface CommandBarActions {
|
|
902
|
+
/** Run the input's command (Enter, or the form's submit, or a click of `run`), or the command given as data */
|
|
903
|
+
RUN: string | Event | undefined
|
|
904
|
+
/** Run the pending consequential action */
|
|
905
|
+
APPROVE: any
|
|
906
|
+
/** Decline it */
|
|
907
|
+
DENY: any
|
|
908
|
+
/** A command ran (a host entry `'cmd.DONE'` runs after the behavior's) */
|
|
909
|
+
DONE: { command: string; tool: string; input: unknown } & AgentResult
|
|
910
|
+
/** Internal: the input's value */
|
|
911
|
+
INPUT: string
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
export interface CommandBarOptions {
|
|
915
|
+
/** The command field: its input events set `text`; Enter runs it (unless `form` is given) */
|
|
916
|
+
input: BehaviorTarget
|
|
917
|
+
/** A form whose submit runs the command (instead of Enter in the field) */
|
|
918
|
+
form?: BehaviorTarget
|
|
919
|
+
/** Its clicks run the field's command too (a Go button, D288) */
|
|
920
|
+
run?: BehaviorTarget
|
|
921
|
+
/**
|
|
922
|
+
* The decision request: `decide()` options without `state` / `questions` (`{ url, model }`), or
|
|
923
|
+
* a function building the request (`(q) => decide.openai({ ...q, url, model })`)
|
|
924
|
+
*/
|
|
925
|
+
decide: Omit<DecideOptions<Questions>, 'state' | 'questions'> | ((q: { state: unknown; questions: Questions }) => DecideRequest<any, any> | Record<string, unknown>)
|
|
926
|
+
/** Below this confidence (the action's, and the target's for an item action) nothing runs (default 0.6) */
|
|
927
|
+
below?: number
|
|
928
|
+
/** The `uses` key of a `chat` behavior on the same host: an unsure command goes to its SEND */
|
|
929
|
+
escalate?: string
|
|
930
|
+
/** Whose `agent` actions are offered: by default the host's and its shown descendants' (D249); `[Comp, …]`: only these */
|
|
931
|
+
agent?: Function[]
|
|
932
|
+
/** The fetch driver's sink (default 'HTTP') */
|
|
933
|
+
sink?: string
|
|
934
|
+
/** Its clicks APPROVE the pending action */
|
|
935
|
+
approve?: BehaviorTarget
|
|
936
|
+
/** Its clicks DENY it */
|
|
937
|
+
deny?: BehaviorTarget
|
|
938
|
+
/**
|
|
939
|
+
* The free text argument of a picked action whose input is one string (ADD's text). Default: a
|
|
940
|
+
* heuristic, a quoted part of the command or else the command without its lead-in (fillers, an
|
|
941
|
+
* "add" verb and what follows it, a destination: "remind me to call mom" → "call mom", "put milk
|
|
942
|
+
* on my list" → "milk"; a question gives none, G-634); undefined escalates (or `unsure`, reason 'input')
|
|
943
|
+
*/
|
|
944
|
+
freeText?: (command: string, tool: string) => string | undefined
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/**
|
|
948
|
+
* PLAN-6 M-3: a command bar on a decision model. `uses = { cmd: commandBar({ input: '.command',
|
|
949
|
+
* decide: { url: '/api/decide', model: 'jev-latest' }, below: 0.6, escalate: 'assistant' }) }`:
|
|
950
|
+
* one decision request per command picks the action (a `choice` over the `agent` actions'
|
|
951
|
+
* descriptions; an enum input gives one option per value) and the target (a `choice` over the
|
|
952
|
+
* live Collection item keys; user-entered `agent.label`s go in the decision's `state.labels`, not
|
|
953
|
+
* the criteria, G-644), then runs it through the agent layer
|
|
954
|
+
* (validation, no-op detection, cause 'agent', `pending` for a consequential action). Below
|
|
955
|
+
* `below` confidence it hands the command to the chat behavior named by `escalate`, or sets
|
|
956
|
+
* `unsure`. Its selectors are the host's own: render the field in the host's view.
|
|
957
|
+
*/
|
|
958
|
+
export function commandBar(options: CommandBarOptions): Behavior<CommandBarState, CommandBarActions, {}, CommandBarOptions>
|
|
959
|
+
// ---- L-2: transports, wave 2, and the strict layer (3-W2) ----------------------------------------
|
|
960
|
+
|
|
961
|
+
/** The provider dialect of a strict schema */
|
|
962
|
+
export type StrictDialect = 'openai' | 'anthropic'
|
|
963
|
+
|
|
964
|
+
/**
|
|
965
|
+
* The strict schema layer (D285): pass it as a transport's `strict` option. Rewrites a portable
|
|
966
|
+
* tool / output schema into the provider's strict subset; `errors` when it has none
|
|
967
|
+
*/
|
|
968
|
+
export type StrictSchemas = (schema: Record<string, unknown>, dialect?: StrictDialect) => { schema: Record<string, unknown>; errors: string[]; optional: number; unions: number }
|
|
969
|
+
|
|
970
|
+
/**
|
|
971
|
+
* Strict mode for openResponses, chatCompletions and anthropicMessages, as an import so apps that
|
|
972
|
+
* don't use it don't carry it:
|
|
973
|
+
*
|
|
974
|
+
* openResponses({ baseURL, model, strict: strictSchemas })
|
|
975
|
+
*/
|
|
976
|
+
export const strictSchemas: StrictSchemas
|
|
977
|
+
|
|
978
|
+
export interface AnthropicMessagesOptions extends HttpTransportOptions {
|
|
979
|
+
/** The API root (default '/v1'); POSTs to `${baseURL}/messages`. Ollama: 'http://localhost:11434/v1' */
|
|
980
|
+
baseURL?: string;
|
|
981
|
+
/** The model (a request's `model` overrides it) */
|
|
982
|
+
model?: string;
|
|
983
|
+
/** `max_tokens` (default 16000; a request's `maxTokens` overrides it) */
|
|
984
|
+
maxTokens?: number;
|
|
985
|
+
/**
|
|
986
|
+
* Anthropic strict tool use and structured output (default off): `strictSchemas`. Schemas are
|
|
987
|
+
* rewritten to Anthropic's subset; one without a strict form, or over Anthropic's per-request
|
|
988
|
+
* limits (20 strict tools, 24 optional / 16 union-typed parameters), is sent non-strict (SYG675)
|
|
989
|
+
*/
|
|
990
|
+
strict?: StrictSchemas | false;
|
|
991
|
+
/** Anthropic server tools sent with the request's tools, e.g. `{ type: 'web_search_20260209', name: 'web_search' }`; their calls come back as executed tool parts */
|
|
992
|
+
serverTools?: Array<Record<string, unknown>>;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
/**
|
|
996
|
+
* anthropicMessages({ baseURL, model }): a transport for Anthropic Messages SSE (Claude through
|
|
997
|
+
* your server's proxy, or Ollama's /v1/messages). Thinking comes back as reasoning parts with
|
|
998
|
+
* their signature (sent back on the next request), server tools as executed tool parts.
|
|
999
|
+
* A hosted endpoint from the browser needs `dangerouslyAllowBrowser` (SYG670): proxy it instead.
|
|
1000
|
+
*
|
|
1001
|
+
* anthropicMessages({ baseURL: '/api/anthropic/v1', model: 'claude-opus-5-5' })
|
|
1002
|
+
*/
|
|
1003
|
+
export function anthropicMessages(options?: AnthropicMessagesOptions): ChatTransport
|
|
1004
|
+
|
|
1005
|
+
export interface AguiOptions extends HttpTransportOptions {
|
|
1006
|
+
/** The AG-UI thread id (a request's `chatId` overrides it; default: one per transport) */
|
|
1007
|
+
threadId?: string;
|
|
1008
|
+
/** The initial agent state (a request's `state` overrides it; later runs send the latest state the agent sent) */
|
|
1009
|
+
state?: unknown;
|
|
1010
|
+
/** AG-UI context entries sent with every run */
|
|
1011
|
+
context?: Array<{ description: string; value: string }>;
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
/**
|
|
1015
|
+
* agui(url): a transport for an AG-UI agent endpoint (TanStack AI, CopilotKit, LangGraph,
|
|
1016
|
+
* Mastra servers). Tool calls the agent answers come back as executed tool parts; STATE_SNAPSHOT
|
|
1017
|
+
* / STATE_DELTA become a `data-agui-state` part (id 'state') holding the agent's current state,
|
|
1018
|
+
* MESSAGES_SNAPSHOT a `data-agui-messages` part, ACTIVITY_SNAPSHOT a `data-agui-activity` part.
|
|
1019
|
+
*/
|
|
1020
|
+
export function agui(url: string, options?: AguiOptions): ChatTransport
|
|
1021
|
+
|
|
1022
|
+
export interface FromAISDKOptions {
|
|
1023
|
+
/** The AI SDK's `streamText` (import { streamText } from 'ai'): sygnal never imports `ai` */
|
|
1024
|
+
streamText: (options: any) => any;
|
|
1025
|
+
/** An AI SDK LanguageModel, used when a request names no `model` */
|
|
1026
|
+
model: unknown;
|
|
1027
|
+
/**
|
|
1028
|
+
* Resolves a request's `model` string (G-637): a function (a provider such as `anthropic`, or
|
|
1029
|
+
* `registry.languageModel`) or a map of ids to LanguageModels. A request `model` string with no
|
|
1030
|
+
* `models`, or one it doesn't resolve, fails the request (never the AI SDK's global provider)
|
|
1031
|
+
*/
|
|
1032
|
+
models?: ((id: string) => unknown) | Record<string, unknown>;
|
|
1033
|
+
/** The AI SDK's `Output`, for requests with `output` */
|
|
1034
|
+
Output?: { object: (options: { schema: any }) => unknown };
|
|
1035
|
+
/** Other streamText settings (temperature, providerOptions, maxRetries, ...) */
|
|
1036
|
+
[setting: string]: unknown;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
1039
|
+
/**
|
|
1040
|
+
* fromAISDK({ streamText, model }): the AI SDK 7 in process as a transport, for SSR, servers
|
|
1041
|
+
* and tests. The provider key is wherever this runs: in a browser use uiMessageStream() instead.
|
|
1042
|
+
*
|
|
1043
|
+
* import { streamText, Output } from 'ai'
|
|
1044
|
+
* makeChatDriver({ transport: fromAISDK({ streamText, Output, model: anthropic('claude-opus-5-5') }) })
|
|
1045
|
+
* // requests may pick another model by id: { model: 'claude-haiku-5-5', ... }
|
|
1046
|
+
* fromAISDK({ streamText, model: anthropic('claude-opus-5-5'), models: anthropic })
|
|
1047
|
+
*/
|
|
1048
|
+
export function fromAISDK(options: FromAISDKOptions): ChatTransport
|
|
1049
|
+
|
|
1050
|
+
// ---------------------------------------------------------------------------------------------
|
|
1051
|
+
// PLAN-6 X-1 (3-X): MCP Apps, the view side (SEP-1865, protocol 2026-01-26)
|
|
1052
|
+
// ---------------------------------------------------------------------------------------------
|
|
1053
|
+
|
|
1054
|
+
export type McpAppDisplayMode = 'inline' | 'fullscreen' | 'pip'
|
|
1055
|
+
/** an MCP content block (text, image, audio, resource, resource_link) */
|
|
1056
|
+
export type McpContentBlock = { type: string; [key: string]: any }
|
|
1057
|
+
/** an MCP CallToolResult: what a server tool returned */
|
|
1058
|
+
export interface McpToolResult {
|
|
1059
|
+
content: McpContentBlock[]
|
|
1060
|
+
structuredContent?: Record<string, any>
|
|
1061
|
+
isError?: boolean
|
|
1062
|
+
_meta?: Record<string, any>
|
|
1063
|
+
[key: string]: any
|
|
1064
|
+
}
|
|
1065
|
+
/** the host context: theme, display mode, container size, locale, ... (merged across changes) */
|
|
1066
|
+
export interface McpHostContext {
|
|
1067
|
+
theme?: 'light' | 'dark'
|
|
1068
|
+
displayMode?: McpAppDisplayMode
|
|
1069
|
+
availableDisplayModes?: McpAppDisplayMode[]
|
|
1070
|
+
locale?: string
|
|
1071
|
+
timeZone?: string
|
|
1072
|
+
platform?: 'web' | 'desktop' | 'mobile'
|
|
1073
|
+
styles?: { variables?: Record<string, string | undefined>; css?: { fonts?: string } }
|
|
1074
|
+
containerDimensions?: Record<string, number | undefined>
|
|
1075
|
+
safeAreaInsets?: { top: number; right: number; bottom: number; left: number }
|
|
1076
|
+
toolInfo?: { id?: string | number; tool: { name: string; [key: string]: any } }
|
|
1077
|
+
[key: string]: any
|
|
1078
|
+
}
|
|
1079
|
+
/** what an `error` reply action of the MCP driver gets */
|
|
1080
|
+
export interface McpAppError {
|
|
1081
|
+
error: string
|
|
1082
|
+
/** the JSON-RPC error code, when the host answered with an error */
|
|
1083
|
+
code?: number
|
|
1084
|
+
/** the result, when it came back with isError: true */
|
|
1085
|
+
result?: McpToolResult
|
|
1086
|
+
request: McpAppRequest
|
|
1087
|
+
}
|
|
1088
|
+
type McpReplies = { ok?: string; error?: string }
|
|
1089
|
+
/** a value for an MCP driver sink (reply actions `ok` / `error` go to the sending component) */
|
|
1090
|
+
export type McpAppRequest =
|
|
1091
|
+
| ({ callTool: string; args?: Record<string, unknown> } & McpReplies)
|
|
1092
|
+
| ({ updateModelContext: string | McpContentBlock[] | Record<string, unknown> } & McpReplies)
|
|
1093
|
+
| ({ message: string | McpContentBlock[] } & McpReplies)
|
|
1094
|
+
| ({ openLink: string } & McpReplies)
|
|
1095
|
+
| ({ displayMode: McpAppDisplayMode } & McpReplies)
|
|
1096
|
+
/** G-641: `ui/download-file` (the host asks the person first); a bare resource contents object is embedded */
|
|
1097
|
+
| ({ downloadFile: McpDownloadItem | McpDownloadItem[] } & McpReplies)
|
|
1098
|
+
/** `resources/read` on the MCP server through the host; ok gets `{ contents }` */
|
|
1099
|
+
| ({ readResource: string | { uri: string; [key: string]: unknown } } & McpReplies)
|
|
1100
|
+
/** `resources/list` through the host; ok gets `{ resources, nextCursor? }` */
|
|
1101
|
+
| ({ listResources: true | { cursor?: string } } & McpReplies)
|
|
1102
|
+
/** `sampling/createMessage`: a completion from the host's model (check `hostCapabilities.sampling`); maxTokens defaults to 1024 */
|
|
1103
|
+
| ({ createMessage: string | McpSamplingRequest } & McpReplies)
|
|
1104
|
+
/** `notifications/message`: a log line for the host (not shown to the model); ok runs once sent */
|
|
1105
|
+
| ({ log: unknown; level?: McpLogLevel; logger?: string } & McpReplies)
|
|
1106
|
+
/** `ui/notifications/request-teardown`: ask the host to remove the view (it may send `teardown`); ok runs once sent */
|
|
1107
|
+
| ({ requestTeardown: true } & McpReplies)
|
|
1108
|
+
|
|
1109
|
+
/** resource contents for `downloadFile`: `{ uri, mimeType?, text }` or `{ uri, mimeType?, blob }` (base64) */
|
|
1110
|
+
export type McpResourceContents = { uri: string; mimeType?: string; text: string; [key: string]: any } | { uri: string; mimeType?: string; blob: string; [key: string]: any }
|
|
1111
|
+
/** an EmbeddedResource / ResourceLink content block, or bare resource contents (embedded for you) */
|
|
1112
|
+
export type McpDownloadItem = McpContentBlock | McpResourceContents
|
|
1113
|
+
export type McpLogLevel = 'debug' | 'info' | 'notice' | 'warning' | 'error' | 'critical' | 'alert' | 'emergency'
|
|
1114
|
+
/** MCP CreateMessageRequest params; `messages` may be one user text */
|
|
1115
|
+
export interface McpSamplingRequest {
|
|
1116
|
+
messages: string | Array<{ role: 'user' | 'assistant'; content: McpContentBlock | McpContentBlock[] }>
|
|
1117
|
+
maxTokens?: number
|
|
1118
|
+
systemPrompt?: string
|
|
1119
|
+
temperature?: number
|
|
1120
|
+
stopSequences?: string[]
|
|
1121
|
+
modelPreferences?: Record<string, any>
|
|
1122
|
+
includeContext?: 'none' | 'thisServer' | 'allServers'
|
|
1123
|
+
metadata?: Record<string, any>
|
|
1124
|
+
tools?: any[]
|
|
1125
|
+
toolChoice?: any
|
|
1126
|
+
[key: string]: any
|
|
1127
|
+
}
|
|
1128
|
+
/** the handshake's answer: `MCP.select('host')` */
|
|
1129
|
+
export interface McpHostInfo {
|
|
1130
|
+
hostInfo?: { name: string; version: string; [key: string]: any }
|
|
1131
|
+
/** openLinks, downloadFile, serverTools, serverResources, logging, sampling, updateModelContext, message, ... */
|
|
1132
|
+
hostCapabilities: Record<string, any>
|
|
1133
|
+
protocolVersion?: string
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
export interface McpAppSource {
|
|
1137
|
+
readonly __sygnalReplies: true
|
|
1138
|
+
/** the arguments the model called the tool with (replayed to a stream that starts later) */
|
|
1139
|
+
select(type: 'tool-input' | 'tool-input-partial'): Stream<Record<string, any>>
|
|
1140
|
+
/** the server tool's result (replayed to a stream that starts later) */
|
|
1141
|
+
select(type: 'tool-result'): Stream<McpToolResult>
|
|
1142
|
+
select(type: 'tool-cancelled'): Stream<{ reason?: string }>
|
|
1143
|
+
/** the whole host context: first the handshake's, then after each change (replayed) */
|
|
1144
|
+
select(type: 'host-context-changed'): Stream<McpHostContext>
|
|
1145
|
+
/** the host is about to remove the view (answered after the actions it caused have run) */
|
|
1146
|
+
select(type: 'teardown'): Stream<{}>
|
|
1147
|
+
/** the handshake's hostInfo, hostCapabilities and protocolVersion (replayed) */
|
|
1148
|
+
select(type: 'host'): Stream<McpHostInfo>
|
|
1149
|
+
dispose(): void
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
export interface McpAppDriverOptions {
|
|
1153
|
+
/** the handshake's `appInfo` (default `{ name: document.title || 'sygnal-app', version: '0.0.0' }`) */
|
|
1154
|
+
appInfo?: { name: string; version: string; [key: string]: any }
|
|
1155
|
+
/** display modes the view supports */
|
|
1156
|
+
availableDisplayModes?: McpAppDisplayMode[]
|
|
1157
|
+
/** report the document's size to the host (`ui/notifications/size-changed`, default true) */
|
|
1158
|
+
autoResize?: boolean
|
|
1159
|
+
/**
|
|
1160
|
+
* `agentTools`: offer the app's `agent` tools (A-1) to the host as the view's own tools
|
|
1161
|
+
* (`appCapabilities.tools`, `tools/list`, `tools/call`, `notifications/tools/list_changed`).
|
|
1162
|
+
* Passed in so an app without it doesn't bundle the agent layer. Call makeMcpAppDriver()
|
|
1163
|
+
* before run(), as in `run(App, { MCP: makeMcpAppDriver({ tools: agentTools }) })`.
|
|
1164
|
+
*/
|
|
1165
|
+
tools?: typeof agentTools
|
|
1166
|
+
/** with `tools`: consequential calls run when it resolves true (default: declined) */
|
|
1167
|
+
confirm?: AgentConfirm
|
|
1168
|
+
/** the host window (default `window.parent`) */
|
|
1169
|
+
host?: { postMessage(message: any, targetOrigin: string): void }
|
|
1170
|
+
/** the window to listen on (default `window`) */
|
|
1171
|
+
window?: any
|
|
1172
|
+
/** the protocol version offered (default '2026-01-26') */
|
|
1173
|
+
protocolVersion?: string
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
/**
|
|
1177
|
+
* makeMcpAppDriver(options?): the MCP Apps bridge as a driver, for a Sygnal app shown by an MCP
|
|
1178
|
+
* host (Claude, ChatGPT, VS Code, ...) in a sandboxed iframe. It does the `ui/initialize`
|
|
1179
|
+
* handshake on start. Source: `MCP.select('tool-input' | 'tool-input-partial' | 'tool-result' |
|
|
1180
|
+
* 'tool-cancelled' | 'host-context-changed' | 'teardown' | 'host')`. Sinks: `{ callTool, args, ok, error }`,
|
|
1181
|
+
* `{ updateModelContext }`, `{ message }`, `{ openLink }`, `{ displayMode }`, `{ downloadFile }`,
|
|
1182
|
+
* `{ readResource }`, `{ listResources }`, `{ createMessage }`, `{ log }`, `{ requestTeardown }`.
|
|
1183
|
+
*
|
|
1184
|
+
* run(WeatherCard, { MCP: makeMcpAppDriver() })
|
|
1185
|
+
*/
|
|
1186
|
+
export function makeMcpAppDriver(options?: McpAppDriverOptions): (sink$: Stream<McpAppRequest>) => McpAppSource
|