@llui/agent 0.0.39 → 0.0.40
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/dist/client/factory.d.ts.map +1 -1
- package/dist/client/factory.js +12 -0
- package/dist/client/factory.js.map +1 -1
- package/dist/client/rpc/describe-context.d.ts +34 -0
- package/dist/client/rpc/describe-context.d.ts.map +1 -1
- package/dist/client/rpc/describe-context.js +42 -3
- package/dist/client/rpc/describe-context.js.map +1 -1
- package/dist/client/rpc/list-actions.d.ts.map +1 -1
- package/dist/client/rpc/list-actions.js +21 -0
- package/dist/client/rpc/list-actions.js.map +1 -1
- package/dist/client/rpc/send-message.d.ts +8 -0
- package/dist/client/rpc/send-message.d.ts.map +1 -1
- package/dist/client/rpc/send-message.js +14 -7
- package/dist/client/rpc/send-message.js.map +1 -1
- package/dist/client/rpc/would-dispatch.d.ts +19 -0
- package/dist/client/rpc/would-dispatch.d.ts.map +1 -1
- package/dist/client/rpc/would-dispatch.js +14 -1
- package/dist/client/rpc/would-dispatch.js.map +1 -1
- package/dist/client/ws-client.d.ts +10 -1
- package/dist/client/ws-client.d.ts.map +1 -1
- package/dist/client/ws-client.js +68 -1
- package/dist/client/ws-client.js.map +1 -1
- package/dist/protocol.d.ts +13 -0
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/client/factory.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/client/factory.ts"],"names":[],"mappings":"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"}
|
package/dist/client/factory.js
CHANGED
|
@@ -58,6 +58,10 @@ export function createAgentClient(opts) {
|
|
|
58
58
|
// decodes incoming agent messages before they hit the reducer. The
|
|
59
59
|
// tagged-value convention is documented in `@llui/agent/codecs`.
|
|
60
60
|
const codecs = opts.codecs ?? makeDefaultCodecs();
|
|
61
|
+
// Track the most recent send_message outcome so `describe_context`
|
|
62
|
+
// can prepend a synthetic hint about it. Apps used to roll their
|
|
63
|
+
// own `lastDispatchError` state field; the framework now owns it.
|
|
64
|
+
let lastDispatchOutcome = null;
|
|
61
65
|
const rpcHost = {
|
|
62
66
|
getState: () => encodeForWire(opts.handle.getState(), codecs),
|
|
63
67
|
send: (m) => opts.handle.send(decodeFromWire(m, codecs)),
|
|
@@ -83,11 +87,18 @@ export function createAgentClient(opts) {
|
|
|
83
87
|
getBindingDescriptors: () => opts.handle.getBindingDescriptors(),
|
|
84
88
|
getAgentAffordances: () => opts.def.agentAffordances ?? null,
|
|
85
89
|
getAgentContext: () => opts.def.agentContext ?? null,
|
|
90
|
+
getLastDispatchOutcome: () => lastDispatchOutcome,
|
|
86
91
|
getRootElement: () => opts.rootElement,
|
|
87
92
|
proposeConfirm: (entry) => {
|
|
88
93
|
opts.handle.send(opts.slices.wrapConfirmMsg({ type: 'Propose', entry }));
|
|
89
94
|
},
|
|
90
95
|
};
|
|
96
|
+
// Exposed so the WS client can update on each send_message reply.
|
|
97
|
+
// Closure scope keeps the field write-protected — only the WS layer
|
|
98
|
+
// mutates it; everyone else reads through the getter above.
|
|
99
|
+
const recordDispatchOutcome = (outcome) => {
|
|
100
|
+
lastDispatchOutcome = outcome;
|
|
101
|
+
};
|
|
91
102
|
const helloBuilder = () => ({
|
|
92
103
|
t: 'hello',
|
|
93
104
|
appName: opts.def.name,
|
|
@@ -118,6 +129,7 @@ export function createAgentClient(opts) {
|
|
|
118
129
|
opts.handle.send(opts.slices.wrapLogMsg({ type: 'Append', entry }));
|
|
119
130
|
}
|
|
120
131
|
: undefined,
|
|
132
|
+
onDispatchOutcome: recordDispatchOutcome,
|
|
121
133
|
});
|
|
122
134
|
ws.addEventListener('open', () => {
|
|
123
135
|
opts.handle.send(opts.slices.wrapConnectMsg({ type: 'WsOpened' }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/client/factory.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AppHandle } from '@llui/dom'\nimport type { AgentEffect } from './effects.js'\nimport type { AgentConfirmState } from './agentConfirm.js'\nimport type {\n AgentDocs,\n AgentContext,\n LapDrainMeta,\n MessageAnnotations,\n MessageSchemaEntry,\n} from '../protocol.js'\nimport { attachWsClient, type WsLike, type RpcHosts } from './ws-client.js'\nimport { createEffectHandler } from './effect-handler.js'\nimport { makeDefaultCodecs, encodeForWire, decodeFromWire, type CodecRegistry } from '../codecs.js'\n\n/**\n * The shape the compiler emits as `__msgSchema`. Mirrors `MsgField`\n * from `@llui/vite-plugin/src/msg-schema.ts`. Three coexisting forms:\n *\n * 1. Bare primitive: `'string' | 'number' | 'boolean' | 'unknown'`\n * and bare enum: `{enum: [...]}` (values may be string, number,\n * or boolean — the compiler preserves the literal kind so JSON\n * round-trips don't lose type info).\n * 2. Bare nested types: `{kind: 'object', shape}` for inline /\n * followed-via-typeIndex shapes; `{kind: 'array', element}` for\n * `T[]` / `readonly T[]` / `Array<T>`; `{kind: 'discriminated-\n * union', discriminant, variants}` for tagged unions of objects\n * (e.g. `Format = {kind:'exact'} | {kind:'range', min, max}`).\n * The synthesizer recurses to build copy-paste-ready nested\n * examples; the validator walks the same tree.\n * 3. Rich descriptor: wraps any of the above with `{optional?,\n * priority?, hint?}` carrying TS optionality and `@should` hints.\n */\nexport type MsgSchemaBareType =\n | string\n | { enum: ReadonlyArray<string | number | boolean> }\n | { kind: 'object'; shape: Record<string, MsgSchemaField> }\n | { kind: 'array'; element: MsgSchemaBareType }\n | {\n kind: 'discriminated-union'\n discriminant: string\n variants: Record<string, Record<string, MsgSchemaField>>\n }\n\nexport type MsgSchemaField =\n | MsgSchemaBareType\n | {\n type: MsgSchemaBareType\n optional?: boolean\n priority?: 'should'\n hint?: string\n /**\n * Boolean JS expression authored with `@validates(\"expr\")` JSDoc.\n * Has `v` bound to the field value at runtime; the validator\n * compiles it lazily with `new Function('v', 'return (' + src +\n * ')')` and caches the function across calls. Use for invariants\n * the type system can't express — numeric ranges, format\n * predicates, length bounds.\n */\n validates?: string\n }\n\nexport type MsgSchemaShape = {\n discriminant: string\n variants: Record<string, Record<string, MsgSchemaField>>\n}\n\ntype ComponentMetadata = {\n __msgSchema?: unknown\n __stateSchema?: unknown\n __msgAnnotations?: Record<string, MessageAnnotations>\n __schemaHash?: string\n name: string\n agentAffordances?: (state: unknown) => Array<{ type: string; [k: string]: unknown }>\n agentDocs?: AgentDocs\n agentContext?: (state: unknown) => AgentContext\n}\n\nexport type CreateAgentClientOpts<State, Msg> = {\n handle: AppHandle\n def: ComponentMetadata\n appVersion?: string\n rootElement: Element | null\n slices: {\n getConnect: (s: State) => unknown\n getConfirm: (s: State) => AgentConfirmState\n wrapConnectMsg: (m: unknown) => Msg\n wrapConfirmMsg: (m: unknown) => Msg\n /**\n * Optional: wrap an agentLog msg so the client-side activity feed\n * mirrors what Claude is doing. If omitted, outbound log-append\n * frames still go to the server, but the local agent.log slice\n * stays empty (the UI won't show activity).\n */\n wrapLogMsg?: (m: unknown) => Msg\n }\n /**\n * Codec registry for non-JSON-safe values (Date, Blob, Map, …)\n * crossing the LAP boundary. Defaults to `makeDefaultCodecs()`\n * which ships `iso-date` and `epoch-millis`. Provide a custom\n * registry to register additional codecs (e.g. `base64-blob` for\n * file uploads). See `@llui/agent/codecs` for the convention.\n */\n codecs?: CodecRegistry\n /**\n * Base path for agent HTTP endpoints. Default: `'/agent'` (matches\n * the canonical paths in `@llui/vite-plugin`'s dev middleware and\n * `@llui/agent/server`). The mint URL, resume URLs, and revoke URL\n * derive from this so consumers don't have to keep them in sync.\n *\n * Override when:\n * - **Cross-origin agent server**: pass the full base, e.g.\n * `'https://api.example.com/agent'` or `'http://localhost:8787/agent'`.\n * - **`@cloudflare/vite-plugin` in dev**: pass `'/cdn-cgi/agent'`\n * because cloudflare-vite shadows non-`/cdn-cgi/*` routes.\n */\n agentBasePath?: string\n}\n\nexport type AgentClient = {\n effectHandler: (effect: AgentEffect) => Promise<void>\n start(): void\n stop(): void\n}\n\nexport function createAgentClient<State, Msg>(\n opts: CreateAgentClientOpts<State, Msg>,\n): AgentClient {\n let ws: WebSocket | null = null\n let wsClient: ReturnType<typeof attachWsClient> | null = null\n let confirmPollTimer: ReturnType<typeof setInterval> | null = null\n let stateSubscription: (() => void) | null = null\n const resolvedConfirms = new Set<string>()\n\n // Drain-error buffer: populated by persistent `window.error` and\n // `window.unhandledrejection` listeners installed on `start()`. The\n // send-message drain loop consumes and clears it per call so the\n // envelope surfaces only errors that fired during that window.\n const drainErrors: LapDrainMeta['errors'] = []\n let errorListenersInstalled = false\n let onErrorEvt: ((e: ErrorEvent) => void) | null = null\n let onRejectionEvt: ((e: PromiseRejectionEvent) => void) | null = null\n\n function installErrorListeners(): void {\n if (errorListenersInstalled) return\n if (typeof window === 'undefined') return\n onErrorEvt = (e: ErrorEvent) => {\n drainErrors.push({\n kind: 'error',\n message: e.message ?? String(e.error ?? 'unknown error'),\n stack: e.error instanceof Error ? e.error.stack : undefined,\n })\n }\n onRejectionEvt = (e: PromiseRejectionEvent) => {\n const r = e.reason\n const message = r instanceof Error ? r.message : typeof r === 'string' ? r : safeStringify(r)\n drainErrors.push({\n kind: 'unhandledrejection',\n message,\n stack: r instanceof Error ? r.stack : undefined,\n })\n }\n window.addEventListener('error', onErrorEvt)\n window.addEventListener('unhandledrejection', onRejectionEvt)\n errorListenersInstalled = true\n }\n\n function removeErrorListeners(): void {\n if (!errorListenersInstalled) return\n if (typeof window === 'undefined') return\n if (onErrorEvt) window.removeEventListener('error', onErrorEvt)\n if (onRejectionEvt) window.removeEventListener('unhandledrejection', onRejectionEvt)\n onErrorEvt = null\n onRejectionEvt = null\n errorListenersInstalled = false\n }\n\n // Codec registry handles non-JSON-safe values (Date, etc.) crossing\n // the LAP boundary. `getState` encodes outgoing snapshots; `send`\n // decodes incoming agent messages before they hit the reducer. The\n // tagged-value convention is documented in `@llui/agent/codecs`.\n const codecs = opts.codecs ?? makeDefaultCodecs()\n\n const rpcHost: RpcHosts = {\n getState: () => encodeForWire(opts.handle.getState(), codecs),\n send: (m) => opts.handle.send(decodeFromWire(m, codecs)),\n flush: () => opts.handle.flush(),\n subscribe: (listener) => opts.handle.subscribe(() => listener()),\n getAndClearDrainErrors: () => drainErrors.splice(0, drainErrors.length),\n getMsgAnnotations: () => opts.def.__msgAnnotations ?? null,\n // The compiler-injected message schema. Used by `list_actions` to\n // synthesize payload examples for `@agentOnly` variants that have\n // no live UI binding — the agent should still see them as\n // affordances even though no human can click them.\n getMsgSchema: () => (opts.def.__msgSchema as MsgSchemaShape | undefined) ?? null,\n // Run the reducer in isolation for `would_dispatch`. Wraps the\n // AppHandle's same-named method so the host doesn't need a direct\n // reference to the live ComponentInstance.\n runReducer: (msg) => opts.handle.runReducer(msg),\n // Live binding descriptors: read from the runtime registry that\n // tracks which Msg variants are dispatchable from currently-mounted\n // event handlers. Empty array when the app wasn't compiled with\n // agent metadata (no tagger pass) or has no view bindings yet —\n // both produce the same \"no live affordances\" signal at the agent\n // layer.\n getBindingDescriptors: () => opts.handle.getBindingDescriptors(),\n getAgentAffordances: () => opts.def.agentAffordances ?? null,\n getAgentContext: () => opts.def.agentContext ?? null,\n getRootElement: () => opts.rootElement,\n proposeConfirm: (entry) => {\n opts.handle.send(opts.slices.wrapConfirmMsg({ type: 'Propose', entry }))\n },\n }\n\n const helloBuilder = () => ({\n t: 'hello' as const,\n appName: opts.def.name,\n appVersion: opts.appVersion ?? '0.0.0',\n msgSchema: (opts.def.__msgSchema ?? {}) as Record<string, MessageSchemaEntry>,\n stateSchema: (opts.def.__stateSchema ?? {}) as object,\n affordancesSample: opts.def.agentAffordances\n ? opts.def.agentAffordances(opts.handle.getState())\n : [],\n docs: opts.def.agentDocs ?? null,\n schemaHash: opts.def.__schemaHash ?? '',\n })\n\n const effectHandler = createEffectHandler({\n send: (m) => opts.handle.send(m),\n wrapAgentConnect: (m) => opts.slices.wrapConnectMsg(m),\n forward: (payload) => opts.handle.send(payload),\n agentBasePath: opts.agentBasePath,\n openWs: (token, wsUrl) => {\n if (ws) ws.close()\n ws = new WebSocket(`${wsUrl}?token=${encodeURIComponent(token)}`)\n wsClient = attachWsClient(ws as unknown as WsLike, rpcHost, helloBuilder, {\n onActivated: () => {\n opts.handle.send(opts.slices.wrapConnectMsg({ type: 'ActivatedByClaude' }))\n },\n onLogEntry: opts.slices.wrapLogMsg\n ? (entry) => {\n opts.handle.send(opts.slices.wrapLogMsg!({ type: 'Append', entry }))\n }\n : undefined,\n })\n ws.addEventListener('open', () => {\n opts.handle.send(opts.slices.wrapConnectMsg({ type: 'WsOpened' }))\n })\n ws.addEventListener('close', () => {\n opts.handle.send(opts.slices.wrapConnectMsg({ type: 'WsClosed' }))\n })\n },\n closeWs: () => {\n wsClient?.close()\n ws = null\n wsClient = null\n },\n })\n\n const pollConfirms = () => {\n const state = opts.handle.getState() as State\n const confirm = opts.slices.getConfirm(state)\n for (const entry of confirm.pending) {\n if (entry.status === 'pending') continue\n if (resolvedConfirms.has(entry.id)) continue\n resolvedConfirms.add(entry.id)\n if (entry.status === 'approved') {\n wsClient?.resolveConfirm(\n entry.id,\n 'confirmed',\n encodeForWire(opts.handle.getState(), codecs),\n )\n } else if (entry.status === 'rejected') {\n wsClient?.resolveConfirm(entry.id, 'user-cancelled')\n }\n }\n }\n\n return {\n effectHandler,\n start() {\n if (!confirmPollTimer) confirmPollTimer = setInterval(pollConfirms, 200)\n if (!stateSubscription) {\n stateSubscription = opts.handle.subscribe((state) => {\n // Same codec convention as `getState`: outgoing snapshots\n // pass through the encoder so non-JSON-safe values (Date,\n // etc.) become tagged-wire form.\n wsClient?.emitStateUpdate('/', encodeForWire(state, codecs))\n })\n }\n installErrorListeners()\n },\n stop() {\n if (confirmPollTimer) clearInterval(confirmPollTimer)\n confirmPollTimer = null\n if (stateSubscription) {\n stateSubscription()\n stateSubscription = null\n }\n removeErrorListeners()\n drainErrors.length = 0\n wsClient?.close()\n },\n }\n}\n\nfunction safeStringify(v: unknown): string {\n try {\n return JSON.stringify(v)\n } catch {\n return String(v)\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/client/factory.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AppHandle } from '@llui/dom'\nimport type { AgentEffect } from './effects.js'\nimport type { AgentConfirmState } from './agentConfirm.js'\nimport type {\n AgentDocs,\n AgentContext,\n LapDrainMeta,\n MessageAnnotations,\n MessageSchemaEntry,\n} from '../protocol.js'\nimport { attachWsClient, type WsLike, type RpcHosts } from './ws-client.js'\nimport { createEffectHandler } from './effect-handler.js'\nimport { makeDefaultCodecs, encodeForWire, decodeFromWire, type CodecRegistry } from '../codecs.js'\n\n/**\n * The shape the compiler emits as `__msgSchema`. Mirrors `MsgField`\n * from `@llui/vite-plugin/src/msg-schema.ts`. Three coexisting forms:\n *\n * 1. Bare primitive: `'string' | 'number' | 'boolean' | 'unknown'`\n * and bare enum: `{enum: [...]}` (values may be string, number,\n * or boolean — the compiler preserves the literal kind so JSON\n * round-trips don't lose type info).\n * 2. Bare nested types: `{kind: 'object', shape}` for inline /\n * followed-via-typeIndex shapes; `{kind: 'array', element}` for\n * `T[]` / `readonly T[]` / `Array<T>`; `{kind: 'discriminated-\n * union', discriminant, variants}` for tagged unions of objects\n * (e.g. `Format = {kind:'exact'} | {kind:'range', min, max}`).\n * The synthesizer recurses to build copy-paste-ready nested\n * examples; the validator walks the same tree.\n * 3. Rich descriptor: wraps any of the above with `{optional?,\n * priority?, hint?}` carrying TS optionality and `@should` hints.\n */\nexport type MsgSchemaBareType =\n | string\n | { enum: ReadonlyArray<string | number | boolean> }\n | { kind: 'object'; shape: Record<string, MsgSchemaField> }\n | { kind: 'array'; element: MsgSchemaBareType }\n | {\n kind: 'discriminated-union'\n discriminant: string\n variants: Record<string, Record<string, MsgSchemaField>>\n }\n\nexport type MsgSchemaField =\n | MsgSchemaBareType\n | {\n type: MsgSchemaBareType\n optional?: boolean\n priority?: 'should'\n hint?: string\n /**\n * Boolean JS expression authored with `@validates(\"expr\")` JSDoc.\n * Has `v` bound to the field value at runtime; the validator\n * compiles it lazily with `new Function('v', 'return (' + src +\n * ')')` and caches the function across calls. Use for invariants\n * the type system can't express — numeric ranges, format\n * predicates, length bounds.\n */\n validates?: string\n }\n\nexport type MsgSchemaShape = {\n discriminant: string\n variants: Record<string, Record<string, MsgSchemaField>>\n}\n\ntype ComponentMetadata = {\n __msgSchema?: unknown\n __stateSchema?: unknown\n __msgAnnotations?: Record<string, MessageAnnotations>\n __schemaHash?: string\n name: string\n agentAffordances?: (state: unknown) => Array<{ type: string; [k: string]: unknown }>\n agentDocs?: AgentDocs\n agentContext?: (state: unknown) => AgentContext\n}\n\nexport type CreateAgentClientOpts<State, Msg> = {\n handle: AppHandle\n def: ComponentMetadata\n appVersion?: string\n rootElement: Element | null\n slices: {\n getConnect: (s: State) => unknown\n getConfirm: (s: State) => AgentConfirmState\n wrapConnectMsg: (m: unknown) => Msg\n wrapConfirmMsg: (m: unknown) => Msg\n /**\n * Optional: wrap an agentLog msg so the client-side activity feed\n * mirrors what Claude is doing. If omitted, outbound log-append\n * frames still go to the server, but the local agent.log slice\n * stays empty (the UI won't show activity).\n */\n wrapLogMsg?: (m: unknown) => Msg\n }\n /**\n * Codec registry for non-JSON-safe values (Date, Blob, Map, …)\n * crossing the LAP boundary. Defaults to `makeDefaultCodecs()`\n * which ships `iso-date` and `epoch-millis`. Provide a custom\n * registry to register additional codecs (e.g. `base64-blob` for\n * file uploads). See `@llui/agent/codecs` for the convention.\n */\n codecs?: CodecRegistry\n /**\n * Base path for agent HTTP endpoints. Default: `'/agent'` (matches\n * the canonical paths in `@llui/vite-plugin`'s dev middleware and\n * `@llui/agent/server`). The mint URL, resume URLs, and revoke URL\n * derive from this so consumers don't have to keep them in sync.\n *\n * Override when:\n * - **Cross-origin agent server**: pass the full base, e.g.\n * `'https://api.example.com/agent'` or `'http://localhost:8787/agent'`.\n * - **`@cloudflare/vite-plugin` in dev**: pass `'/cdn-cgi/agent'`\n * because cloudflare-vite shadows non-`/cdn-cgi/*` routes.\n */\n agentBasePath?: string\n}\n\nexport type AgentClient = {\n effectHandler: (effect: AgentEffect) => Promise<void>\n start(): void\n stop(): void\n}\n\nexport function createAgentClient<State, Msg>(\n opts: CreateAgentClientOpts<State, Msg>,\n): AgentClient {\n let ws: WebSocket | null = null\n let wsClient: ReturnType<typeof attachWsClient> | null = null\n let confirmPollTimer: ReturnType<typeof setInterval> | null = null\n let stateSubscription: (() => void) | null = null\n const resolvedConfirms = new Set<string>()\n\n // Drain-error buffer: populated by persistent `window.error` and\n // `window.unhandledrejection` listeners installed on `start()`. The\n // send-message drain loop consumes and clears it per call so the\n // envelope surfaces only errors that fired during that window.\n const drainErrors: LapDrainMeta['errors'] = []\n let errorListenersInstalled = false\n let onErrorEvt: ((e: ErrorEvent) => void) | null = null\n let onRejectionEvt: ((e: PromiseRejectionEvent) => void) | null = null\n\n function installErrorListeners(): void {\n if (errorListenersInstalled) return\n if (typeof window === 'undefined') return\n onErrorEvt = (e: ErrorEvent) => {\n drainErrors.push({\n kind: 'error',\n message: e.message ?? String(e.error ?? 'unknown error'),\n stack: e.error instanceof Error ? e.error.stack : undefined,\n })\n }\n onRejectionEvt = (e: PromiseRejectionEvent) => {\n const r = e.reason\n const message = r instanceof Error ? r.message : typeof r === 'string' ? r : safeStringify(r)\n drainErrors.push({\n kind: 'unhandledrejection',\n message,\n stack: r instanceof Error ? r.stack : undefined,\n })\n }\n window.addEventListener('error', onErrorEvt)\n window.addEventListener('unhandledrejection', onRejectionEvt)\n errorListenersInstalled = true\n }\n\n function removeErrorListeners(): void {\n if (!errorListenersInstalled) return\n if (typeof window === 'undefined') return\n if (onErrorEvt) window.removeEventListener('error', onErrorEvt)\n if (onRejectionEvt) window.removeEventListener('unhandledrejection', onRejectionEvt)\n onErrorEvt = null\n onRejectionEvt = null\n errorListenersInstalled = false\n }\n\n // Codec registry handles non-JSON-safe values (Date, etc.) crossing\n // the LAP boundary. `getState` encodes outgoing snapshots; `send`\n // decodes incoming agent messages before they hit the reducer. The\n // tagged-value convention is documented in `@llui/agent/codecs`.\n const codecs = opts.codecs ?? makeDefaultCodecs()\n\n // Track the most recent send_message outcome so `describe_context`\n // can prepend a synthetic hint about it. Apps used to roll their\n // own `lastDispatchError` state field; the framework now owns it.\n let lastDispatchOutcome: import('./rpc/describe-context.js').LastDispatchOutcome | null = null\n\n const rpcHost: RpcHosts = {\n getState: () => encodeForWire(opts.handle.getState(), codecs),\n send: (m) => opts.handle.send(decodeFromWire(m, codecs)),\n flush: () => opts.handle.flush(),\n subscribe: (listener) => opts.handle.subscribe(() => listener()),\n getAndClearDrainErrors: () => drainErrors.splice(0, drainErrors.length),\n getMsgAnnotations: () => opts.def.__msgAnnotations ?? null,\n // The compiler-injected message schema. Used by `list_actions` to\n // synthesize payload examples for `@agentOnly` variants that have\n // no live UI binding — the agent should still see them as\n // affordances even though no human can click them.\n getMsgSchema: () => (opts.def.__msgSchema as MsgSchemaShape | undefined) ?? null,\n // Run the reducer in isolation for `would_dispatch`. Wraps the\n // AppHandle's same-named method so the host doesn't need a direct\n // reference to the live ComponentInstance.\n runReducer: (msg) => opts.handle.runReducer(msg),\n // Live binding descriptors: read from the runtime registry that\n // tracks which Msg variants are dispatchable from currently-mounted\n // event handlers. Empty array when the app wasn't compiled with\n // agent metadata (no tagger pass) or has no view bindings yet —\n // both produce the same \"no live affordances\" signal at the agent\n // layer.\n getBindingDescriptors: () => opts.handle.getBindingDescriptors(),\n getAgentAffordances: () => opts.def.agentAffordances ?? null,\n getAgentContext: () => opts.def.agentContext ?? null,\n getLastDispatchOutcome: () => lastDispatchOutcome,\n getRootElement: () => opts.rootElement,\n proposeConfirm: (entry) => {\n opts.handle.send(opts.slices.wrapConfirmMsg({ type: 'Propose', entry }))\n },\n }\n\n // Exposed so the WS client can update on each send_message reply.\n // Closure scope keeps the field write-protected — only the WS layer\n // mutates it; everyone else reads through the getter above.\n const recordDispatchOutcome = (outcome: typeof lastDispatchOutcome): void => {\n lastDispatchOutcome = outcome\n }\n\n const helloBuilder = () => ({\n t: 'hello' as const,\n appName: opts.def.name,\n appVersion: opts.appVersion ?? '0.0.0',\n msgSchema: (opts.def.__msgSchema ?? {}) as Record<string, MessageSchemaEntry>,\n stateSchema: (opts.def.__stateSchema ?? {}) as object,\n affordancesSample: opts.def.agentAffordances\n ? opts.def.agentAffordances(opts.handle.getState())\n : [],\n docs: opts.def.agentDocs ?? null,\n schemaHash: opts.def.__schemaHash ?? '',\n })\n\n const effectHandler = createEffectHandler({\n send: (m) => opts.handle.send(m),\n wrapAgentConnect: (m) => opts.slices.wrapConnectMsg(m),\n forward: (payload) => opts.handle.send(payload),\n agentBasePath: opts.agentBasePath,\n openWs: (token, wsUrl) => {\n if (ws) ws.close()\n ws = new WebSocket(`${wsUrl}?token=${encodeURIComponent(token)}`)\n wsClient = attachWsClient(ws as unknown as WsLike, rpcHost, helloBuilder, {\n onActivated: () => {\n opts.handle.send(opts.slices.wrapConnectMsg({ type: 'ActivatedByClaude' }))\n },\n onLogEntry: opts.slices.wrapLogMsg\n ? (entry) => {\n opts.handle.send(opts.slices.wrapLogMsg!({ type: 'Append', entry }))\n }\n : undefined,\n onDispatchOutcome: recordDispatchOutcome,\n })\n ws.addEventListener('open', () => {\n opts.handle.send(opts.slices.wrapConnectMsg({ type: 'WsOpened' }))\n })\n ws.addEventListener('close', () => {\n opts.handle.send(opts.slices.wrapConnectMsg({ type: 'WsClosed' }))\n })\n },\n closeWs: () => {\n wsClient?.close()\n ws = null\n wsClient = null\n },\n })\n\n const pollConfirms = () => {\n const state = opts.handle.getState() as State\n const confirm = opts.slices.getConfirm(state)\n for (const entry of confirm.pending) {\n if (entry.status === 'pending') continue\n if (resolvedConfirms.has(entry.id)) continue\n resolvedConfirms.add(entry.id)\n if (entry.status === 'approved') {\n wsClient?.resolveConfirm(\n entry.id,\n 'confirmed',\n encodeForWire(opts.handle.getState(), codecs),\n )\n } else if (entry.status === 'rejected') {\n wsClient?.resolveConfirm(entry.id, 'user-cancelled')\n }\n }\n }\n\n return {\n effectHandler,\n start() {\n if (!confirmPollTimer) confirmPollTimer = setInterval(pollConfirms, 200)\n if (!stateSubscription) {\n stateSubscription = opts.handle.subscribe((state) => {\n // Same codec convention as `getState`: outgoing snapshots\n // pass through the encoder so non-JSON-safe values (Date,\n // etc.) become tagged-wire form.\n wsClient?.emitStateUpdate('/', encodeForWire(state, codecs))\n })\n }\n installErrorListeners()\n },\n stop() {\n if (confirmPollTimer) clearInterval(confirmPollTimer)\n confirmPollTimer = null\n if (stateSubscription) {\n stateSubscription()\n stateSubscription = null\n }\n removeErrorListeners()\n drainErrors.length = 0\n wsClient?.close()\n },\n }\n}\n\nfunction safeStringify(v: unknown): string {\n try {\n return JSON.stringify(v)\n } catch {\n return String(v)\n }\n}\n"]}
|
|
@@ -1,7 +1,41 @@
|
|
|
1
1
|
import type { AgentContext } from '../../protocol.js';
|
|
2
|
+
/**
|
|
3
|
+
* Snapshot of the most recent `send_message` outcome — what the agent
|
|
4
|
+
* just did and whether it landed cleanly. Tracked by the WS RPC layer
|
|
5
|
+
* after every dispatch, surfaced through `describe_context` so apps
|
|
6
|
+
* don't have to roll their own `lastDispatchError` field in state.
|
|
7
|
+
*
|
|
8
|
+
* `status: 'dispatched'` means the reducer ran and state advanced;
|
|
9
|
+
* `errors` here describes downstream effects that failed during the
|
|
10
|
+
* drain window (Phase-5 catch-and-report). `status: 'rejected'`
|
|
11
|
+
* means the validator caught a structural issue before the reducer.
|
|
12
|
+
* `status: 'reducer-threw'` means the reducer itself threw — state
|
|
13
|
+
* may be partially advanced, but the dispatch should be treated as
|
|
14
|
+
* a failure for retry purposes.
|
|
15
|
+
*/
|
|
16
|
+
export type LastDispatchOutcome = {
|
|
17
|
+
variant: string;
|
|
18
|
+
status: 'dispatched' | 'rejected' | 'reducer-threw';
|
|
19
|
+
errors?: ReadonlyArray<{
|
|
20
|
+
message: string;
|
|
21
|
+
}>;
|
|
22
|
+
warnings?: ReadonlyArray<{
|
|
23
|
+
path: string;
|
|
24
|
+
message: string;
|
|
25
|
+
}>;
|
|
26
|
+
at: number;
|
|
27
|
+
};
|
|
2
28
|
export type DescribeContextHost = {
|
|
3
29
|
getState(): unknown;
|
|
4
30
|
getAgentContext(): ((state: unknown) => AgentContext) | null;
|
|
31
|
+
/**
|
|
32
|
+
* Optional accessor for the most recent dispatch outcome. When the
|
|
33
|
+
* outcome had errors or warnings, `describe_context` prepends a
|
|
34
|
+
* synthetic hint so the agent reads "Last dispatch X failed/landed
|
|
35
|
+
* with caveats" without the app having to maintain a parallel state
|
|
36
|
+
* field. Lenient: undefined / null accessor disables the feature.
|
|
37
|
+
*/
|
|
38
|
+
getLastDispatchOutcome?: () => LastDispatchOutcome | null;
|
|
5
39
|
};
|
|
6
40
|
export type DescribeContextResult = {
|
|
7
41
|
context: AgentContext;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"describe-context.d.ts","sourceRoot":"","sources":["../../../src/client/rpc/describe-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAErD,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,IAAI,OAAO,CAAA;IACnB,eAAe,IAAI,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,YAAY,CAAC,GAAG,IAAI,CAAA;
|
|
1
|
+
{"version":3,"file":"describe-context.d.ts","sourceRoot":"","sources":["../../../src/client/rpc/describe-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAErD;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,YAAY,GAAG,UAAU,GAAG,eAAe,CAAA;IACnD,MAAM,CAAC,EAAE,aAAa,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC3C,QAAQ,CAAC,EAAE,aAAa,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAC3D,EAAE,EAAE,MAAM,CAAA;CACX,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,IAAI,OAAO,CAAA;IACnB,eAAe,IAAI,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,YAAY,CAAC,GAAG,IAAI,CAAA;IAC5D;;;;;;OAMG;IACH,sBAAsB,CAAC,EAAE,MAAM,mBAAmB,GAAG,IAAI,CAAA;CAC1D,CAAA;AACD,MAAM,MAAM,qBAAqB,GAAG;IAAE,OAAO,EAAE,YAAY,CAAA;CAAE,CAAA;AAI7D,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,mBAAmB,GAAG,qBAAqB,CAWtF"}
|
|
@@ -1,8 +1,47 @@
|
|
|
1
1
|
const EMPTY = { summary: '', hints: [], cautions: [] };
|
|
2
2
|
export function handleDescribeContext(host) {
|
|
3
3
|
const fn = host.getAgentContext();
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
const baseContext = fn ? fn(host.getState()) : EMPTY;
|
|
5
|
+
const outcome = host.getLastDispatchOutcome?.() ?? null;
|
|
6
|
+
const synthetic = outcome ? formatLastOutcomeHint(outcome) : null;
|
|
7
|
+
if (!synthetic)
|
|
8
|
+
return { context: baseContext };
|
|
9
|
+
// Prepend the synthetic hint so it's the first thing the agent sees.
|
|
10
|
+
// The app's own hints follow.
|
|
11
|
+
const hints = [synthetic, ...(baseContext.hints ?? [])];
|
|
12
|
+
return { context: { ...baseContext, hints } };
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Render the outcome as a single hint string. Returns null for clean
|
|
16
|
+
* outcomes — no need to add noise to the context when nothing went
|
|
17
|
+
* wrong on the last dispatch.
|
|
18
|
+
*/
|
|
19
|
+
function formatLastOutcomeHint(outcome) {
|
|
20
|
+
const errors = outcome.errors ?? [];
|
|
21
|
+
const warnings = outcome.warnings ?? [];
|
|
22
|
+
if (outcome.status === 'dispatched' && errors.length === 0 && warnings.length === 0) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
if (outcome.status === 'rejected') {
|
|
26
|
+
const errMsgs = errors.length > 0 ? errors.map((e) => e.message).join('; ') : 'no detail';
|
|
27
|
+
return `LAST DISPATCH REJECTED: ${outcome.variant} — ${errMsgs}. Fix the payload and retry.`;
|
|
28
|
+
}
|
|
29
|
+
if (outcome.status === 'reducer-threw') {
|
|
30
|
+
const msg = errors.length > 0 ? (errors[0]?.message ?? 'reducer threw') : 'reducer threw';
|
|
31
|
+
return `LAST DISPATCH ERRORED MID-FLIGHT: ${outcome.variant} — ${msg}. State may be partially advanced; observe before retrying.`;
|
|
32
|
+
}
|
|
33
|
+
// dispatched with errors / warnings
|
|
34
|
+
const parts = [];
|
|
35
|
+
if (errors.length > 0) {
|
|
36
|
+
parts.push(`${String(errors.length)} downstream error${errors.length === 1 ? '' : 's'}: ${errors
|
|
37
|
+
.map((e) => e.message)
|
|
38
|
+
.join('; ')}`);
|
|
39
|
+
}
|
|
40
|
+
if (warnings.length > 0) {
|
|
41
|
+
parts.push(`${String(warnings.length)} validation warning${warnings.length === 1 ? '' : 's'}: ${warnings
|
|
42
|
+
.map((w) => `${w.path}: ${w.message}`)
|
|
43
|
+
.join('; ')}`);
|
|
44
|
+
}
|
|
45
|
+
return `LAST DISPATCH (${outcome.variant}) landed with caveats — ${parts.join(' / ')}.`;
|
|
7
46
|
}
|
|
8
47
|
//# sourceMappingURL=describe-context.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"describe-context.js","sourceRoot":"","sources":["../../../src/client/rpc/describe-context.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"describe-context.js","sourceRoot":"","sources":["../../../src/client/rpc/describe-context.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentContext } from '../../protocol.js'\n\n/**\n * Snapshot of the most recent `send_message` outcome — what the agent\n * just did and whether it landed cleanly. Tracked by the WS RPC layer\n * after every dispatch, surfaced through `describe_context` so apps\n * don't have to roll their own `lastDispatchError` field in state.\n *\n * `status: 'dispatched'` means the reducer ran and state advanced;\n * `errors` here describes downstream effects that failed during the\n * drain window (Phase-5 catch-and-report). `status: 'rejected'`\n * means the validator caught a structural issue before the reducer.\n * `status: 'reducer-threw'` means the reducer itself threw — state\n * may be partially advanced, but the dispatch should be treated as\n * a failure for retry purposes.\n */\nexport type LastDispatchOutcome = {\n variant: string\n status: 'dispatched' | 'rejected' | 'reducer-threw'\n errors?: ReadonlyArray<{ message: string }>\n warnings?: ReadonlyArray<{ path: string; message: string }>\n at: number\n}\n\nexport type DescribeContextHost = {\n getState(): unknown\n getAgentContext(): ((state: unknown) => AgentContext) | null\n /**\n * Optional accessor for the most recent dispatch outcome. When the\n * outcome had errors or warnings, `describe_context` prepends a\n * synthetic hint so the agent reads \"Last dispatch X failed/landed\n * with caveats\" without the app having to maintain a parallel state\n * field. Lenient: undefined / null accessor disables the feature.\n */\n getLastDispatchOutcome?: () => LastDispatchOutcome | null\n}\nexport type DescribeContextResult = { context: AgentContext }\n\nconst EMPTY: AgentContext = { summary: '', hints: [], cautions: [] }\n\nexport function handleDescribeContext(host: DescribeContextHost): DescribeContextResult {\n const fn = host.getAgentContext()\n const baseContext = fn ? fn(host.getState()) : EMPTY\n const outcome = host.getLastDispatchOutcome?.() ?? null\n const synthetic = outcome ? formatLastOutcomeHint(outcome) : null\n if (!synthetic) return { context: baseContext }\n\n // Prepend the synthetic hint so it's the first thing the agent sees.\n // The app's own hints follow.\n const hints: string[] = [synthetic, ...(baseContext.hints ?? [])]\n return { context: { ...baseContext, hints } }\n}\n\n/**\n * Render the outcome as a single hint string. Returns null for clean\n * outcomes — no need to add noise to the context when nothing went\n * wrong on the last dispatch.\n */\nfunction formatLastOutcomeHint(outcome: LastDispatchOutcome): string | null {\n const errors = outcome.errors ?? []\n const warnings = outcome.warnings ?? []\n if (outcome.status === 'dispatched' && errors.length === 0 && warnings.length === 0) {\n return null\n }\n if (outcome.status === 'rejected') {\n const errMsgs = errors.length > 0 ? errors.map((e) => e.message).join('; ') : 'no detail'\n return `LAST DISPATCH REJECTED: ${outcome.variant} — ${errMsgs}. Fix the payload and retry.`\n }\n if (outcome.status === 'reducer-threw') {\n const msg = errors.length > 0 ? (errors[0]?.message ?? 'reducer threw') : 'reducer threw'\n return `LAST DISPATCH ERRORED MID-FLIGHT: ${outcome.variant} — ${msg}. State may be partially advanced; observe before retrying.`\n }\n // dispatched with errors / warnings\n const parts: string[] = []\n if (errors.length > 0) {\n parts.push(\n `${String(errors.length)} downstream error${errors.length === 1 ? '' : 's'}: ${errors\n .map((e) => e.message)\n .join('; ')}`,\n )\n }\n if (warnings.length > 0) {\n parts.push(\n `${String(warnings.length)} validation warning${warnings.length === 1 ? '' : 's'}: ${warnings\n .map((w) => `${w.path}: ${w.message}`)\n .join('; ')}`,\n )\n }\n return `LAST DISPATCH (${outcome.variant}) landed with caveats — ${parts.join(' / ')}.`\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list-actions.d.ts","sourceRoot":"","sources":["../../../src/client/rpc/list-actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,eAAe,CAAA;AAEnE,KAAK,OAAO,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAClC,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAA;AAErD,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,IAAI,OAAO,CAAA;IACnB,qBAAqB,IAAI,OAAO,EAAE,GAAG,IAAI,CAAA;IACzC,iBAAiB,IAAI,WAAW,GAAG,IAAI,CAAA;IACvC,YAAY,IAAI,cAAc,GAAG,IAAI,CAAA;IACrC,mBAAmB,IAAI,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC,CAAC,GAAG,IAAI,CAAA;CAClG,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,KAAK,CAAC;QACb,OAAO,EAAE,MAAM,CAAA;QACf;;;;;WAKG;QACH,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;QACrB,eAAe,EAAE,OAAO,CAAA;QACxB,YAAY,EAAE,QAAQ,GAAG,YAAY,CAAA;QACrC,MAAM,EAAE,SAAS,GAAG,mBAAmB,GAAG,QAAQ,CAAA;QAClD,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;QAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;QAC1B,sDAAsD;QACtD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;QACtB,gEAAgE;QAChE,QAAQ,EAAE,MAAM,EAAE,CAAA;QAClB;;;;;WAKG;QACH,KAAK,EAAE,MAAM,EAAE,CAAA;QACf;;;;;;;;;;WAUG;QACH,UAAU,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAClD,CAAC,CAAA;CACH,CAAA;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,iBAAiB,
|
|
1
|
+
{"version":3,"file":"list-actions.d.ts","sourceRoot":"","sources":["../../../src/client/rpc/list-actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,eAAe,CAAA;AAEnE,KAAK,OAAO,GAAG;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,CAAA;AAClC,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAA;AAErD,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,IAAI,OAAO,CAAA;IACnB,qBAAqB,IAAI,OAAO,EAAE,GAAG,IAAI,CAAA;IACzC,iBAAiB,IAAI,WAAW,GAAG,IAAI,CAAA;IACvC,YAAY,IAAI,cAAc,GAAG,IAAI,CAAA;IACrC,mBAAmB,IAAI,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC,CAAC,GAAG,IAAI,CAAA;CAClG,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,KAAK,CAAC;QACb,OAAO,EAAE,MAAM,CAAA;QACf;;;;;WAKG;QACH,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;QACrB,eAAe,EAAE,OAAO,CAAA;QACxB,YAAY,EAAE,QAAQ,GAAG,YAAY,CAAA;QACrC,MAAM,EAAE,SAAS,GAAG,mBAAmB,GAAG,QAAQ,CAAA;QAClD,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;QAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;QAC1B,sDAAsD;QACtD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;QACtB,gEAAgE;QAChE,QAAQ,EAAE,MAAM,EAAE,CAAA;QAClB;;;;;WAKG;QACH,KAAK,EAAE,MAAM,EAAE,CAAA;QACf;;;;;;;;;;WAUG;QACH,UAAU,EAAE,KAAK,CAAC;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAClD,CAAC,CAAA;CACH,CAAA;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,iBAAiB,CAgI1E"}
|
|
@@ -10,7 +10,19 @@ export function handleListActions(host) {
|
|
|
10
10
|
// either 'shared' (default) or, in the malformed case where someone
|
|
11
11
|
// bound an `@agentOnly` Msg in a view, 'agent-only'. Either way the
|
|
12
12
|
// agent can dispatch them.
|
|
13
|
+
//
|
|
14
|
+
// Filtered against the Msg schema: a binding whose variant isn't in
|
|
15
|
+
// the user's Msg union is a library-internal Msg leaking through
|
|
16
|
+
// `tagSend` translator wiring (the sortable component's `move`,
|
|
17
|
+
// `drop`, `cancel`, etc. — they're routed into the user's update.ts
|
|
18
|
+
// via a different shape but their lib names slip into the binding
|
|
19
|
+
// registry). The agent has no use for those names — `would_dispatch`
|
|
20
|
+
// / `send_message` would reject them as `unknown-variant` anyway —
|
|
21
|
+
// so they pollute the affordance list. When a schema is available,
|
|
22
|
+
// the schema's variant set is the source of truth.
|
|
13
23
|
for (const d of descriptors) {
|
|
24
|
+
if (schema && !(d.variant in schema.variants))
|
|
25
|
+
continue;
|
|
14
26
|
const ann = annotations[d.variant];
|
|
15
27
|
if (ann?.dispatchMode === 'human-only')
|
|
16
28
|
continue;
|
|
@@ -165,6 +177,15 @@ function walkHint(path, d, out) {
|
|
|
165
177
|
if (typeof d.hint === 'string' && d.hint.length > 0) {
|
|
166
178
|
out.push({ path, hint: d.hint });
|
|
167
179
|
}
|
|
180
|
+
// `@validates(...)` predicates surface alongside `@should` hints
|
|
181
|
+
// so the agent sees the constraint at affordance time rather than
|
|
182
|
+
// only as a post-dispatch rejection. The verbatim predicate text
|
|
183
|
+
// is what the runtime evaluates; agents trained on JS read it
|
|
184
|
+
// directly. Prefix `validates: ` to disambiguate from freeform
|
|
185
|
+
// `@should` text.
|
|
186
|
+
if (typeof d.validates === 'string' && d.validates.length > 0) {
|
|
187
|
+
out.push({ path, hint: `validates: ${d.validates}` });
|
|
188
|
+
}
|
|
168
189
|
walkHintBare(path, d.type, out);
|
|
169
190
|
return;
|
|
170
191
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"list-actions.js","sourceRoot":"","sources":["../../../src/client/rpc/list-actions.ts"],"names":[],"mappings":"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","sourcesContent":["import type { MessageAnnotations } from '../../protocol.js'\nimport type { MsgSchemaShape, MsgSchemaField } from '../factory.js'\n\ntype Binding = { variant: string }\ntype Annotations = Record<string, MessageAnnotations>\n\nexport type ListActionsHost = {\n getState(): unknown\n getBindingDescriptors(): Binding[] | null\n getMsgAnnotations(): Annotations | null\n getMsgSchema(): MsgSchemaShape | null\n getAgentAffordances(): ((state: unknown) => Array<{ type: string; [k: string]: unknown }>) | null\n}\n\n/**\n * `dispatchMode` on each action is `'shared'` (human can also click via\n * a UI affordance) or `'agent-only'` (no UI binding — agent is the only\n * dispatcher). `'human-only'` variants are filtered out before this\n * point — they never reach the LLM.\n *\n * `source` distinguishes WHERE the affordance came from:\n * - `'binding'` — a tagged event handler is currently mounted in a\n * live scope (refcount > 0). Variants inside dead branches —\n * `show({when: false})`, unmounted `branch()` cases, removed `each`\n * items — auto-vanish from this set as their lifetimes dispose.\n * This is the framework's \"what can the user click right now\"\n * answer, and it's the default surface for the agent.\n * - `'always-affordable'` — either the app's `agentAffordances(state)`\n * hook listed the variant, or the variant carries the\n * `@alwaysAffordable` JSDoc tag. Both are the explicit \"agent can\n * reach this even when no live UI binding maps to it\" knob — bulk\n * seed ops (`Matrix/AddAlternatives`) and similar agent-driven\n * paths typically land here.\n * - `'schema'` — variant is annotated `@agentOnly` (the canonical\n * \"no UI button maps to this; the agent is the only dispatcher\")\n * and isn't already covered above. The payload is schema-synthesized\n * and the agent fills it in.\n *\n * `'shared'` variants WITHOUT a live binding, without\n * `agentAffordances` mention, and without `@alwaysAffordable` are\n * **deliberately hidden**. They're reachable through UI navigation —\n * the human user can't click them right now, and dispatching them\n * blindly would mutate state that drives `show()`/`branch()` gates,\n * popping hidden UI subtrees into view in places the user didn't\n * navigate to.\n */\nexport type ListActionsResult = {\n actions: Array<{\n variant: string\n /**\n * Human-readable phrase from `@intent(\"…\")`, or `null` when the\n * variant is unannotated. Mirror of LapActionsResponse.intent —\n * callers should treat `null` as a documentation gap and not as\n * \"missing label, fall back to variant name\".\n */\n intent: string | null\n requiresConfirm: boolean\n dispatchMode: 'shared' | 'agent-only'\n source: 'binding' | 'always-affordable' | 'schema'\n selectorHint: string | null\n payloadHint: object | null\n /** Cautionary text from `@warning` JSDoc, or null. */\n warning: string | null\n /** Concrete examples from `@example` JSDoc, in source order. */\n examples: string[]\n /**\n * Effect kinds this variant emits, from `@emits(\"k1\", \"k2\")`.\n * Empty when not annotated. Lets the agent know what side\n * effects fire — useful for batching (\"100 dispatches × cloud-\n * save = bad\") and for confirming destructive flows.\n */\n emits: string[]\n /**\n * Per-field guidance lifted from `@should(\"…\")` JSDoc on payload\n * fields. Path is dot/bracket notation rooted at the payload\n * (e.g. `\"cells\"` for a top-level field, `\"cells[].meta\"` for an\n * array element's nested field). Useful when the field is typed\n * as `unknown` or as a polymorphic shape — the hint says \"type\n * matches the criterion's kind: number for quantity, …\" so the\n * agent doesn't have to guess from the bare schema.\n *\n * Empty when no field on this variant carries an `@should` hint.\n */\n fieldHints: Array<{ path: string; hint: string }>\n }>\n}\n\nexport function handleListActions(host: ListActionsHost): ListActionsResult {\n const annotations = host.getMsgAnnotations() ?? {}\n const state = host.getState()\n const descriptors = host.getBindingDescriptors() ?? []\n const affordances = host.getAgentAffordances()?.(state) ?? []\n const schema = host.getMsgSchema()\n\n const out: ListActionsResult['actions'] = []\n const seen = new Set<string>()\n\n // From bindings — these have UI affordances by definition, so they're\n // either 'shared' (default) or, in the malformed case where someone\n // bound an `@agentOnly` Msg in a view, 'agent-only'. Either way the\n // agent can dispatch them.\n for (const d of descriptors) {\n const ann = annotations[d.variant]\n if (ann?.dispatchMode === 'human-only') continue\n seen.add(d.variant)\n const variantSchema = schema?.variants[d.variant]\n out.push({\n variant: d.variant,\n intent: ann?.intent ?? null,\n requiresConfirm: ann?.requiresConfirm ?? false,\n dispatchMode: ann?.dispatchMode === 'agent-only' ? 'agent-only' : 'shared',\n source: 'binding',\n selectorHint: null,\n payloadHint: null,\n warning: ann?.warning ?? null,\n examples: ann?.examples ?? [],\n emits: ann?.emits ?? [],\n fieldHints: variantSchema ? collectFieldHints(variantSchema) : [],\n })\n }\n\n // From `agentAffordances(state)` — the integrator's explicit list of\n // currently-reachable Msgs, with concrete payloads they author.\n for (const msg of affordances) {\n const ann = annotations[msg.type]\n if (ann?.dispatchMode === 'human-only') continue\n seen.add(msg.type)\n const { type, ...rest } = msg\n const variantSchema = schema?.variants[type]\n out.push({\n variant: type,\n intent: ann?.intent ?? null,\n requiresConfirm: ann?.requiresConfirm ?? false,\n dispatchMode: ann?.dispatchMode === 'agent-only' ? 'agent-only' : 'shared',\n source: 'always-affordable',\n selectorHint: null,\n payloadHint: Object.keys(rest).length > 0 ? rest : null,\n warning: ann?.warning ?? null,\n examples: ann?.examples ?? [],\n emits: ann?.emits ?? [],\n fieldHints: variantSchema ? collectFieldHints(variantSchema) : [],\n })\n }\n\n // From `@alwaysAffordable` annotations — the per-variant equivalent\n // of `agentAffordances`. Variants tagged this way surface regardless\n // of whether a live binding maps to them, so bulk seed ops\n // (`Matrix/AddAlternatives`) and similar agent-driven paths are\n // available even when their UI counterparts (if any) aren't mounted.\n // The payload is schema-synthesized — `agentAffordances` is the way\n // to ship a concrete pre-filled payload alongside the affordance.\n for (const [variant, ann] of Object.entries(annotations)) {\n if (seen.has(variant)) continue\n if (ann.dispatchMode === 'human-only') continue\n if (!ann.alwaysAffordable) continue\n seen.add(variant)\n const fields = schema?.variants[variant]\n out.push({\n variant,\n intent: ann.intent,\n requiresConfirm: ann.requiresConfirm,\n dispatchMode: ann.dispatchMode === 'agent-only' ? 'agent-only' : 'shared',\n source: 'always-affordable',\n selectorHint: null,\n payloadHint: fields ? synthesizePayload(variant, fields) : null,\n warning: ann.warning,\n examples: ann.examples,\n emits: ann.emits,\n fieldHints: fields ? collectFieldHints(fields) : [],\n })\n }\n\n // From schema — variants that aren't already surfaced above and that\n // the author marked `@agentOnly`. The canonical \"no UI button maps\n // to this; the agent is the only dispatcher.\" Bulk edits, imports,\n // admin operations that have no human-facing affordance at all.\n //\n // `'shared'` variants WITHOUT a live binding stay hidden here — they\n // are reachable through UI navigation, and dispatching them while\n // their UI subtree is unmounted would pop hidden state in places the\n // user didn't navigate to. The explicit knobs are `@alwaysAffordable`\n // (handled above) or `agentAffordances(state)`.\n if (schema) {\n for (const [variant, fields] of Object.entries(schema.variants)) {\n if (seen.has(variant)) continue\n const ann = annotations[variant]\n if (ann?.dispatchMode !== 'agent-only') continue\n out.push({\n variant,\n intent: ann.intent,\n requiresConfirm: ann.requiresConfirm,\n dispatchMode: 'agent-only',\n source: 'schema',\n selectorHint: null,\n payloadHint: synthesizePayload(variant, fields),\n warning: ann.warning,\n examples: ann.examples,\n emits: ann.emits,\n fieldHints: collectFieldHints(fields),\n })\n }\n }\n\n return { actions: out }\n}\n\n/**\n * Build an example payload object the LLM can fill in. Required\n * fields always appear; optional fields appear only when annotated\n * `@should` (LLM is encouraged to fill them in). Fields without a\n * concrete primitive type (`'unknown'`) emit `null` placeholders the\n * LLM is expected to replace.\n *\n * The first key is `type` so the payload reads as a complete Msg\n * shape — copy-paste-ready into `send_message`.\n */\nfunction synthesizePayload(variant: string, fields: Record<string, MsgSchemaField>): object {\n const out: Record<string, unknown> = { type: variant }\n for (const [name, descriptor] of Object.entries(fields)) {\n const optional = isOptional(descriptor)\n const priority = isShould(descriptor)\n // Skip optional fields unless they're @should-flagged. Required\n // fields always appear.\n if (optional && priority !== 'should') continue\n out[name] = exampleValue(descriptor)\n }\n return out\n}\n\n/**\n * Walk a variant's field tree and collect every `@should` hint into a\n * flat list keyed by field path. Path conventions:\n * - top-level field: `\"cells\"`\n * - nested object property: `\"cells.value\"`\n * - array element: `\"cells[]\"` for the element itself; descendants\n * use `\"cells[].meta\"` and so on.\n *\n * Surfaces the same hints that show up nested inside\n * `description.messages.variants[X].field.hint` so callers don't have\n * to dig through the schema tree to find them.\n */\nfunction collectFieldHints(\n fields: Record<string, MsgSchemaField>,\n): Array<{ path: string; hint: string }> {\n const out: Array<{ path: string; hint: string }> = []\n for (const [name, descriptor] of Object.entries(fields)) {\n walkHint(name, descriptor, out)\n }\n return out\n}\n\nfunction walkHint(\n path: string,\n d: MsgSchemaField,\n out: Array<{ path: string; hint: string }>,\n): void {\n // Rich descriptor with a hint at this position.\n if (typeof d === 'object' && d !== null && 'type' in d) {\n if (typeof d.hint === 'string' && d.hint.length > 0) {\n out.push({ path, hint: d.hint })\n }\n walkHintBare(path, d.type, out)\n return\n }\n walkHintBare(path, d, out)\n}\n\nfunction walkHintBare(path: string, t: unknown, out: Array<{ path: string; hint: string }>): void {\n if (t === null || typeof t !== 'object') return\n const obj = t as Record<string, unknown>\n if (obj.kind === 'object' && obj.shape !== null && typeof obj.shape === 'object') {\n for (const [name, descriptor] of Object.entries(obj.shape as Record<string, MsgSchemaField>)) {\n walkHint(`${path}.${name}`, descriptor, out)\n }\n return\n }\n if (obj.kind === 'array') {\n walkHint(`${path}[]`, obj.element as MsgSchemaField, out)\n }\n if (obj.kind === 'discriminated-union') {\n // Synthetic hint at the union's path summarizing the legal\n // discriminant values. Lets the agent see \"format expects one of:\n // exact, range, compound\" without walking the full schema. Then\n // walk each branch's per-field hints with a path-suffix that\n // disambiguates which branch the hint applies to.\n const variants = obj.variants as Record<string, Record<string, MsgSchemaField>>\n const discriminant = String(obj.discriminant)\n const legalValues = Object.keys(variants)\n if (legalValues.length > 0) {\n out.push({\n path,\n hint: `Discriminated union — set \\`${discriminant}\\` to one of: ${legalValues\n .map((v) => `'${v}'`)\n .join(', ')}.`,\n })\n }\n for (const [discValue, fields] of Object.entries(variants)) {\n for (const [fieldName, fieldDesc] of Object.entries(fields)) {\n walkHint(`${path}(${discriminant}=${discValue}).${fieldName}`, fieldDesc, out)\n }\n }\n }\n}\n\nfunction isOptional(d: MsgSchemaField): boolean {\n return typeof d === 'object' && 'type' in d && d.optional === true\n}\n\nfunction isShould(d: MsgSchemaField): 'should' | undefined {\n return typeof d === 'object' && 'type' in d ? d.priority : undefined\n}\n\nfunction exampleValue(d: MsgSchemaField): unknown {\n // Unwrap rich descriptor to get the bare type for synthesis.\n const t =\n typeof d === 'object' && 'type' in d\n ? d.type\n : (d as\n | Exclude<MsgSchemaField, object>\n | Extract<MsgSchemaField, { kind?: string; enum?: string[] }>)\n return synthesizeBare(t as never)\n}\n\nfunction synthesizeBare(t: unknown): unknown {\n if (typeof t === 'string') {\n if (t === 'string') return ''\n if (t === 'number') return 0\n if (t === 'boolean') return false\n return null // 'unknown' or unrecognized keyword → placeholder\n }\n if (t === null || typeof t !== 'object') return null\n const obj = t as Record<string, unknown>\n if ('enum' in obj && Array.isArray(obj.enum)) {\n // First option doubles as the canonical example. Native value type\n // round-trips (string/number/boolean) since the compiler preserved\n // the literal kind on emit.\n return obj.enum[0] ?? null\n }\n if (obj.kind === 'object' && obj.shape !== null && typeof obj.shape === 'object') {\n // Recurse into the nested shape. Same optional-skip rule as the\n // top-level synthesizer: required fields appear, optional ones\n // appear only when @should-flagged.\n const out: Record<string, unknown> = {}\n for (const [name, descriptor] of Object.entries(obj.shape as Record<string, MsgSchemaField>)) {\n const isOpt = isOptional(descriptor)\n const pri = isShould(descriptor)\n if (isOpt && pri !== 'should') continue\n out[name] = exampleValue(descriptor)\n }\n return out\n }\n if (obj.kind === 'array') {\n // Wrap the synthesized element in a one-item array. Lets the LLM\n // see the per-entry shape without us guessing at array length.\n return [synthesizeBare(obj.element)]\n }\n if (obj.kind === 'discriminated-union') {\n // Synthesize the FIRST branch as the concrete example. The full\n // shape (every legal branch + its payload) is preserved in\n // `description.messages.variants[X]` from `describe_app`, so the\n // agent that needs another branch reads the schema directly.\n // `collectFieldHints` adds a synthetic enumeration of the legal\n // `<discriminant>` values onto the hint surface so the agent\n // doesn't have to dig into the schema for the simple case.\n const variants = obj.variants as Record<string, Record<string, MsgSchemaField>>\n const discriminant = String(obj.discriminant)\n const firstEntry = Object.entries(variants).at(0)\n if (!firstEntry) return null\n const [firstValue, firstFields] = firstEntry\n const branch: Record<string, unknown> = { [discriminant]: firstValue }\n for (const [name, descriptor] of Object.entries(firstFields)) {\n const isOpt = isOptional(descriptor)\n const pri = isShould(descriptor)\n if (isOpt && pri !== 'should') continue\n branch[name] = exampleValue(descriptor)\n }\n return branch\n }\n return null\n}\n"]}
|
|
1
|
+
{"version":3,"file":"list-actions.js","sourceRoot":"","sources":["../../../src/client/rpc/list-actions.ts"],"names":[],"mappings":"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","sourcesContent":["import type { MessageAnnotations } from '../../protocol.js'\nimport type { MsgSchemaShape, MsgSchemaField } from '../factory.js'\n\ntype Binding = { variant: string }\ntype Annotations = Record<string, MessageAnnotations>\n\nexport type ListActionsHost = {\n getState(): unknown\n getBindingDescriptors(): Binding[] | null\n getMsgAnnotations(): Annotations | null\n getMsgSchema(): MsgSchemaShape | null\n getAgentAffordances(): ((state: unknown) => Array<{ type: string; [k: string]: unknown }>) | null\n}\n\n/**\n * `dispatchMode` on each action is `'shared'` (human can also click via\n * a UI affordance) or `'agent-only'` (no UI binding — agent is the only\n * dispatcher). `'human-only'` variants are filtered out before this\n * point — they never reach the LLM.\n *\n * `source` distinguishes WHERE the affordance came from:\n * - `'binding'` — a tagged event handler is currently mounted in a\n * live scope (refcount > 0). Variants inside dead branches —\n * `show({when: false})`, unmounted `branch()` cases, removed `each`\n * items — auto-vanish from this set as their lifetimes dispose.\n * This is the framework's \"what can the user click right now\"\n * answer, and it's the default surface for the agent.\n * - `'always-affordable'` — either the app's `agentAffordances(state)`\n * hook listed the variant, or the variant carries the\n * `@alwaysAffordable` JSDoc tag. Both are the explicit \"agent can\n * reach this even when no live UI binding maps to it\" knob — bulk\n * seed ops (`Matrix/AddAlternatives`) and similar agent-driven\n * paths typically land here.\n * - `'schema'` — variant is annotated `@agentOnly` (the canonical\n * \"no UI button maps to this; the agent is the only dispatcher\")\n * and isn't already covered above. The payload is schema-synthesized\n * and the agent fills it in.\n *\n * `'shared'` variants WITHOUT a live binding, without\n * `agentAffordances` mention, and without `@alwaysAffordable` are\n * **deliberately hidden**. They're reachable through UI navigation —\n * the human user can't click them right now, and dispatching them\n * blindly would mutate state that drives `show()`/`branch()` gates,\n * popping hidden UI subtrees into view in places the user didn't\n * navigate to.\n */\nexport type ListActionsResult = {\n actions: Array<{\n variant: string\n /**\n * Human-readable phrase from `@intent(\"…\")`, or `null` when the\n * variant is unannotated. Mirror of LapActionsResponse.intent —\n * callers should treat `null` as a documentation gap and not as\n * \"missing label, fall back to variant name\".\n */\n intent: string | null\n requiresConfirm: boolean\n dispatchMode: 'shared' | 'agent-only'\n source: 'binding' | 'always-affordable' | 'schema'\n selectorHint: string | null\n payloadHint: object | null\n /** Cautionary text from `@warning` JSDoc, or null. */\n warning: string | null\n /** Concrete examples from `@example` JSDoc, in source order. */\n examples: string[]\n /**\n * Effect kinds this variant emits, from `@emits(\"k1\", \"k2\")`.\n * Empty when not annotated. Lets the agent know what side\n * effects fire — useful for batching (\"100 dispatches × cloud-\n * save = bad\") and for confirming destructive flows.\n */\n emits: string[]\n /**\n * Per-field guidance lifted from `@should(\"…\")` JSDoc on payload\n * fields. Path is dot/bracket notation rooted at the payload\n * (e.g. `\"cells\"` for a top-level field, `\"cells[].meta\"` for an\n * array element's nested field). Useful when the field is typed\n * as `unknown` or as a polymorphic shape — the hint says \"type\n * matches the criterion's kind: number for quantity, …\" so the\n * agent doesn't have to guess from the bare schema.\n *\n * Empty when no field on this variant carries an `@should` hint.\n */\n fieldHints: Array<{ path: string; hint: string }>\n }>\n}\n\nexport function handleListActions(host: ListActionsHost): ListActionsResult {\n const annotations = host.getMsgAnnotations() ?? {}\n const state = host.getState()\n const descriptors = host.getBindingDescriptors() ?? []\n const affordances = host.getAgentAffordances()?.(state) ?? []\n const schema = host.getMsgSchema()\n\n const out: ListActionsResult['actions'] = []\n const seen = new Set<string>()\n\n // From bindings — these have UI affordances by definition, so they're\n // either 'shared' (default) or, in the malformed case where someone\n // bound an `@agentOnly` Msg in a view, 'agent-only'. Either way the\n // agent can dispatch them.\n //\n // Filtered against the Msg schema: a binding whose variant isn't in\n // the user's Msg union is a library-internal Msg leaking through\n // `tagSend` translator wiring (the sortable component's `move`,\n // `drop`, `cancel`, etc. — they're routed into the user's update.ts\n // via a different shape but their lib names slip into the binding\n // registry). The agent has no use for those names — `would_dispatch`\n // / `send_message` would reject them as `unknown-variant` anyway —\n // so they pollute the affordance list. When a schema is available,\n // the schema's variant set is the source of truth.\n for (const d of descriptors) {\n if (schema && !(d.variant in schema.variants)) continue\n const ann = annotations[d.variant]\n if (ann?.dispatchMode === 'human-only') continue\n seen.add(d.variant)\n const variantSchema = schema?.variants[d.variant]\n out.push({\n variant: d.variant,\n intent: ann?.intent ?? null,\n requiresConfirm: ann?.requiresConfirm ?? false,\n dispatchMode: ann?.dispatchMode === 'agent-only' ? 'agent-only' : 'shared',\n source: 'binding',\n selectorHint: null,\n payloadHint: null,\n warning: ann?.warning ?? null,\n examples: ann?.examples ?? [],\n emits: ann?.emits ?? [],\n fieldHints: variantSchema ? collectFieldHints(variantSchema) : [],\n })\n }\n\n // From `agentAffordances(state)` — the integrator's explicit list of\n // currently-reachable Msgs, with concrete payloads they author.\n for (const msg of affordances) {\n const ann = annotations[msg.type]\n if (ann?.dispatchMode === 'human-only') continue\n seen.add(msg.type)\n const { type, ...rest } = msg\n const variantSchema = schema?.variants[type]\n out.push({\n variant: type,\n intent: ann?.intent ?? null,\n requiresConfirm: ann?.requiresConfirm ?? false,\n dispatchMode: ann?.dispatchMode === 'agent-only' ? 'agent-only' : 'shared',\n source: 'always-affordable',\n selectorHint: null,\n payloadHint: Object.keys(rest).length > 0 ? rest : null,\n warning: ann?.warning ?? null,\n examples: ann?.examples ?? [],\n emits: ann?.emits ?? [],\n fieldHints: variantSchema ? collectFieldHints(variantSchema) : [],\n })\n }\n\n // From `@alwaysAffordable` annotations — the per-variant equivalent\n // of `agentAffordances`. Variants tagged this way surface regardless\n // of whether a live binding maps to them, so bulk seed ops\n // (`Matrix/AddAlternatives`) and similar agent-driven paths are\n // available even when their UI counterparts (if any) aren't mounted.\n // The payload is schema-synthesized — `agentAffordances` is the way\n // to ship a concrete pre-filled payload alongside the affordance.\n for (const [variant, ann] of Object.entries(annotations)) {\n if (seen.has(variant)) continue\n if (ann.dispatchMode === 'human-only') continue\n if (!ann.alwaysAffordable) continue\n seen.add(variant)\n const fields = schema?.variants[variant]\n out.push({\n variant,\n intent: ann.intent,\n requiresConfirm: ann.requiresConfirm,\n dispatchMode: ann.dispatchMode === 'agent-only' ? 'agent-only' : 'shared',\n source: 'always-affordable',\n selectorHint: null,\n payloadHint: fields ? synthesizePayload(variant, fields) : null,\n warning: ann.warning,\n examples: ann.examples,\n emits: ann.emits,\n fieldHints: fields ? collectFieldHints(fields) : [],\n })\n }\n\n // From schema — variants that aren't already surfaced above and that\n // the author marked `@agentOnly`. The canonical \"no UI button maps\n // to this; the agent is the only dispatcher.\" Bulk edits, imports,\n // admin operations that have no human-facing affordance at all.\n //\n // `'shared'` variants WITHOUT a live binding stay hidden here — they\n // are reachable through UI navigation, and dispatching them while\n // their UI subtree is unmounted would pop hidden state in places the\n // user didn't navigate to. The explicit knobs are `@alwaysAffordable`\n // (handled above) or `agentAffordances(state)`.\n if (schema) {\n for (const [variant, fields] of Object.entries(schema.variants)) {\n if (seen.has(variant)) continue\n const ann = annotations[variant]\n if (ann?.dispatchMode !== 'agent-only') continue\n out.push({\n variant,\n intent: ann.intent,\n requiresConfirm: ann.requiresConfirm,\n dispatchMode: 'agent-only',\n source: 'schema',\n selectorHint: null,\n payloadHint: synthesizePayload(variant, fields),\n warning: ann.warning,\n examples: ann.examples,\n emits: ann.emits,\n fieldHints: collectFieldHints(fields),\n })\n }\n }\n\n return { actions: out }\n}\n\n/**\n * Build an example payload object the LLM can fill in. Required\n * fields always appear; optional fields appear only when annotated\n * `@should` (LLM is encouraged to fill them in). Fields without a\n * concrete primitive type (`'unknown'`) emit `null` placeholders the\n * LLM is expected to replace.\n *\n * The first key is `type` so the payload reads as a complete Msg\n * shape — copy-paste-ready into `send_message`.\n */\nfunction synthesizePayload(variant: string, fields: Record<string, MsgSchemaField>): object {\n const out: Record<string, unknown> = { type: variant }\n for (const [name, descriptor] of Object.entries(fields)) {\n const optional = isOptional(descriptor)\n const priority = isShould(descriptor)\n // Skip optional fields unless they're @should-flagged. Required\n // fields always appear.\n if (optional && priority !== 'should') continue\n out[name] = exampleValue(descriptor)\n }\n return out\n}\n\n/**\n * Walk a variant's field tree and collect every `@should` hint into a\n * flat list keyed by field path. Path conventions:\n * - top-level field: `\"cells\"`\n * - nested object property: `\"cells.value\"`\n * - array element: `\"cells[]\"` for the element itself; descendants\n * use `\"cells[].meta\"` and so on.\n *\n * Surfaces the same hints that show up nested inside\n * `description.messages.variants[X].field.hint` so callers don't have\n * to dig through the schema tree to find them.\n */\nfunction collectFieldHints(\n fields: Record<string, MsgSchemaField>,\n): Array<{ path: string; hint: string }> {\n const out: Array<{ path: string; hint: string }> = []\n for (const [name, descriptor] of Object.entries(fields)) {\n walkHint(name, descriptor, out)\n }\n return out\n}\n\nfunction walkHint(\n path: string,\n d: MsgSchemaField,\n out: Array<{ path: string; hint: string }>,\n): void {\n // Rich descriptor with a hint at this position.\n if (typeof d === 'object' && d !== null && 'type' in d) {\n if (typeof d.hint === 'string' && d.hint.length > 0) {\n out.push({ path, hint: d.hint })\n }\n // `@validates(...)` predicates surface alongside `@should` hints\n // so the agent sees the constraint at affordance time rather than\n // only as a post-dispatch rejection. The verbatim predicate text\n // is what the runtime evaluates; agents trained on JS read it\n // directly. Prefix `validates: ` to disambiguate from freeform\n // `@should` text.\n if (typeof d.validates === 'string' && d.validates.length > 0) {\n out.push({ path, hint: `validates: ${d.validates}` })\n }\n walkHintBare(path, d.type, out)\n return\n }\n walkHintBare(path, d, out)\n}\n\nfunction walkHintBare(path: string, t: unknown, out: Array<{ path: string; hint: string }>): void {\n if (t === null || typeof t !== 'object') return\n const obj = t as Record<string, unknown>\n if (obj.kind === 'object' && obj.shape !== null && typeof obj.shape === 'object') {\n for (const [name, descriptor] of Object.entries(obj.shape as Record<string, MsgSchemaField>)) {\n walkHint(`${path}.${name}`, descriptor, out)\n }\n return\n }\n if (obj.kind === 'array') {\n walkHint(`${path}[]`, obj.element as MsgSchemaField, out)\n }\n if (obj.kind === 'discriminated-union') {\n // Synthetic hint at the union's path summarizing the legal\n // discriminant values. Lets the agent see \"format expects one of:\n // exact, range, compound\" without walking the full schema. Then\n // walk each branch's per-field hints with a path-suffix that\n // disambiguates which branch the hint applies to.\n const variants = obj.variants as Record<string, Record<string, MsgSchemaField>>\n const discriminant = String(obj.discriminant)\n const legalValues = Object.keys(variants)\n if (legalValues.length > 0) {\n out.push({\n path,\n hint: `Discriminated union — set \\`${discriminant}\\` to one of: ${legalValues\n .map((v) => `'${v}'`)\n .join(', ')}.`,\n })\n }\n for (const [discValue, fields] of Object.entries(variants)) {\n for (const [fieldName, fieldDesc] of Object.entries(fields)) {\n walkHint(`${path}(${discriminant}=${discValue}).${fieldName}`, fieldDesc, out)\n }\n }\n }\n}\n\nfunction isOptional(d: MsgSchemaField): boolean {\n return typeof d === 'object' && 'type' in d && d.optional === true\n}\n\nfunction isShould(d: MsgSchemaField): 'should' | undefined {\n return typeof d === 'object' && 'type' in d ? d.priority : undefined\n}\n\nfunction exampleValue(d: MsgSchemaField): unknown {\n // Unwrap rich descriptor to get the bare type for synthesis.\n const t =\n typeof d === 'object' && 'type' in d\n ? d.type\n : (d as\n | Exclude<MsgSchemaField, object>\n | Extract<MsgSchemaField, { kind?: string; enum?: string[] }>)\n return synthesizeBare(t as never)\n}\n\nfunction synthesizeBare(t: unknown): unknown {\n if (typeof t === 'string') {\n if (t === 'string') return ''\n if (t === 'number') return 0\n if (t === 'boolean') return false\n return null // 'unknown' or unrecognized keyword → placeholder\n }\n if (t === null || typeof t !== 'object') return null\n const obj = t as Record<string, unknown>\n if ('enum' in obj && Array.isArray(obj.enum)) {\n // First option doubles as the canonical example. Native value type\n // round-trips (string/number/boolean) since the compiler preserved\n // the literal kind on emit.\n return obj.enum[0] ?? null\n }\n if (obj.kind === 'object' && obj.shape !== null && typeof obj.shape === 'object') {\n // Recurse into the nested shape. Same optional-skip rule as the\n // top-level synthesizer: required fields appear, optional ones\n // appear only when @should-flagged.\n const out: Record<string, unknown> = {}\n for (const [name, descriptor] of Object.entries(obj.shape as Record<string, MsgSchemaField>)) {\n const isOpt = isOptional(descriptor)\n const pri = isShould(descriptor)\n if (isOpt && pri !== 'should') continue\n out[name] = exampleValue(descriptor)\n }\n return out\n }\n if (obj.kind === 'array') {\n // Wrap the synthesized element in a one-item array. Lets the LLM\n // see the per-entry shape without us guessing at array length.\n return [synthesizeBare(obj.element)]\n }\n if (obj.kind === 'discriminated-union') {\n // Synthesize the FIRST branch as the concrete example. The full\n // shape (every legal branch + its payload) is preserved in\n // `description.messages.variants[X]` from `describe_app`, so the\n // agent that needs another branch reads the schema directly.\n // `collectFieldHints` adds a synthetic enumeration of the legal\n // `<discriminant>` values onto the hint surface so the agent\n // doesn't have to dig into the schema for the simple case.\n const variants = obj.variants as Record<string, Record<string, MsgSchemaField>>\n const discriminant = String(obj.discriminant)\n const firstEntry = Object.entries(variants).at(0)\n if (!firstEntry) return null\n const [firstValue, firstFields] = firstEntry\n const branch: Record<string, unknown> = { [discriminant]: firstValue }\n for (const [name, descriptor] of Object.entries(firstFields)) {\n const isOpt = isOptional(descriptor)\n const pri = isShould(descriptor)\n if (isOpt && pri !== 'should') continue\n branch[name] = exampleValue(descriptor)\n }\n return branch\n }\n return null\n}\n"]}
|
|
@@ -40,6 +40,14 @@ export type SendMessageHost = ListActionsHost & {
|
|
|
40
40
|
* without `window`), the drain envelope records an empty array.
|
|
41
41
|
*/
|
|
42
42
|
getAndClearDrainErrors?: () => LapDrainMeta['errors'];
|
|
43
|
+
/**
|
|
44
|
+
* Optional dispatch-policy accessor — when defined, returns the
|
|
45
|
+
* server's configured `'strict' | 'lenient'` policy for payload
|
|
46
|
+
* validation. Strict mode rejects fields not in the schema and
|
|
47
|
+
* emits warnings for `'unknown'`-typed fields the agent provided
|
|
48
|
+
* values for. Default is lenient (omit / undefined).
|
|
49
|
+
*/
|
|
50
|
+
getDispatchPolicy?: () => 'strict' | 'lenient';
|
|
43
51
|
/** Called when @requiresConfirm; caller stores a ConfirmEntry in state. */
|
|
44
52
|
proposeConfirm(entry: {
|
|
45
53
|
id: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"send-message.d.ts","sourceRoot":"","sources":["../../../src/client/rpc/send-message.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAG3E,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,mBAAmB,CAAA;AAE1B,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAA;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,4DAA4D;IAC5D,OAAO,CAAC,EAAE,SAAS,GAAG,MAAM,GAAG,MAAM,CAAA;IACrC,6DAA6D;IAC7D,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,eAAe,GAAG,eAAe,GAAG;IAC9C,QAAQ,IAAI,OAAO,CAAA;IACnB,IAAI,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAA;IACxB,KAAK,IAAI,IAAI,CAAA;IACb;;;;;;;;OAQG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;IAC3C,iBAAiB,IAAI,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAAG,IAAI,CAAA;IAC9D;;;;;;;;OAQG;IACH,sBAAsB,CAAC,EAAE,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAA;IACrD,2EAA2E;IAC3E,cAAc,CAAC,KAAK,EAAE;QACpB,EAAE,EAAE,MAAM,CAAA;QACV,OAAO,EAAE,MAAM,CAAA;QACf,OAAO,EAAE,OAAO,CAAA;QAChB,MAAM,EAAE,MAAM,CAAA;QACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;QACrB,UAAU,EAAE,MAAM,CAAA;QAClB,MAAM,EAAE,SAAS,CAAA;KAClB,GAAG,IAAI,CAAA;CACT,CAAA;AAKD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,eAAe,EACrB,IAAI,EAAE,eAAe,GACpB,OAAO,CAAC,kBAAkB,CAAC,
|
|
1
|
+
{"version":3,"file":"send-message.d.ts","sourceRoot":"","sources":["../../../src/client/rpc/send-message.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAG3E,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EACnB,MAAM,mBAAmB,CAAA;AAE1B,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAA;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,4DAA4D;IAC5D,OAAO,CAAC,EAAE,SAAS,GAAG,MAAM,GAAG,MAAM,CAAA;IACrC,6DAA6D;IAC7D,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,2DAA2D;IAC3D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,6DAA6D;IAC7D,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,eAAe,GAAG,eAAe,GAAG;IAC9C,QAAQ,IAAI,OAAO,CAAA;IACnB,IAAI,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAAA;IACxB,KAAK,IAAI,IAAI,CAAA;IACb;;;;;;;;OAQG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;IAC3C,iBAAiB,IAAI,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAAG,IAAI,CAAA;IAC9D;;;;;;;;OAQG;IACH,sBAAsB,CAAC,EAAE,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAA;IACrD;;;;;;OAMG;IACH,iBAAiB,CAAC,EAAE,MAAM,QAAQ,GAAG,SAAS,CAAA;IAC9C,2EAA2E;IAC3E,cAAc,CAAC,KAAK,EAAE;QACpB,EAAE,EAAE,MAAM,CAAA;QACV,OAAO,EAAE,MAAM,CAAA;QACf,OAAO,EAAE,OAAO,CAAA;QAChB,MAAM,EAAE,MAAM,CAAA;QACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;QACrB,UAAU,EAAE,MAAM,CAAA;QAClB,MAAM,EAAE,SAAS,CAAA;KAClB,GAAG,IAAI,CAAA;CACT,CAAA;AAKD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,eAAe,EACrB,IAAI,EAAE,eAAe,GACpB,OAAO,CAAC,kBAAkB,CAAC,CAiK7B;AA2GD,MAAM,MAAM,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,EAAE;IAAE,MAAM,EAAE,YAAY,CAAA;CAAE,CAAC,CAAA;AACtF,YAAY,EAAE,kBAAkB,EAAE,CAAA"}
|
|
@@ -28,7 +28,8 @@ export async function handleSendMessage(host, args) {
|
|
|
28
28
|
// correct from in one round trip. Reducers stay the last line of
|
|
29
29
|
// defense; this is the first.
|
|
30
30
|
const schema = host.getMsgSchema?.() ?? null;
|
|
31
|
-
const
|
|
31
|
+
const policy = host.getDispatchPolicy?.() ?? 'lenient';
|
|
32
|
+
const validation = validatePayload(args.msg, schema, { policy });
|
|
32
33
|
if (!validation.ok) {
|
|
33
34
|
return {
|
|
34
35
|
status: 'rejected',
|
|
@@ -36,6 +37,11 @@ export async function handleSendMessage(host, args) {
|
|
|
36
37
|
detail: validation.errors.map((e) => `${e.path}: ${e.message}`).join('; '),
|
|
37
38
|
};
|
|
38
39
|
}
|
|
40
|
+
// Warnings from the validator (strict-mode `untyped-field` flags etc.)
|
|
41
|
+
// ride along to `drain.warnings` so the agent sees them on the
|
|
42
|
+
// dispatched envelope. Lenient mode never emits warnings; this is
|
|
43
|
+
// a no-op array for the default path.
|
|
44
|
+
const validationWarnings = validation.warnings ?? [];
|
|
39
45
|
if (ann?.requiresConfirm) {
|
|
40
46
|
const id = randomUUID();
|
|
41
47
|
const { type: _type, ...payload } = args.msg;
|
|
@@ -62,13 +68,13 @@ export async function handleSendMessage(host, args) {
|
|
|
62
68
|
const includeState = args.includeState === true;
|
|
63
69
|
if (waitFor === 'none') {
|
|
64
70
|
safeSend(host, args.msg, []);
|
|
65
|
-
return dispatched(host, emptyDrain(), prevState, includeState);
|
|
71
|
+
return dispatched(host, emptyDrain(), prevState, includeState, validationWarnings);
|
|
66
72
|
}
|
|
67
73
|
if (waitFor === 'idle') {
|
|
68
74
|
const dispatchErrors = [];
|
|
69
75
|
safeSendAndFlush(host, args.msg, dispatchErrors);
|
|
70
76
|
await Promise.resolve();
|
|
71
|
-
return dispatched(host, { effectsObserved: 1, durationMs: 0, timedOut: false, errors: dispatchErrors }, prevState, includeState);
|
|
77
|
+
return dispatched(host, { effectsObserved: 1, durationMs: 0, timedOut: false, errors: dispatchErrors }, prevState, includeState, validationWarnings);
|
|
72
78
|
}
|
|
73
79
|
// waitFor === 'drained' — message-queue quiescence detection.
|
|
74
80
|
// Clear any errors buffered before this call so `drain.errors`
|
|
@@ -98,7 +104,7 @@ export async function handleSendMessage(host, args) {
|
|
|
98
104
|
durationMs: elapsed,
|
|
99
105
|
timedOut: true,
|
|
100
106
|
errors: mergeDrainErrors(dispatchErrors, host.getAndClearDrainErrors?.()),
|
|
101
|
-
}, prevState, includeState);
|
|
107
|
+
}, prevState, includeState, validationWarnings);
|
|
102
108
|
}
|
|
103
109
|
const budget = Math.min(quietMs, capMs - elapsed);
|
|
104
110
|
// When the cap is within `quietMs` of `elapsed`, the quiet
|
|
@@ -116,7 +122,7 @@ export async function handleSendMessage(host, args) {
|
|
|
116
122
|
durationMs: now() - t0,
|
|
117
123
|
timedOut: !fullQuiet,
|
|
118
124
|
errors: mergeDrainErrors(dispatchErrors, host.getAndClearDrainErrors?.()),
|
|
119
|
-
}, prevState, includeState);
|
|
125
|
+
}, prevState, includeState, validationWarnings);
|
|
120
126
|
}
|
|
121
127
|
// A commit fired during the wait — flush any queued follow-ups so
|
|
122
128
|
// effects dispatched by that cycle run before we re-check.
|
|
@@ -181,13 +187,14 @@ function mergeDrainErrors(fromDispatch, fromHost) {
|
|
|
181
187
|
return fromHost;
|
|
182
188
|
return [...fromDispatch, ...fromHost];
|
|
183
189
|
}
|
|
184
|
-
function dispatched(host, drain, prevState, includeState) {
|
|
190
|
+
function dispatched(host, drain, prevState, includeState, validationWarnings = []) {
|
|
185
191
|
const stateAfter = host.getState();
|
|
192
|
+
const drainWithWarnings = validationWarnings.length === 0 ? drain : { ...drain, warnings: validationWarnings };
|
|
186
193
|
const base = {
|
|
187
194
|
status: 'dispatched',
|
|
188
195
|
stateDiff: computeStateDiff(prevState, stateAfter),
|
|
189
196
|
actions: handleListActions(host).actions,
|
|
190
|
-
drain,
|
|
197
|
+
drain: drainWithWarnings,
|
|
191
198
|
};
|
|
192
199
|
return includeState ? { ...base, stateAfter } : base;
|
|
193
200
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"send-message.js","sourceRoot":"","sources":["../../../src/client/rpc/send-message.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from '../uuid.js'\nimport { handleListActions, type ListActionsHost } from './list-actions.js'\nimport { computeStateDiff } from '../../state-diff.js'\nimport { validatePayload } from './validate-payload.js'\nimport type {\n LapActionsResponse,\n LapDrainMeta,\n LapMessageResponse,\n MessageAnnotations,\n} from '../../protocol.js'\n\nexport type SendMessageArgs = {\n msg: { type: string; [k: string]: unknown }\n reason?: string\n /** See LapMessageRequest['waitFor']. Default: 'drained'. */\n waitFor?: 'drained' | 'idle' | 'none'\n /** See LapMessageRequest['drainQuietMs']. Default: 100ms. */\n drainQuietMs?: number\n /** See LapMessageRequest['timeoutMs']. Default: 5000ms. */\n timeoutMs?: number\n /** See LapMessageRequest['includeState']. Default: false. */\n includeState?: boolean\n}\n\nexport type SendMessageHost = ListActionsHost & {\n getState(): unknown\n send(msg: unknown): void\n flush(): void\n /**\n * Register a listener called after every update cycle commits —\n * backed by `AppHandle.subscribe`. Returns an unsubscribe function.\n * The drain loop uses this to detect message-queue quiescence: each\n * listener fire resets the quiet-window timer; no fires for\n * `drainQuietMs` means the loop has gone idle and async effects (if\n * any) have either completed or are persistent\n * (websocket/interval/storageWatch).\n */\n subscribe(listener: () => void): () => void\n getMsgAnnotations(): Record<string, MessageAnnotations> | null\n /**\n * Snapshot and clear the drain-error buffer. The agent factory\n * installs persistent `window.error` / `unhandledrejection`\n * listeners that accumulate into this buffer; calling this at the\n * start of a drain discards stale errors from prior windows, and\n * calling it at the end yields just the errors that fired during\n * this drain. Optional — when omitted (e.g., Node test harness\n * without `window`), the drain envelope records an empty array.\n */\n getAndClearDrainErrors?: () => LapDrainMeta['errors']\n /** Called when @requiresConfirm; caller stores a ConfirmEntry in state. */\n proposeConfirm(entry: {\n id: string\n variant: string\n payload: unknown\n intent: string\n reason: string | null\n proposedAt: number\n status: 'pending'\n }): void\n}\n\nconst DEFAULT_QUIET_MS = 100\nconst DEFAULT_TIMEOUT_MS = 5_000\n\nexport async function handleSendMessage(\n host: SendMessageHost,\n args: SendMessageArgs,\n): Promise<LapMessageResponse> {\n if (!args.msg || typeof args.msg.type !== 'string') {\n return { status: 'rejected', reason: 'invalid' }\n }\n const annotations = host.getMsgAnnotations() ?? {}\n const ann = annotations[args.msg.type]\n\n // If annotations map is non-empty and this variant isn't in it, it's an\n // unknown msg type that the app never declared — reject early so the\n // browser never dispatches an unrecognised variant into update().\n const hasAnnotations = Object.keys(annotations).length > 0\n if (hasAnnotations && !ann) {\n return { status: 'rejected', reason: 'invalid', detail: `unknown variant: ${args.msg.type}` }\n }\n\n if (ann?.dispatchMode === 'human-only') {\n return { status: 'rejected', reason: 'human-only' }\n }\n\n // Schema validation: when the compiler emitted a `__msgSchema`,\n // check the payload against this variant's field shape before\n // dispatch. Catches the everyday agent bug — missing required\n // field, wrong enum value, missing discriminant on a tagged union,\n // typo in a key name — early, with structured errors the LLM can\n // correct from in one round trip. Reducers stay the last line of\n // defense; this is the first.\n const schema = host.getMsgSchema?.() ?? null\n const validation = validatePayload(args.msg, schema)\n if (!validation.ok) {\n return {\n status: 'rejected',\n reason: 'invalid',\n detail: validation.errors.map((e) => `${e.path}: ${e.message}`).join('; '),\n }\n }\n\n if (ann?.requiresConfirm) {\n const id = randomUUID()\n const { type: _type, ...payload } = args.msg\n host.proposeConfirm({\n id,\n variant: args.msg.type,\n payload,\n intent: ann?.intent ?? args.msg.type,\n reason: args.reason ?? null,\n proposedAt: Date.now(),\n status: 'pending',\n })\n return { status: 'pending-confirmation', confirmId: id }\n }\n\n const waitFor = args.waitFor ?? 'drained'\n const quietMs = Math.max(0, args.drainQuietMs ?? DEFAULT_QUIET_MS)\n const capMs = Math.max(0, args.timeoutMs ?? DEFAULT_TIMEOUT_MS)\n\n // Snapshot pre-dispatch state for diffing. The host's `getState`\n // returns a reference; capturing it here keeps a pre-mutation\n // pointer even after `host.send` triggers reducer-driven state\n // replacement (state itself is immutable per LLui's TEA contract,\n // so the reference stays valid).\n const prevState = host.getState()\n\n const includeState = args.includeState === true\n\n if (waitFor === 'none') {\n safeSend(host, args.msg, [])\n return dispatched(host, emptyDrain(), prevState, includeState)\n }\n\n if (waitFor === 'idle') {\n const dispatchErrors: LapDrainMeta['errors'] = []\n safeSendAndFlush(host, args.msg, dispatchErrors)\n await Promise.resolve()\n return dispatched(\n host,\n { effectsObserved: 1, durationMs: 0, timedOut: false, errors: dispatchErrors },\n prevState,\n includeState,\n )\n }\n\n // waitFor === 'drained' — message-queue quiescence detection.\n // Clear any errors buffered before this call so `drain.errors`\n // attributes only to this window.\n host.getAndClearDrainErrors?.()\n\n const t0 = now()\n let observed = 0\n let wake: ((reason: 'msg' | 'timeout') => void) | null = null\n const unsub = host.subscribe(() => {\n observed++\n const w = wake\n wake = null\n w?.('msg')\n })\n // Synchronous throws during send/flush — captured here and folded\n // into drain.errors. Async post-flush errors come in via\n // `getAndClearDrainErrors` (effect handler crashes, async rejections\n // observed by the runtime) and are merged at response time.\n const dispatchErrors: LapDrainMeta['errors'] = []\n try {\n safeSendAndFlush(host, args.msg, dispatchErrors)\n\n while (true) {\n const elapsed = now() - t0\n if (elapsed >= capMs) {\n return dispatched(\n host,\n {\n effectsObserved: observed,\n durationMs: elapsed,\n timedOut: true,\n errors: mergeDrainErrors(dispatchErrors, host.getAndClearDrainErrors?.()),\n },\n prevState,\n includeState,\n )\n }\n const budget = Math.min(quietMs, capMs - elapsed)\n // When the cap is within `quietMs` of `elapsed`, the quiet\n // window is truncated. In that case a timeout resolution does\n // NOT mean we detected quiescence — it means the cap cut the\n // window short. Only a full-length quiet window that elapses\n // without a new commit counts as real idle.\n const fullQuiet = budget >= quietMs\n const reason = await awaitQuietOrMsg(budget, (resolve) => {\n wake = resolve\n })\n if (reason === 'timeout') {\n return dispatched(\n host,\n {\n effectsObserved: observed,\n durationMs: now() - t0,\n timedOut: !fullQuiet,\n errors: mergeDrainErrors(dispatchErrors, host.getAndClearDrainErrors?.()),\n },\n prevState,\n includeState,\n )\n }\n // A commit fired during the wait — flush any queued follow-ups so\n // effects dispatched by that cycle run before we re-check.\n try {\n host.flush()\n } catch (e) {\n dispatchErrors.push(toDrainError(e))\n }\n }\n } finally {\n unsub()\n }\n}\n\n/**\n * Send a Msg and capture any synchronous throw into `errors` rather\n * than letting it propagate to the WS RPC layer. By the time `send`\n * has thrown, the reducer may have partially run (state can advance),\n * but bindings or downstream effects on the same commit may have\n * crashed mid-flight. From the agent's POV: the dispatch IS dispatched,\n * the state diff reflects what actually changed, and `drain.errors`\n * reports the in-flight crash. That's strictly more useful than HTTP\n * 500, which the agent reads as \"the dispatch never happened.\"\n */\nfunction safeSend(\n host: SendMessageHost,\n msg: { type: string; [k: string]: unknown },\n errors: LapDrainMeta['errors'],\n): void {\n try {\n host.send(msg)\n } catch (e) {\n errors.push(toDrainError(e))\n }\n}\n\nfunction safeSendAndFlush(\n host: SendMessageHost,\n msg: { type: string; [k: string]: unknown },\n errors: LapDrainMeta['errors'],\n): void {\n try {\n host.send(msg)\n } catch (e) {\n errors.push(toDrainError(e))\n return // can't flush something we never sent\n }\n try {\n host.flush()\n } catch (e) {\n errors.push(toDrainError(e))\n }\n}\n\nfunction toDrainError(e: unknown): LapDrainMeta['errors'][number] {\n if (e instanceof Error) {\n const stack = e.stack ? e.stack.split('\\n').slice(0, 8).join('\\n') : undefined\n return stack !== undefined\n ? { kind: 'error', message: `${e.name}: ${e.message}`, stack }\n : { kind: 'error', message: `${e.name}: ${e.message}` }\n }\n return { kind: 'error', message: String(e) }\n}\n\nfunction mergeDrainErrors(\n fromDispatch: LapDrainMeta['errors'],\n fromHost: LapDrainMeta['errors'] | undefined,\n): LapDrainMeta['errors'] {\n if (!fromHost || fromHost.length === 0) return fromDispatch\n if (fromDispatch.length === 0) return fromHost\n return [...fromDispatch, ...fromHost]\n}\n\nfunction dispatched(\n host: SendMessageHost,\n drain: LapDrainMeta,\n prevState: unknown,\n includeState: boolean,\n): LapMessageResponse {\n const stateAfter = host.getState()\n const base = {\n status: 'dispatched' as const,\n stateDiff: computeStateDiff(prevState, stateAfter),\n actions: handleListActions(host).actions,\n drain,\n }\n return includeState ? { ...base, stateAfter } : base\n}\n\nfunction emptyDrain(): LapDrainMeta {\n return { effectsObserved: 0, durationMs: 0, timedOut: false, errors: [] }\n}\n\nfunction awaitQuietOrMsg(\n budgetMs: number,\n registerWake: (resolve: (r: 'msg' | 'timeout') => void) => void,\n): Promise<'msg' | 'timeout'> {\n return new Promise<'msg' | 'timeout'>((resolve) => {\n let settled = false\n const guarded = (r: 'msg' | 'timeout') => {\n if (settled) return\n settled = true\n resolve(r)\n }\n registerWake(guarded)\n setTimeout(() => guarded('timeout'), budgetMs)\n })\n}\n\nfunction now(): number {\n return typeof performance !== 'undefined' && typeof performance.now === 'function'\n ? performance.now()\n : Date.now()\n}\n\n// Helper types for external callers that want the dispatched envelope.\nexport type DispatchedEnvelope = Extract<LapMessageResponse, { status: 'dispatched' }>\nexport type { LapActionsResponse }\n"]}
|
|
1
|
+
{"version":3,"file":"send-message.js","sourceRoot":"","sources":["../../../src/client/rpc/send-message.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from '../uuid.js'\nimport { handleListActions, type ListActionsHost } from './list-actions.js'\nimport { computeStateDiff } from '../../state-diff.js'\nimport { validatePayload } from './validate-payload.js'\nimport type {\n LapActionsResponse,\n LapDrainMeta,\n LapMessageResponse,\n MessageAnnotations,\n} from '../../protocol.js'\n\nexport type SendMessageArgs = {\n msg: { type: string; [k: string]: unknown }\n reason?: string\n /** See LapMessageRequest['waitFor']. Default: 'drained'. */\n waitFor?: 'drained' | 'idle' | 'none'\n /** See LapMessageRequest['drainQuietMs']. Default: 100ms. */\n drainQuietMs?: number\n /** See LapMessageRequest['timeoutMs']. Default: 5000ms. */\n timeoutMs?: number\n /** See LapMessageRequest['includeState']. Default: false. */\n includeState?: boolean\n}\n\nexport type SendMessageHost = ListActionsHost & {\n getState(): unknown\n send(msg: unknown): void\n flush(): void\n /**\n * Register a listener called after every update cycle commits —\n * backed by `AppHandle.subscribe`. Returns an unsubscribe function.\n * The drain loop uses this to detect message-queue quiescence: each\n * listener fire resets the quiet-window timer; no fires for\n * `drainQuietMs` means the loop has gone idle and async effects (if\n * any) have either completed or are persistent\n * (websocket/interval/storageWatch).\n */\n subscribe(listener: () => void): () => void\n getMsgAnnotations(): Record<string, MessageAnnotations> | null\n /**\n * Snapshot and clear the drain-error buffer. The agent factory\n * installs persistent `window.error` / `unhandledrejection`\n * listeners that accumulate into this buffer; calling this at the\n * start of a drain discards stale errors from prior windows, and\n * calling it at the end yields just the errors that fired during\n * this drain. Optional — when omitted (e.g., Node test harness\n * without `window`), the drain envelope records an empty array.\n */\n getAndClearDrainErrors?: () => LapDrainMeta['errors']\n /**\n * Optional dispatch-policy accessor — when defined, returns the\n * server's configured `'strict' | 'lenient'` policy for payload\n * validation. Strict mode rejects fields not in the schema and\n * emits warnings for `'unknown'`-typed fields the agent provided\n * values for. Default is lenient (omit / undefined).\n */\n getDispatchPolicy?: () => 'strict' | 'lenient'\n /** Called when @requiresConfirm; caller stores a ConfirmEntry in state. */\n proposeConfirm(entry: {\n id: string\n variant: string\n payload: unknown\n intent: string\n reason: string | null\n proposedAt: number\n status: 'pending'\n }): void\n}\n\nconst DEFAULT_QUIET_MS = 100\nconst DEFAULT_TIMEOUT_MS = 5_000\n\nexport async function handleSendMessage(\n host: SendMessageHost,\n args: SendMessageArgs,\n): Promise<LapMessageResponse> {\n if (!args.msg || typeof args.msg.type !== 'string') {\n return { status: 'rejected', reason: 'invalid' }\n }\n const annotations = host.getMsgAnnotations() ?? {}\n const ann = annotations[args.msg.type]\n\n // If annotations map is non-empty and this variant isn't in it, it's an\n // unknown msg type that the app never declared — reject early so the\n // browser never dispatches an unrecognised variant into update().\n const hasAnnotations = Object.keys(annotations).length > 0\n if (hasAnnotations && !ann) {\n return { status: 'rejected', reason: 'invalid', detail: `unknown variant: ${args.msg.type}` }\n }\n\n if (ann?.dispatchMode === 'human-only') {\n return { status: 'rejected', reason: 'human-only' }\n }\n\n // Schema validation: when the compiler emitted a `__msgSchema`,\n // check the payload against this variant's field shape before\n // dispatch. Catches the everyday agent bug — missing required\n // field, wrong enum value, missing discriminant on a tagged union,\n // typo in a key name — early, with structured errors the LLM can\n // correct from in one round trip. Reducers stay the last line of\n // defense; this is the first.\n const schema = host.getMsgSchema?.() ?? null\n const policy = host.getDispatchPolicy?.() ?? 'lenient'\n const validation = validatePayload(args.msg, schema, { policy })\n if (!validation.ok) {\n return {\n status: 'rejected',\n reason: 'invalid',\n detail: validation.errors.map((e) => `${e.path}: ${e.message}`).join('; '),\n }\n }\n // Warnings from the validator (strict-mode `untyped-field` flags etc.)\n // ride along to `drain.warnings` so the agent sees them on the\n // dispatched envelope. Lenient mode never emits warnings; this is\n // a no-op array for the default path.\n const validationWarnings = validation.warnings ?? []\n\n if (ann?.requiresConfirm) {\n const id = randomUUID()\n const { type: _type, ...payload } = args.msg\n host.proposeConfirm({\n id,\n variant: args.msg.type,\n payload,\n intent: ann?.intent ?? args.msg.type,\n reason: args.reason ?? null,\n proposedAt: Date.now(),\n status: 'pending',\n })\n return { status: 'pending-confirmation', confirmId: id }\n }\n\n const waitFor = args.waitFor ?? 'drained'\n const quietMs = Math.max(0, args.drainQuietMs ?? DEFAULT_QUIET_MS)\n const capMs = Math.max(0, args.timeoutMs ?? DEFAULT_TIMEOUT_MS)\n\n // Snapshot pre-dispatch state for diffing. The host's `getState`\n // returns a reference; capturing it here keeps a pre-mutation\n // pointer even after `host.send` triggers reducer-driven state\n // replacement (state itself is immutable per LLui's TEA contract,\n // so the reference stays valid).\n const prevState = host.getState()\n\n const includeState = args.includeState === true\n\n if (waitFor === 'none') {\n safeSend(host, args.msg, [])\n return dispatched(host, emptyDrain(), prevState, includeState, validationWarnings)\n }\n\n if (waitFor === 'idle') {\n const dispatchErrors: LapDrainMeta['errors'] = []\n safeSendAndFlush(host, args.msg, dispatchErrors)\n await Promise.resolve()\n return dispatched(\n host,\n { effectsObserved: 1, durationMs: 0, timedOut: false, errors: dispatchErrors },\n prevState,\n includeState,\n validationWarnings,\n )\n }\n\n // waitFor === 'drained' — message-queue quiescence detection.\n // Clear any errors buffered before this call so `drain.errors`\n // attributes only to this window.\n host.getAndClearDrainErrors?.()\n\n const t0 = now()\n let observed = 0\n let wake: ((reason: 'msg' | 'timeout') => void) | null = null\n const unsub = host.subscribe(() => {\n observed++\n const w = wake\n wake = null\n w?.('msg')\n })\n // Synchronous throws during send/flush — captured here and folded\n // into drain.errors. Async post-flush errors come in via\n // `getAndClearDrainErrors` (effect handler crashes, async rejections\n // observed by the runtime) and are merged at response time.\n const dispatchErrors: LapDrainMeta['errors'] = []\n try {\n safeSendAndFlush(host, args.msg, dispatchErrors)\n\n while (true) {\n const elapsed = now() - t0\n if (elapsed >= capMs) {\n return dispatched(\n host,\n {\n effectsObserved: observed,\n durationMs: elapsed,\n timedOut: true,\n errors: mergeDrainErrors(dispatchErrors, host.getAndClearDrainErrors?.()),\n },\n prevState,\n includeState,\n validationWarnings,\n )\n }\n const budget = Math.min(quietMs, capMs - elapsed)\n // When the cap is within `quietMs` of `elapsed`, the quiet\n // window is truncated. In that case a timeout resolution does\n // NOT mean we detected quiescence — it means the cap cut the\n // window short. Only a full-length quiet window that elapses\n // without a new commit counts as real idle.\n const fullQuiet = budget >= quietMs\n const reason = await awaitQuietOrMsg(budget, (resolve) => {\n wake = resolve\n })\n if (reason === 'timeout') {\n return dispatched(\n host,\n {\n effectsObserved: observed,\n durationMs: now() - t0,\n timedOut: !fullQuiet,\n errors: mergeDrainErrors(dispatchErrors, host.getAndClearDrainErrors?.()),\n },\n prevState,\n includeState,\n validationWarnings,\n )\n }\n // A commit fired during the wait — flush any queued follow-ups so\n // effects dispatched by that cycle run before we re-check.\n try {\n host.flush()\n } catch (e) {\n dispatchErrors.push(toDrainError(e))\n }\n }\n } finally {\n unsub()\n }\n}\n\n/**\n * Send a Msg and capture any synchronous throw into `errors` rather\n * than letting it propagate to the WS RPC layer. By the time `send`\n * has thrown, the reducer may have partially run (state can advance),\n * but bindings or downstream effects on the same commit may have\n * crashed mid-flight. From the agent's POV: the dispatch IS dispatched,\n * the state diff reflects what actually changed, and `drain.errors`\n * reports the in-flight crash. That's strictly more useful than HTTP\n * 500, which the agent reads as \"the dispatch never happened.\"\n */\nfunction safeSend(\n host: SendMessageHost,\n msg: { type: string; [k: string]: unknown },\n errors: LapDrainMeta['errors'],\n): void {\n try {\n host.send(msg)\n } catch (e) {\n errors.push(toDrainError(e))\n }\n}\n\nfunction safeSendAndFlush(\n host: SendMessageHost,\n msg: { type: string; [k: string]: unknown },\n errors: LapDrainMeta['errors'],\n): void {\n try {\n host.send(msg)\n } catch (e) {\n errors.push(toDrainError(e))\n return // can't flush something we never sent\n }\n try {\n host.flush()\n } catch (e) {\n errors.push(toDrainError(e))\n }\n}\n\nfunction toDrainError(e: unknown): LapDrainMeta['errors'][number] {\n if (e instanceof Error) {\n const stack = e.stack ? e.stack.split('\\n').slice(0, 8).join('\\n') : undefined\n return stack !== undefined\n ? { kind: 'error', message: `${e.name}: ${e.message}`, stack }\n : { kind: 'error', message: `${e.name}: ${e.message}` }\n }\n return { kind: 'error', message: String(e) }\n}\n\nfunction mergeDrainErrors(\n fromDispatch: LapDrainMeta['errors'],\n fromHost: LapDrainMeta['errors'] | undefined,\n): LapDrainMeta['errors'] {\n if (!fromHost || fromHost.length === 0) return fromDispatch\n if (fromDispatch.length === 0) return fromHost\n return [...fromDispatch, ...fromHost]\n}\n\nfunction dispatched(\n host: SendMessageHost,\n drain: LapDrainMeta,\n prevState: unknown,\n includeState: boolean,\n validationWarnings: NonNullable<LapDrainMeta['warnings']> = [],\n): LapMessageResponse {\n const stateAfter = host.getState()\n const drainWithWarnings: LapDrainMeta =\n validationWarnings.length === 0 ? drain : { ...drain, warnings: validationWarnings }\n const base = {\n status: 'dispatched' as const,\n stateDiff: computeStateDiff(prevState, stateAfter),\n actions: handleListActions(host).actions,\n drain: drainWithWarnings,\n }\n return includeState ? { ...base, stateAfter } : base\n}\n\nfunction emptyDrain(): LapDrainMeta {\n return { effectsObserved: 0, durationMs: 0, timedOut: false, errors: [] }\n}\n\nfunction awaitQuietOrMsg(\n budgetMs: number,\n registerWake: (resolve: (r: 'msg' | 'timeout') => void) => void,\n): Promise<'msg' | 'timeout'> {\n return new Promise<'msg' | 'timeout'>((resolve) => {\n let settled = false\n const guarded = (r: 'msg' | 'timeout') => {\n if (settled) return\n settled = true\n resolve(r)\n }\n registerWake(guarded)\n setTimeout(() => guarded('timeout'), budgetMs)\n })\n}\n\nfunction now(): number {\n return typeof performance !== 'undefined' && typeof performance.now === 'function'\n ? performance.now()\n : Date.now()\n}\n\n// Helper types for external callers that want the dispatched envelope.\nexport type DispatchedEnvelope = Extract<LapMessageResponse, { status: 'dispatched' }>\nexport type { LapActionsResponse }\n"]}
|
|
@@ -84,6 +84,25 @@ export type WouldDispatchResult = {
|
|
|
84
84
|
status: 'rejected';
|
|
85
85
|
reason: 'schema-mismatch';
|
|
86
86
|
errors: ValidationError[];
|
|
87
|
+
} | {
|
|
88
|
+
/**
|
|
89
|
+
* The reducer threw while running against the candidate Msg.
|
|
90
|
+
* State is poisoned (or the reducer has a latent bug); a real
|
|
91
|
+
* `send_message` would land state change + an error in
|
|
92
|
+
* `drain.errors`. `would_dispatch` mirrors that contract: the
|
|
93
|
+
* "diff" is empty (we couldn't compute it), and the throw text
|
|
94
|
+
* is surfaced so the agent knows to back off rather than
|
|
95
|
+
* retrying the same payload.
|
|
96
|
+
*
|
|
97
|
+
* Distinct from `'rejected'` because the agent learned something
|
|
98
|
+
* different: the reducer DOES accept this Msg shape but errors
|
|
99
|
+
* downstream. Often that means earlier state needs fixing first
|
|
100
|
+
* (a previously-dispatched bad Msg poisoned a derived path),
|
|
101
|
+
* not that this candidate is malformed.
|
|
102
|
+
*/
|
|
103
|
+
status: 'reducer-threw';
|
|
104
|
+
message: string;
|
|
105
|
+
stack?: string;
|
|
87
106
|
};
|
|
88
107
|
export declare function handleWouldDispatch(host: WouldDispatchHost, args: WouldDispatchArgs): WouldDispatchResult;
|
|
89
108
|
//# sourceMappingURL=would-dispatch.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"would-dispatch.d.ts","sourceRoot":"","sources":["../../../src/client/rpc/would-dispatch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC7E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAEnD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,IAAI,OAAO,CAAA;IACnB;;;;;;;OAOG;IACH,UAAU,CAAC,GAAG,EAAE;QACd,IAAI,EAAE,MAAM,CAAA;QACZ,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KACrB,GAAG;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,OAAO,EAAE,CAAA;KAAE,GAAG,IAAI,CAAA;IACjD;;;;;;;;OAQG;IACH,YAAY,CAAC,IAAI,cAAc,GAAG,IAAI,CAAA;CACvC,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAA;CAC5C,CAAA;AAED,MAAM,MAAM,mBAAmB,GAC3B;IACE,MAAM,EAAE,WAAW,CAAA;IACnB,mEAAmE;IACnE,SAAS,EAAE,SAAS,CAAA;IACpB,0EAA0E;IAC1E,OAAO,EAAE,OAAO,EAAE,CAAA;CACnB,GACD;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,SAAS,GAAG,aAAa,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1E;IACE;;;;;;OAMG;IACH,MAAM,EAAE,UAAU,CAAA;IAClB,MAAM,EAAE,iBAAiB,CAAA;IACzB,MAAM,EAAE,eAAe,EAAE,CAAA;CAC1B,CAAA;AAEL,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,iBAAiB,EACvB,IAAI,EAAE,iBAAiB,GACtB,mBAAmB,
|
|
1
|
+
{"version":3,"file":"would-dispatch.d.ts","sourceRoot":"","sources":["../../../src/client/rpc/would-dispatch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AACpD,OAAO,EAAmB,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC7E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAEnD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,IAAI,OAAO,CAAA;IACnB;;;;;;;OAOG;IACH,UAAU,CAAC,GAAG,EAAE;QACd,IAAI,EAAE,MAAM,CAAA;QACZ,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KACrB,GAAG;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,OAAO,EAAE,CAAA;KAAE,GAAG,IAAI,CAAA;IACjD;;;;;;;;OAQG;IACH,YAAY,CAAC,IAAI,cAAc,GAAG,IAAI,CAAA;CACvC,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAA;CAC5C,CAAA;AAED,MAAM,MAAM,mBAAmB,GAC3B;IACE,MAAM,EAAE,WAAW,CAAA;IACnB,mEAAmE;IACnE,SAAS,EAAE,SAAS,CAAA;IACpB,0EAA0E;IAC1E,OAAO,EAAE,OAAO,EAAE,CAAA;CACnB,GACD;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,SAAS,GAAG,aAAa,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1E;IACE;;;;;;OAMG;IACH,MAAM,EAAE,UAAU,CAAA;IAClB,MAAM,EAAE,iBAAiB,CAAA;IACzB,MAAM,EAAE,eAAe,EAAE,CAAA;CAC1B,GACD;IACE;;;;;;;;;;;;;;OAcG;IACH,MAAM,EAAE,eAAe,CAAA;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAEL,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,iBAAiB,EACvB,IAAI,EAAE,iBAAiB,GACtB,mBAAmB,CAyCrB"}
|
|
@@ -13,7 +13,20 @@ export function handleWouldDispatch(host, args) {
|
|
|
13
13
|
if (!validation.ok) {
|
|
14
14
|
return { status: 'rejected', reason: 'schema-mismatch', errors: validation.errors };
|
|
15
15
|
}
|
|
16
|
-
|
|
16
|
+
let result;
|
|
17
|
+
try {
|
|
18
|
+
result = host.runReducer(args.msg);
|
|
19
|
+
}
|
|
20
|
+
catch (e) {
|
|
21
|
+
// The reducer threw — same Phase-5 contract as `send_message`:
|
|
22
|
+
// surface the throw as structured data instead of letting it
|
|
23
|
+
// become an HTTP 500 the agent reads as "transport failure."
|
|
24
|
+
const err = e instanceof Error ? e : new Error(String(e));
|
|
25
|
+
const stack = err.stack ? err.stack.split('\n').slice(0, 8).join('\n') : undefined;
|
|
26
|
+
return stack !== undefined
|
|
27
|
+
? { status: 'reducer-threw', message: `${err.name}: ${err.message}`, stack }
|
|
28
|
+
: { status: 'reducer-threw', message: `${err.name}: ${err.message}` };
|
|
29
|
+
}
|
|
17
30
|
if (result === null) {
|
|
18
31
|
return {
|
|
19
32
|
status: 'rejected',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"would-dispatch.js","sourceRoot":"","sources":["../../../src/client/rpc/would-dispatch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAEtD,OAAO,EAAE,eAAe,EAAwB,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"would-dispatch.js","sourceRoot":"","sources":["../../../src/client/rpc/would-dispatch.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAA;AAEtD,OAAO,EAAE,eAAe,EAAwB,MAAM,uBAAuB,CAAA;AAuG7E,MAAM,UAAU,mBAAmB,CACjC,IAAuB,EACvB,IAAuB;IAEvB,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACnD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,2BAA2B,EAAE,CAAA;IACvF,CAAC;IAED,oEAAoE;IACpE,mEAAmE;IACnE,+DAA+D;IAC/D,cAAc;IACd,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,EAAE,EAAE,IAAI,IAAI,CAAA;IAC5C,MAAM,UAAU,GAAG,eAAe,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;IACpD,IAAI,CAAC,UAAU,CAAC,EAAE,EAAE,CAAC;QACnB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAA;IACrF,CAAC;IAED,IAAI,MAAqD,CAAA;IACzD,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IACpC,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,+DAA+D;QAC/D,6DAA6D;QAC7D,6DAA6D;QAC7D,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;QACzD,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;QAClF,OAAO,KAAK,KAAK,SAAS;YACxB,CAAC,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE;YAC5E,CAAC,CAAC,EAAE,MAAM,EAAE,eAAe,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,OAAO,EAAE,EAAE,CAAA;IACzE,CAAC;IACD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO;YACL,MAAM,EAAE,UAAU;YAClB,MAAM,EAAE,aAAa;YACrB,MAAM,EAAE,6DAA6D;SACtE,CAAA;IACH,CAAC;IACD,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAA;IACjC,OAAO;QACL,MAAM,EAAE,WAAW;QACnB,SAAS,EAAE,gBAAgB,CAAC,SAAS,EAAE,MAAM,CAAC,KAAK,CAAC;QACpD,OAAO,EAAE,MAAM,CAAC,OAAO;KACxB,CAAA;AACH,CAAC","sourcesContent":["import { computeStateDiff } from '../../state-diff.js'\nimport type { StateDiff } from '../../state-diff.js'\nimport { validatePayload, type ValidationError } from './validate-payload.js'\nimport type { MsgSchemaShape } from '../factory.js'\n\n/**\n * Predict the result of dispatching `msg` without actually applying\n * it. Runs the reducer in isolation against the current state,\n * returns the would-be diff and the would-fire effects, but doesn't\n * commit or run anything. Lets the agent reason about a candidate\n * action before pulling the trigger:\n *\n * - \"If I dispatch X, what will change?\" — read `stateDiff`.\n * - \"Will it fire effects? Which ones?\" — read `effects`.\n * - \"Should I batch?\" — predict each, see whether the diffs\n * compose without conflict.\n *\n * The contract is bounded by TEA's purity assumption: the reducer\n * must be a pure function `(state, msg) → [newState, effects]`. LLui\n * reducers are pure by convention (the runtime never re-runs them\n * speculatively for any other reason, so impurity would already be\n * a latent bug). Apps whose reducers branch on `Date.now()` or read\n * `localStorage` will see prediction drift from real dispatch by\n * exactly that amount of impurity — usually negligible, sometimes\n * surprising; document at the call site.\n *\n * **No effects fire.** The returned `effects` array is the literal\n * effect descriptors the reducer produced — what `onEffect` would\n * have received. The agent reads them; the runtime ignores them.\n * This is the entire reason the tool exists separately from\n * `send_message`: a real dispatch hits the cloud / analytics /\n * persistence; a predicted one doesn't.\n */\nexport type WouldDispatchHost = {\n getState(): unknown\n /**\n * Run the reducer in isolation. `[newState, effects]` shape.\n * Implemented by the AppHandle as a thin wrapper around\n * `inst.def.update(state, msg)` — no flush, no subscribe, no\n * commit. Implementations that can't run the reducer (e.g.\n * test harnesses with no live instance) return null and the\n * tool reports unsupported.\n */\n runReducer(msg: {\n type: string\n [k: string]: unknown\n }): { state: unknown; effects: unknown[] } | null\n /**\n * The compiler-emitted Msg schema, when available. Used to\n * validate the candidate payload structurally before running the\n * reducer — wrong enum values, missing discriminants, primitive\n * type mismatches surface as structured errors the agent can fix\n * in one round trip instead of probing field by field. Hosts\n * without schema metadata return null and the validator skips,\n * keeping the tool permissive (the reducer still runs).\n */\n getMsgSchema?(): MsgSchemaShape | null\n}\n\nexport type WouldDispatchArgs = {\n msg: { type: string; [k: string]: unknown }\n}\n\nexport type WouldDispatchResult =\n | {\n status: 'predicted'\n /** Diff from current state to the predicted post-reducer state. */\n stateDiff: StateDiff\n /** Effects the reducer would emit. Order matches the reducer's return. */\n effects: unknown[]\n }\n | { status: 'rejected'; reason: 'invalid' | 'unsupported'; detail?: string }\n | {\n /**\n * The candidate Msg failed schema validation BEFORE the reducer\n * ran. `errors` lists every structural mismatch with a path-\n * keyed description. The agent reads this as \"fix these fields\n * and retry\" — no reducer side-effects to roll back, no state\n * change to predict around.\n */\n status: 'rejected'\n reason: 'schema-mismatch'\n errors: ValidationError[]\n }\n | {\n /**\n * The reducer threw while running against the candidate Msg.\n * State is poisoned (or the reducer has a latent bug); a real\n * `send_message` would land state change + an error in\n * `drain.errors`. `would_dispatch` mirrors that contract: the\n * \"diff\" is empty (we couldn't compute it), and the throw text\n * is surfaced so the agent knows to back off rather than\n * retrying the same payload.\n *\n * Distinct from `'rejected'` because the agent learned something\n * different: the reducer DOES accept this Msg shape but errors\n * downstream. Often that means earlier state needs fixing first\n * (a previously-dispatched bad Msg poisoned a derived path),\n * not that this candidate is malformed.\n */\n status: 'reducer-threw'\n message: string\n stack?: string\n }\n\nexport function handleWouldDispatch(\n host: WouldDispatchHost,\n args: WouldDispatchArgs,\n): WouldDispatchResult {\n if (!args.msg || typeof args.msg.type !== 'string') {\n return { status: 'rejected', reason: 'invalid', detail: 'msg.type must be a string' }\n }\n\n // Schema-driven preflight. Permissive when no schema is available —\n // the reducer still validates semantically. With a schema, surface\n // structured errors so the agent doesn't iterate one field per\n // round trip.\n const schema = host.getMsgSchema?.() ?? null\n const validation = validatePayload(args.msg, schema)\n if (!validation.ok) {\n return { status: 'rejected', reason: 'schema-mismatch', errors: validation.errors }\n }\n\n let result: { state: unknown; effects: unknown[] } | null\n try {\n result = host.runReducer(args.msg)\n } catch (e) {\n // The reducer threw — same Phase-5 contract as `send_message`:\n // surface the throw as structured data instead of letting it\n // become an HTTP 500 the agent reads as \"transport failure.\"\n const err = e instanceof Error ? e : new Error(String(e))\n const stack = err.stack ? err.stack.split('\\n').slice(0, 8).join('\\n') : undefined\n return stack !== undefined\n ? { status: 'reducer-threw', message: `${err.name}: ${err.message}`, stack }\n : { status: 'reducer-threw', message: `${err.name}: ${err.message}` }\n }\n if (result === null) {\n return {\n status: 'rejected',\n reason: 'unsupported',\n detail: 'host does not expose a reducer (no live component instance)',\n }\n }\n const prevState = host.getState()\n return {\n status: 'predicted',\n stateDiff: computeStateDiff(prevState, result.state),\n effects: result.effects,\n }\n}\n"]}
|
|
@@ -6,7 +6,7 @@ import { type SendMessageHost } from './rpc/send-message.js';
|
|
|
6
6
|
import { type ListActionsHost } from './rpc/list-actions.js';
|
|
7
7
|
import { type QueryDomHost } from './rpc/query-dom.js';
|
|
8
8
|
import { type DescribeVisibleHost } from './rpc/describe-visible-content.js';
|
|
9
|
-
import { type DescribeContextHost } from './rpc/describe-context.js';
|
|
9
|
+
import { type DescribeContextHost, type LastDispatchOutcome } from './rpc/describe-context.js';
|
|
10
10
|
import { type ObserveHost } from './rpc/observe.js';
|
|
11
11
|
export interface WsLike {
|
|
12
12
|
send(data: string): void;
|
|
@@ -39,6 +39,15 @@ export type WsClientOpts = {
|
|
|
39
39
|
* server regardless.
|
|
40
40
|
*/
|
|
41
41
|
onLogEntry?: (entry: LogEntry) => void;
|
|
42
|
+
/**
|
|
43
|
+
* Called with the outcome of every `send_message` rpc — `dispatched`
|
|
44
|
+
* (with optional errors / warnings), `rejected` (with errors), or
|
|
45
|
+
* `reducer-threw`. The factory uses this to maintain a "last
|
|
46
|
+
* outcome" snapshot that `describe_context` injects as a synthetic
|
|
47
|
+
* hint, so apps don't have to maintain their own
|
|
48
|
+
* `lastDispatchError` state field.
|
|
49
|
+
*/
|
|
50
|
+
onDispatchOutcome?: (outcome: LastDispatchOutcome | null) => void;
|
|
42
51
|
};
|
|
43
52
|
/**
|
|
44
53
|
* Wires up a WebSocket to serve rpc requests from the server. See spec §9.4.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws-client.d.ts","sourceRoot":"","sources":["../../src/client/ws-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,UAAU,EACV,QAAQ,EAGT,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtE,OAAO,EAAoB,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC5E,OAAO,EAAuB,KAAK,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AACrF,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC/E,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC/E,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtE,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,mCAAmC,CAAA;AAC1C,OAAO,
|
|
1
|
+
{"version":3,"file":"ws-client.d.ts","sourceRoot":"","sources":["../../src/client/ws-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,UAAU,EACV,QAAQ,EAGT,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtE,OAAO,EAAoB,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAC5E,OAAO,EAAuB,KAAK,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AACrF,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC/E,OAAO,EAAqB,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAC/E,OAAO,EAAkB,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACtE,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,mBAAmB,EACzB,MAAM,2BAA2B,CAAA;AAClC,OAAO,EAAiB,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAElE,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,KAAK,IAAI,IAAI,CAAA;IACb,gBAAgB,CAAC,KAAK,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAAA;KAAE,KAAK,IAAI,GAAG,IAAI,CAAA;IACxF,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,EAAE,CAAC,EAAE,MAAM,IAAI,GAAG,IAAI,CAAA;CAC/D;AAED,MAAM,MAAM,QAAQ,GAAG,YAAY,GACjC,cAAc,GACd,eAAe,GACf,eAAe,GACf,YAAY,GACZ,mBAAmB,GACnB,mBAAmB,GACnB,WAAW,GACX,iBAAiB,CAAA;AAEnB,MAAM,MAAM,YAAY,GAAG,MAAM,UAAU,CAAA;AAE3C,MAAM,MAAM,QAAQ,GAAG;IACrB,kFAAkF;IAClF,cAAc,CACZ,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,WAAW,GAAG,gBAAgB,EACvC,UAAU,CAAC,EAAE,OAAO,GACnB,IAAI,CAAA;IACP,kFAAkF;IAClF,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,IAAI,CAAA;IACxD,kGAAkG;IAClG,aAAa,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAA;IACpC,gCAAgC;IAChC,KAAK,IAAI,IAAI,CAAA;CACd,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IACxB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAA;IACtC;;;;;;;OAOG;IACH,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,KAAK,IAAI,CAAA;CAClE,CAAA;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,EAAE,EAAE,MAAM,EACV,GAAG,EAAE,QAAQ,EACb,KAAK,EAAE,YAAY,EACnB,IAAI,GAAE,YAAiB,GACtB,QAAQ,CAqGV"}
|
package/dist/client/ws-client.js
CHANGED
|
@@ -5,7 +5,7 @@ import { handleSendMessage } from './rpc/send-message.js';
|
|
|
5
5
|
import { handleListActions } from './rpc/list-actions.js';
|
|
6
6
|
import { handleQueryDom } from './rpc/query-dom.js';
|
|
7
7
|
import { handleDescribeVisibleContent, } from './rpc/describe-visible-content.js';
|
|
8
|
-
import { handleDescribeContext } from './rpc/describe-context.js';
|
|
8
|
+
import { handleDescribeContext, } from './rpc/describe-context.js';
|
|
9
9
|
import { handleObserve } from './rpc/observe.js';
|
|
10
10
|
/**
|
|
11
11
|
* Wires up a WebSocket to serve rpc requests from the server. See spec §9.4.
|
|
@@ -80,6 +80,14 @@ export function attachWsClient(ws, rpc, hello, opts = {}) {
|
|
|
80
80
|
if (frame.tool === 'send_message' && rpcErr === null && isDispatchedResult(result)) {
|
|
81
81
|
logEntry.stateDiff = result.stateDiff;
|
|
82
82
|
}
|
|
83
|
+
if (frame.tool === 'send_message') {
|
|
84
|
+
// Capture the outcome so `describe_context` can surface it as a
|
|
85
|
+
// synthetic "Last dispatch …" hint. Apps used to roll their
|
|
86
|
+
// own `lastDispatchError` field; the framework owns it now.
|
|
87
|
+
const outcome = extractDispatchOutcome(frame.args, result, rpcErr);
|
|
88
|
+
if (outcome !== null)
|
|
89
|
+
opts.onDispatchOutcome?.(outcome);
|
|
90
|
+
}
|
|
83
91
|
opts.onLogEntry?.(logEntry);
|
|
84
92
|
ws.send(JSON.stringify({ t: 'log-append', entry: logEntry }));
|
|
85
93
|
});
|
|
@@ -169,6 +177,65 @@ function isDispatchedResult(result) {
|
|
|
169
177
|
result.status === 'dispatched' &&
|
|
170
178
|
Array.isArray(result.stateDiff));
|
|
171
179
|
}
|
|
180
|
+
/**
|
|
181
|
+
* Build a `LastDispatchOutcome` snapshot from the send_message rpc's
|
|
182
|
+
* result/error pair. Returns null when the args don't carry a variant
|
|
183
|
+
* name (malformed dispatch — would never have been logged anyway).
|
|
184
|
+
*
|
|
185
|
+
* Status mapping:
|
|
186
|
+
* - { status: 'dispatched' } → 'dispatched' (with errors/warnings)
|
|
187
|
+
* - { status: 'rejected' } → 'rejected'
|
|
188
|
+
* - rpcErr (rpc handler threw, including reducer-throw on the predict path) → 'reducer-threw'
|
|
189
|
+
*
|
|
190
|
+
* The outcome is consumed by `describe_context` to prepend a synthetic
|
|
191
|
+
* "Last dispatch …" hint. Clean dispatched outcomes (no errors, no
|
|
192
|
+
* warnings) still get tracked — `formatLastOutcomeHint` decides whether
|
|
193
|
+
* to surface them.
|
|
194
|
+
*/
|
|
195
|
+
function extractDispatchOutcome(args, result, rpcErr) {
|
|
196
|
+
const variant = extractVariant('send_message', args);
|
|
197
|
+
if (variant === undefined)
|
|
198
|
+
return null;
|
|
199
|
+
const at = Date.now();
|
|
200
|
+
if (rpcErr !== null) {
|
|
201
|
+
// The rpc handler itself threw — most often this is a reducer
|
|
202
|
+
// throw that bubbled past the catch in send-message.ts (e.g.
|
|
203
|
+
// would_dispatch's reducer threw in a non-send-message tool that
|
|
204
|
+
// shares the path). Treat as `reducer-threw` so the agent reads
|
|
205
|
+
// "state may be partially advanced; observe before retrying."
|
|
206
|
+
return {
|
|
207
|
+
variant,
|
|
208
|
+
status: 'reducer-threw',
|
|
209
|
+
errors: [{ message: rpcErr.detail ?? rpcErr.code ?? 'rpc handler threw' }],
|
|
210
|
+
at,
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
if (result === null || typeof result !== 'object')
|
|
214
|
+
return null;
|
|
215
|
+
const r = result;
|
|
216
|
+
if (r.status === 'dispatched') {
|
|
217
|
+
const errors = r.drain?.errors ?? [];
|
|
218
|
+
const warnings = r.drain?.warnings ?? [];
|
|
219
|
+
const out = { variant, status: 'dispatched', at };
|
|
220
|
+
if (errors.length > 0)
|
|
221
|
+
out.errors = errors;
|
|
222
|
+
if (warnings.length > 0)
|
|
223
|
+
out.warnings = warnings;
|
|
224
|
+
return out;
|
|
225
|
+
}
|
|
226
|
+
if (r.status === 'rejected') {
|
|
227
|
+
const detail = r.detail ?? 'rejected';
|
|
228
|
+
return {
|
|
229
|
+
variant,
|
|
230
|
+
status: 'rejected',
|
|
231
|
+
errors: [{ message: detail }],
|
|
232
|
+
at,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
// Other statuses (`pending-confirmation`, etc.) — don't update the
|
|
236
|
+
// last-outcome cache. The dispatch hasn't really concluded yet.
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
172
239
|
function extractVariant(tool, args) {
|
|
173
240
|
if (tool === 'send_message') {
|
|
174
241
|
const a = args;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws-client.js","sourceRoot":"","sources":["../../src/client/ws-client.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ClientFrame,\n ServerFrame,\n HelloFrame,\n LogEntry,\n LogKind,\n MessageAnnotations,\n} from '../protocol.js'\nimport { handleGetState, type GetStateHost } from './rpc/get-state.js'\nimport { handleQueryState, type QueryStateHost } from './rpc/query-state.js'\nimport { handleWouldDispatch, type WouldDispatchHost } from './rpc/would-dispatch.js'\nimport { handleSendMessage, type SendMessageHost } from './rpc/send-message.js'\nimport { handleListActions, type ListActionsHost } from './rpc/list-actions.js'\nimport { handleQueryDom, type QueryDomHost } from './rpc/query-dom.js'\nimport {\n handleDescribeVisibleContent,\n type DescribeVisibleHost,\n} from './rpc/describe-visible-content.js'\nimport { handleDescribeContext, type DescribeContextHost } from './rpc/describe-context.js'\nimport { handleObserve, type ObserveHost } from './rpc/observe.js'\n\nexport interface WsLike {\n send(data: string): void\n close(): void\n addEventListener(event: 'message', h: (e: { data: string | ArrayBuffer }) => void): void\n addEventListener(event: 'open' | 'close', h: () => void): void\n}\n\nexport type RpcHosts = GetStateHost &\n QueryStateHost &\n SendMessageHost &\n ListActionsHost &\n QueryDomHost &\n DescribeVisibleHost &\n DescribeContextHost &\n ObserveHost &\n WouldDispatchHost\n\nexport type HelloBuilder = () => HelloFrame\n\nexport type WsClient = {\n /** Resolve a pending confirmation; emits confirm-resolved frame to the server. */\n resolveConfirm(\n confirmId: string,\n outcome: 'confirmed' | 'user-cancelled',\n stateAfter?: unknown,\n ): void\n /** Emit a state-update frame so the server can resolve waitForChange promises. */\n emitStateUpdate(path: string, stateAfter: unknown): void\n /** Emit a log-append frame so the server can mirror client-observed actions to the audit sink. */\n emitLogAppend(entry: LogEntry): void\n /** Close the socket cleanly. */\n close(): void\n}\n\nexport type WsClientOpts = {\n /** Called once when the server sends an `{t: 'active'}` frame. Idempotent. */\n onActivated?: () => void\n /**\n * Called with every LogEntry emitted by the ws-client (one per rpc\n * dispatched or errored). Used by the factory to mirror the entries\n * into the app's local `agent.log` slice so the UI can show activity.\n * The ws-client still sends the outbound `log-append` frame to the\n * server regardless.\n */\n onLogEntry?: (entry: LogEntry) => void\n}\n\n/**\n * Wires up a WebSocket to serve rpc requests from the server. See spec §9.4.\n */\nexport function attachWsClient(\n ws: WsLike,\n rpc: RpcHosts,\n hello: HelloBuilder,\n opts: WsClientOpts = {},\n): WsClient {\n let activated = false\n ws.addEventListener('open', () => {\n ws.send(JSON.stringify(hello()))\n })\n ws.addEventListener('message', async (ev) => {\n let frame: ServerFrame\n try {\n const raw = typeof ev.data === 'string' ? ev.data : new TextDecoder().decode(ev.data)\n frame = JSON.parse(raw) as ServerFrame\n } catch {\n return\n }\n if (frame.t === 'revoked') {\n ws.close()\n return\n }\n if (frame.t === 'active') {\n if (!activated) {\n activated = true\n opts.onActivated?.()\n }\n return\n }\n if (frame.t !== 'rpc') return\n let result: unknown\n let rpcErr: { code?: string; detail?: string } | null = null\n try {\n result = await dispatch(frame.tool, frame.args, rpc)\n const reply: ClientFrame = { t: 'rpc-reply', id: frame.id, result }\n ws.send(JSON.stringify(reply))\n } catch (e: unknown) {\n rpcErr = e as { code?: string; detail?: string }\n // When a plain JS exception bubbles up (TypeError, RangeError, etc.),\n // rpcErr has no .code/.detail. Enrich the detail with the actual\n // message + stack so the server/Claude can see the real cause.\n const detail =\n rpcErr.detail ??\n (e instanceof Error\n ? `${e.name}: ${e.message}${e.stack ? '\\n' + e.stack.split('\\n').slice(0, 5).join('\\n') : ''}`\n : undefined)\n const errFrame: ClientFrame = {\n t: 'rpc-error',\n id: frame.id,\n code: rpcErr.code ?? 'internal',\n detail,\n }\n ws.send(JSON.stringify(errFrame))\n // Also log to the browser console so operators see the real cause even\n // when the server/Claude just show \"internal\".\n console.error(`[llui-agent] rpc handler threw for ${frame.tool}:`, e)\n }\n const kind = getLogKindForTool(frame.tool, result, rpcErr)\n const logEntry: LogEntry = {\n id: frame.id,\n at: Date.now(),\n kind,\n variant: extractVariant(frame.tool, frame.args),\n intent: buildIntent(frame.tool, frame.args, rpc.getMsgAnnotations()),\n }\n // For successful send_message dispatches, the stateDiff is part\n // of the response. Lifting it into the log entry means the agent\n // can read its own past actions with full \"what changed\" detail\n // without re-querying state — essential for self-correcting\n // behavior over multi-step flows.\n if (frame.tool === 'send_message' && rpcErr === null && isDispatchedResult(result)) {\n logEntry.stateDiff = result.stateDiff\n }\n opts.onLogEntry?.(logEntry)\n ws.send(JSON.stringify({ t: 'log-append', entry: logEntry } satisfies ClientFrame))\n })\n\n return {\n resolveConfirm(confirmId, outcome, stateAfter) {\n const frame: ClientFrame = {\n t: 'confirm-resolved',\n confirmId,\n outcome,\n stateAfter,\n }\n ws.send(JSON.stringify(frame))\n },\n emitStateUpdate(path, stateAfter) {\n const frame: ClientFrame = { t: 'state-update', path, stateAfter }\n ws.send(JSON.stringify(frame))\n },\n emitLogAppend(entry) {\n const frame: ClientFrame = { t: 'log-append', entry }\n ws.send(JSON.stringify(frame))\n },\n close() {\n ws.close()\n },\n }\n}\n\nasync function dispatch(tool: string, args: unknown, rpc: RpcHosts): Promise<unknown> {\n switch (tool) {\n case 'get_state':\n return handleGetState(rpc, (args ?? {}) as { path?: string })\n case 'query_state':\n return handleQueryState(rpc, (args ?? {}) as { path: string })\n case 'list_actions':\n return handleListActions(rpc)\n case 'send_message':\n return handleSendMessage(rpc, args as never)\n case 'query_dom':\n return handleQueryDom(rpc, args as never)\n case 'describe_visible_content':\n return handleDescribeVisibleContent(rpc)\n case 'describe_context':\n return handleDescribeContext(rpc)\n case 'observe':\n return handleObserve(rpc)\n case 'would_dispatch':\n return handleWouldDispatch(rpc, args as never)\n default:\n throw { code: 'invalid', detail: `unknown tool: ${tool}` }\n }\n}\n\nconst READ_TOOLS = new Set([\n 'get_state',\n 'query_state',\n 'list_actions',\n 'describe_context',\n 'query_dom',\n 'describe_visible_content',\n 'observe',\n 'would_dispatch',\n])\n\nfunction getLogKindForTool(\n tool: string,\n result: unknown,\n err: { code?: string; detail?: string } | null,\n): LogKind {\n if (err !== null) return 'error'\n if (tool === 'send_message') {\n const r = result as { status?: string } | null\n const status = r?.status\n if (status === 'dispatched' || status === 'confirmed') return 'dispatched'\n if (status === 'pending-confirmation') return 'proposed'\n if (status === 'rejected') return 'blocked'\n return 'dispatched'\n }\n if (READ_TOOLS.has(tool)) return 'read'\n return 'read'\n}\n\n/**\n * Type guard for the `dispatched` shape of `LapMessageResponse`.\n * Used to lift the stateDiff into the log entry without polluting\n * the type chain with cross-cutting imports.\n */\nfunction isDispatchedResult(\n result: unknown,\n): result is { status: 'dispatched'; stateDiff: import('../state-diff.js').StateDiff } {\n return (\n result !== null &&\n typeof result === 'object' &&\n (result as { status?: unknown }).status === 'dispatched' &&\n Array.isArray((result as { stateDiff?: unknown }).stateDiff)\n )\n}\n\nfunction extractVariant(tool: string, args: unknown): string | undefined {\n if (tool === 'send_message') {\n const a = args as { msg?: { type?: string } } | null\n const t = a?.msg?.type\n return typeof t === 'string' ? t : undefined\n }\n return undefined\n}\n\n// Human-readable label for each rpc. For send_message, prefer the @intent\n// annotation authored on the Msg union; fall back to the raw variant name.\n// For read tools, return a short fixed label so the activity feed doesn't\n// show opaque tool ids like \"describe_visible_content\".\nfunction buildIntent(\n tool: string,\n args: unknown,\n annotations: Record<string, MessageAnnotations> | null,\n): string {\n if (tool === 'send_message') {\n const a = args as { msg?: { type?: string } } | null\n const variant = typeof a?.msg?.type === 'string' ? a.msg.type : undefined\n const annotated = variant ? annotations?.[variant]?.intent : null\n if (annotated) return annotated\n return variant ?? 'Send message'\n }\n if (tool === 'get_state') return 'Read app state'\n if (tool === 'query_state') return 'Read state slice'\n if (tool === 'list_actions') return 'List available actions'\n if (tool === 'describe_context') return 'Read current context'\n if (tool === 'describe_visible_content') return 'Read visible content'\n if (tool === 'query_dom') {\n const a = args as { name?: string } | null\n return a?.name ? `Query DOM: ${a.name}` : 'Query DOM'\n }\n return tool\n}\n"]}
|
|
1
|
+
{"version":3,"file":"ws-client.js","sourceRoot":"","sources":["../../src/client/ws-client.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n ClientFrame,\n ServerFrame,\n HelloFrame,\n LogEntry,\n LogKind,\n MessageAnnotations,\n} from '../protocol.js'\nimport { handleGetState, type GetStateHost } from './rpc/get-state.js'\nimport { handleQueryState, type QueryStateHost } from './rpc/query-state.js'\nimport { handleWouldDispatch, type WouldDispatchHost } from './rpc/would-dispatch.js'\nimport { handleSendMessage, type SendMessageHost } from './rpc/send-message.js'\nimport { handleListActions, type ListActionsHost } from './rpc/list-actions.js'\nimport { handleQueryDom, type QueryDomHost } from './rpc/query-dom.js'\nimport {\n handleDescribeVisibleContent,\n type DescribeVisibleHost,\n} from './rpc/describe-visible-content.js'\nimport {\n handleDescribeContext,\n type DescribeContextHost,\n type LastDispatchOutcome,\n} from './rpc/describe-context.js'\nimport { handleObserve, type ObserveHost } from './rpc/observe.js'\n\nexport interface WsLike {\n send(data: string): void\n close(): void\n addEventListener(event: 'message', h: (e: { data: string | ArrayBuffer }) => void): void\n addEventListener(event: 'open' | 'close', h: () => void): void\n}\n\nexport type RpcHosts = GetStateHost &\n QueryStateHost &\n SendMessageHost &\n ListActionsHost &\n QueryDomHost &\n DescribeVisibleHost &\n DescribeContextHost &\n ObserveHost &\n WouldDispatchHost\n\nexport type HelloBuilder = () => HelloFrame\n\nexport type WsClient = {\n /** Resolve a pending confirmation; emits confirm-resolved frame to the server. */\n resolveConfirm(\n confirmId: string,\n outcome: 'confirmed' | 'user-cancelled',\n stateAfter?: unknown,\n ): void\n /** Emit a state-update frame so the server can resolve waitForChange promises. */\n emitStateUpdate(path: string, stateAfter: unknown): void\n /** Emit a log-append frame so the server can mirror client-observed actions to the audit sink. */\n emitLogAppend(entry: LogEntry): void\n /** Close the socket cleanly. */\n close(): void\n}\n\nexport type WsClientOpts = {\n /** Called once when the server sends an `{t: 'active'}` frame. Idempotent. */\n onActivated?: () => void\n /**\n * Called with every LogEntry emitted by the ws-client (one per rpc\n * dispatched or errored). Used by the factory to mirror the entries\n * into the app's local `agent.log` slice so the UI can show activity.\n * The ws-client still sends the outbound `log-append` frame to the\n * server regardless.\n */\n onLogEntry?: (entry: LogEntry) => void\n /**\n * Called with the outcome of every `send_message` rpc — `dispatched`\n * (with optional errors / warnings), `rejected` (with errors), or\n * `reducer-threw`. The factory uses this to maintain a \"last\n * outcome\" snapshot that `describe_context` injects as a synthetic\n * hint, so apps don't have to maintain their own\n * `lastDispatchError` state field.\n */\n onDispatchOutcome?: (outcome: LastDispatchOutcome | null) => void\n}\n\n/**\n * Wires up a WebSocket to serve rpc requests from the server. See spec §9.4.\n */\nexport function attachWsClient(\n ws: WsLike,\n rpc: RpcHosts,\n hello: HelloBuilder,\n opts: WsClientOpts = {},\n): WsClient {\n let activated = false\n ws.addEventListener('open', () => {\n ws.send(JSON.stringify(hello()))\n })\n ws.addEventListener('message', async (ev) => {\n let frame: ServerFrame\n try {\n const raw = typeof ev.data === 'string' ? ev.data : new TextDecoder().decode(ev.data)\n frame = JSON.parse(raw) as ServerFrame\n } catch {\n return\n }\n if (frame.t === 'revoked') {\n ws.close()\n return\n }\n if (frame.t === 'active') {\n if (!activated) {\n activated = true\n opts.onActivated?.()\n }\n return\n }\n if (frame.t !== 'rpc') return\n let result: unknown\n let rpcErr: { code?: string; detail?: string } | null = null\n try {\n result = await dispatch(frame.tool, frame.args, rpc)\n const reply: ClientFrame = { t: 'rpc-reply', id: frame.id, result }\n ws.send(JSON.stringify(reply))\n } catch (e: unknown) {\n rpcErr = e as { code?: string; detail?: string }\n // When a plain JS exception bubbles up (TypeError, RangeError, etc.),\n // rpcErr has no .code/.detail. Enrich the detail with the actual\n // message + stack so the server/Claude can see the real cause.\n const detail =\n rpcErr.detail ??\n (e instanceof Error\n ? `${e.name}: ${e.message}${e.stack ? '\\n' + e.stack.split('\\n').slice(0, 5).join('\\n') : ''}`\n : undefined)\n const errFrame: ClientFrame = {\n t: 'rpc-error',\n id: frame.id,\n code: rpcErr.code ?? 'internal',\n detail,\n }\n ws.send(JSON.stringify(errFrame))\n // Also log to the browser console so operators see the real cause even\n // when the server/Claude just show \"internal\".\n console.error(`[llui-agent] rpc handler threw for ${frame.tool}:`, e)\n }\n const kind = getLogKindForTool(frame.tool, result, rpcErr)\n const logEntry: LogEntry = {\n id: frame.id,\n at: Date.now(),\n kind,\n variant: extractVariant(frame.tool, frame.args),\n intent: buildIntent(frame.tool, frame.args, rpc.getMsgAnnotations()),\n }\n // For successful send_message dispatches, the stateDiff is part\n // of the response. Lifting it into the log entry means the agent\n // can read its own past actions with full \"what changed\" detail\n // without re-querying state — essential for self-correcting\n // behavior over multi-step flows.\n if (frame.tool === 'send_message' && rpcErr === null && isDispatchedResult(result)) {\n logEntry.stateDiff = result.stateDiff\n }\n if (frame.tool === 'send_message') {\n // Capture the outcome so `describe_context` can surface it as a\n // synthetic \"Last dispatch …\" hint. Apps used to roll their\n // own `lastDispatchError` field; the framework owns it now.\n const outcome = extractDispatchOutcome(frame.args, result, rpcErr)\n if (outcome !== null) opts.onDispatchOutcome?.(outcome)\n }\n opts.onLogEntry?.(logEntry)\n ws.send(JSON.stringify({ t: 'log-append', entry: logEntry } satisfies ClientFrame))\n })\n\n return {\n resolveConfirm(confirmId, outcome, stateAfter) {\n const frame: ClientFrame = {\n t: 'confirm-resolved',\n confirmId,\n outcome,\n stateAfter,\n }\n ws.send(JSON.stringify(frame))\n },\n emitStateUpdate(path, stateAfter) {\n const frame: ClientFrame = { t: 'state-update', path, stateAfter }\n ws.send(JSON.stringify(frame))\n },\n emitLogAppend(entry) {\n const frame: ClientFrame = { t: 'log-append', entry }\n ws.send(JSON.stringify(frame))\n },\n close() {\n ws.close()\n },\n }\n}\n\nasync function dispatch(tool: string, args: unknown, rpc: RpcHosts): Promise<unknown> {\n switch (tool) {\n case 'get_state':\n return handleGetState(rpc, (args ?? {}) as { path?: string })\n case 'query_state':\n return handleQueryState(rpc, (args ?? {}) as { path: string })\n case 'list_actions':\n return handleListActions(rpc)\n case 'send_message':\n return handleSendMessage(rpc, args as never)\n case 'query_dom':\n return handleQueryDom(rpc, args as never)\n case 'describe_visible_content':\n return handleDescribeVisibleContent(rpc)\n case 'describe_context':\n return handleDescribeContext(rpc)\n case 'observe':\n return handleObserve(rpc)\n case 'would_dispatch':\n return handleWouldDispatch(rpc, args as never)\n default:\n throw { code: 'invalid', detail: `unknown tool: ${tool}` }\n }\n}\n\nconst READ_TOOLS = new Set([\n 'get_state',\n 'query_state',\n 'list_actions',\n 'describe_context',\n 'query_dom',\n 'describe_visible_content',\n 'observe',\n 'would_dispatch',\n])\n\nfunction getLogKindForTool(\n tool: string,\n result: unknown,\n err: { code?: string; detail?: string } | null,\n): LogKind {\n if (err !== null) return 'error'\n if (tool === 'send_message') {\n const r = result as { status?: string } | null\n const status = r?.status\n if (status === 'dispatched' || status === 'confirmed') return 'dispatched'\n if (status === 'pending-confirmation') return 'proposed'\n if (status === 'rejected') return 'blocked'\n return 'dispatched'\n }\n if (READ_TOOLS.has(tool)) return 'read'\n return 'read'\n}\n\n/**\n * Type guard for the `dispatched` shape of `LapMessageResponse`.\n * Used to lift the stateDiff into the log entry without polluting\n * the type chain with cross-cutting imports.\n */\nfunction isDispatchedResult(\n result: unknown,\n): result is { status: 'dispatched'; stateDiff: import('../state-diff.js').StateDiff } {\n return (\n result !== null &&\n typeof result === 'object' &&\n (result as { status?: unknown }).status === 'dispatched' &&\n Array.isArray((result as { stateDiff?: unknown }).stateDiff)\n )\n}\n\n/**\n * Build a `LastDispatchOutcome` snapshot from the send_message rpc's\n * result/error pair. Returns null when the args don't carry a variant\n * name (malformed dispatch — would never have been logged anyway).\n *\n * Status mapping:\n * - { status: 'dispatched' } → 'dispatched' (with errors/warnings)\n * - { status: 'rejected' } → 'rejected'\n * - rpcErr (rpc handler threw, including reducer-throw on the predict path) → 'reducer-threw'\n *\n * The outcome is consumed by `describe_context` to prepend a synthetic\n * \"Last dispatch …\" hint. Clean dispatched outcomes (no errors, no\n * warnings) still get tracked — `formatLastOutcomeHint` decides whether\n * to surface them.\n */\nfunction extractDispatchOutcome(\n args: unknown,\n result: unknown,\n rpcErr: { code?: string; detail?: string } | null,\n): LastDispatchOutcome | null {\n const variant = extractVariant('send_message', args)\n if (variant === undefined) return null\n\n const at = Date.now()\n\n if (rpcErr !== null) {\n // The rpc handler itself threw — most often this is a reducer\n // throw that bubbled past the catch in send-message.ts (e.g.\n // would_dispatch's reducer threw in a non-send-message tool that\n // shares the path). Treat as `reducer-threw` so the agent reads\n // \"state may be partially advanced; observe before retrying.\"\n return {\n variant,\n status: 'reducer-threw',\n errors: [{ message: rpcErr.detail ?? rpcErr.code ?? 'rpc handler threw' }],\n at,\n }\n }\n if (result === null || typeof result !== 'object') return null\n const r = result as {\n status?: string\n drain?: {\n errors?: ReadonlyArray<{ message: string }>\n warnings?: ReadonlyArray<{ path: string; message: string }>\n }\n detail?: string\n }\n if (r.status === 'dispatched') {\n const errors = r.drain?.errors ?? []\n const warnings = r.drain?.warnings ?? []\n const out: LastDispatchOutcome = { variant, status: 'dispatched', at }\n if (errors.length > 0) out.errors = errors\n if (warnings.length > 0) out.warnings = warnings\n return out\n }\n if (r.status === 'rejected') {\n const detail = r.detail ?? 'rejected'\n return {\n variant,\n status: 'rejected',\n errors: [{ message: detail }],\n at,\n }\n }\n // Other statuses (`pending-confirmation`, etc.) — don't update the\n // last-outcome cache. The dispatch hasn't really concluded yet.\n return null\n}\n\nfunction extractVariant(tool: string, args: unknown): string | undefined {\n if (tool === 'send_message') {\n const a = args as { msg?: { type?: string } } | null\n const t = a?.msg?.type\n return typeof t === 'string' ? t : undefined\n }\n return undefined\n}\n\n// Human-readable label for each rpc. For send_message, prefer the @intent\n// annotation authored on the Msg union; fall back to the raw variant name.\n// For read tools, return a short fixed label so the activity feed doesn't\n// show opaque tool ids like \"describe_visible_content\".\nfunction buildIntent(\n tool: string,\n args: unknown,\n annotations: Record<string, MessageAnnotations> | null,\n): string {\n if (tool === 'send_message') {\n const a = args as { msg?: { type?: string } } | null\n const variant = typeof a?.msg?.type === 'string' ? a.msg.type : undefined\n const annotated = variant ? annotations?.[variant]?.intent : null\n if (annotated) return annotated\n return variant ?? 'Send message'\n }\n if (tool === 'get_state') return 'Read app state'\n if (tool === 'query_state') return 'Read state slice'\n if (tool === 'list_actions') return 'List available actions'\n if (tool === 'describe_context') return 'Read current context'\n if (tool === 'describe_visible_content') return 'Read visible content'\n if (tool === 'query_dom') {\n const a = args as { name?: string } | null\n return a?.name ? `Query DOM: ${a.name}` : 'Query DOM'\n }\n return tool\n}\n"]}
|
package/dist/protocol.d.ts
CHANGED
|
@@ -188,6 +188,14 @@ export type LapMessageRejectReason = 'human-only' | 'user-cancelled' | 'timeout'
|
|
|
188
188
|
* `errors` surfaces sync throws from `onEffect` and unhandled rejections
|
|
189
189
|
* from effect handlers that fired during the drain window, so the LLM
|
|
190
190
|
* can see when an HTTP handler crashed silently.
|
|
191
|
+
*
|
|
192
|
+
* `warnings` surfaces non-blocking observations from the schema
|
|
193
|
+
* validator — typically `untyped-field` flags raised in strict mode
|
|
194
|
+
* when the agent provided a value for an `'unknown'`-typed field. The
|
|
195
|
+
* dispatch landed (we accepted the value) but the validator couldn't
|
|
196
|
+
* structurally check it, so the agent learns of the gap and can
|
|
197
|
+
* tighten the next try if needed. Lenient mode never emits warnings;
|
|
198
|
+
* the field is omitted in that case.
|
|
191
199
|
*/
|
|
192
200
|
export type LapDrainMeta = {
|
|
193
201
|
effectsObserved: number;
|
|
@@ -198,6 +206,11 @@ export type LapDrainMeta = {
|
|
|
198
206
|
message: string;
|
|
199
207
|
stack?: string;
|
|
200
208
|
}>;
|
|
209
|
+
warnings?: Array<{
|
|
210
|
+
path: string;
|
|
211
|
+
code: string;
|
|
212
|
+
message: string;
|
|
213
|
+
}>;
|
|
201
214
|
};
|
|
202
215
|
export type LapMessageResponse = {
|
|
203
216
|
status: 'dispatched';
|
package/dist/protocol.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"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"}
|
package/dist/protocol.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,mEAAmE;AACnE,iEAAiE;AACjE,iEAAiE;AA6gBjE,qEAAqE;AACrE,EAAE;AACF,qEAAqE;AACrE,4DAA4D;AAC5D,mEAAmE;AACnE,qCAAqC;AAErC,OAAO,EACL,QAAQ,EACR,UAAU,EACV,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,cAAc,GAEf,MAAM,aAAa,CAAA","sourcesContent":["// ── LAP — LLui Agent Protocol ────────────────────────────────────\n// JSON over HTTPS between the llui-agent bridge (MCP side) and the\n// @llui/agent server library mounted in the developer's backend.\n// See docs/superpowers/specs/2026-04-19-llui-agent-design.md §7.\n\nexport type LapErrorCode =\n | 'auth-failed'\n | 'revoked'\n | 'paused'\n | 'rate-limited'\n | 'invalid'\n | 'schema-error'\n | 'timeout'\n | 'internal'\n\nexport type LapError = {\n error: {\n code: LapErrorCode\n detail?: string\n retryAfterMs?: number\n }\n}\n\n/**\n * Who can dispatch a Msg variant.\n *\n * - `'shared'` (default) — both UI bindings and the agent can dispatch.\n * - `'human-only'` — UI-only. Agent calls to `/message` for these variants\n * are rejected with `LapMessageRejectReason: 'human-only'`. Use for\n * internal UI events (focus/blur, scroll, hover) the LLM has no business\n * triggering.\n * - `'agent-only'` — no UI binding exists. Reserved for LLM-driven flows\n * like batch operations or \"explain this state\" introspection variants.\n * Lint warns if a view references one via `send({ type: 'X' })`.\n *\n * JSDoc sugar: `@humanOnly` → `'human-only'`, `@agentOnly` → `'agent-only'`.\n * Absence of either tag → `'shared'`. The two tags are mutually exclusive\n * (enforced by `llui/agent-exclusive-annotations` ESLint rule).\n */\nexport type DispatchMode = 'shared' | 'human-only' | 'agent-only'\n\nexport type MessageAnnotations = {\n intent: string | null\n alwaysAffordable: boolean\n requiresConfirm: boolean\n dispatchMode: DispatchMode\n /**\n * Concrete copy-paste example dispatches authored as `@example`\n * JSDoc tags. Multiple tags on one variant become multiple\n * entries (mix typical / edge cases without nesting strings).\n */\n examples: string[]\n /**\n * Non-blocking caution authored as `@warning`. Distinct from\n * `requiresConfirm` (runtime user gate); this informs the LLM at\n * affordance time so it can decide whether the dispatch's\n * downstream is acceptable.\n */\n warning: string | null\n /**\n * Effect kinds this variant emits when dispatched, declared via\n * `@emits(\"kind1\", \"kind2\")`. Lets the agent reason about side\n * effects (cloud writes, analytics, persistent state changes)\n * before dispatching, and chunk multi-step flows accordingly\n * (\"don't dispatch X 100 times — each one fires cloud/save\").\n * Empty when the variant doesn't emit effects or the author hasn't\n * annotated it yet.\n */\n emits: string[]\n}\n\nexport type MessageSchemaEntry = {\n payloadSchema: object\n annotations: MessageAnnotations\n}\n\nexport type LapDescribeResponse = {\n name: string\n version: string\n stateSchema: object\n messages: Record<string, MessageSchemaEntry>\n docs: AgentDocs | null\n conventions: {\n dispatchModel: 'TEA'\n confirmationModel: 'runtime-mediated'\n readSurfaces: readonly (\n | 'state'\n | 'query_dom'\n | 'describe_visible_content'\n | 'describe_context'\n )[]\n }\n schemaHash: string\n}\n\nexport type LapStateRequest = { path?: string }\nexport type LapStateResponse = { state: unknown }\n\nexport type LapActionsResponse = {\n actions: Array<{\n variant: string\n /**\n * Human-readable phrase from `@intent(\"…\")`, or `null` when the\n * variant has no `@intent` annotation. Callers that surface\n * affordances to an LLM should treat `null` as \"this action is\n * undocumented\" — neither synthesise a label from the variant name\n * nor invent one. Pre-`@intent` variants would previously surface\n * as `intent: \"<variant>\"` here, which made unannotated actions\n * indistinguishable from properly-labelled ones; emitting `null`\n * keeps the gap visible.\n */\n intent: string | null\n requiresConfirm: boolean\n /**\n * `'shared'` — both UI and agent can dispatch. `'agent-only'` — no UI\n * binding exists; the agent is the sole dispatcher. `'human-only'`\n * variants never appear here (filtered before serialization).\n */\n dispatchMode: 'shared' | 'agent-only'\n /**\n * Where this affordance came from:\n * - `'binding'` — a tagged event handler is currently\n * mounted in the rendered DOM.\n * - `'always-affordable'` — the app's `agentAffordances(state)`\n * hook listed it as available right now.\n * - `'schema'` — neither of the above; the variant\n * is in the Msg union and annotated `@agentOnly`. The\n * `payloadHint` carries a synthesized example from the\n * compiler-derived field types — copy-paste-ready for\n * `send_message`. Bulk-edit operations land here.\n */\n source: 'binding' | 'always-affordable' | 'schema'\n selectorHint: string | null\n payloadHint: object | null\n /** Cautionary text from `@warning` JSDoc, or null. */\n warning: string | null\n /** Concrete examples from `@example` JSDoc, in source order. */\n examples: string[]\n /**\n * Effect kinds this variant emits, from `@emits(\"k1\", \"k2\")`.\n * Empty when not annotated.\n */\n emits: string[]\n /**\n * Per-field guidance lifted from `@should(\"…\")` JSDoc on payload\n * fields. Path is dot/bracket notation rooted at the payload (e.g.\n * `\"cells[].meta\"`). Surfaces hints that would otherwise be buried\n * inside the schema tree, so callers can read them alongside\n * `examples` without diving into `description.messages.variants`.\n */\n fieldHints: Array<{ path: string; hint: string }>\n }>\n}\n\nexport type LapMessageRequest = {\n msg: { type: string; [k: string]: unknown }\n reason?: string\n /**\n * Backpressure contract for how long `/message` waits before returning:\n * - `drained` (default): dispatch, then loop until the message queue is\n * idle for `drainQuietMs` ms or the 5s hard cap trips. Captures any\n * effect round-trips (http/delay/debounce) that feed back as messages.\n * - `idle`: dispatch + flush + one microtask yield. Captures the\n * synchronous update cycle but not async effects.\n * - `none`: dispatch and return without flushing. For high-throughput\n * fire-and-forget dispatch.\n */\n waitFor?: 'drained' | 'idle' | 'none'\n /**\n * Quiescence window when `waitFor === 'drained'`. Drain completes when\n * no new update cycle fires for this many ms. Default 100ms — long\n * enough for a localhost HTTP round-trip, short enough to be\n * imperceptible. Ignored for `idle` / `none`.\n */\n drainQuietMs?: number\n /**\n * Hard cap on total wait time. When `waitFor === 'drained'`, this is\n * the upper bound on how long the drain loop can run; if reached, the\n * response carries `drain.timedOut: true` with partial results. For\n * `pending-confirmation` messages, this is how long to wait for\n * the user's confirm/reject. Default 5_000ms.\n */\n timeoutMs?: number\n /**\n * Include the full post-drain `stateAfter` snapshot in the response.\n * Default `false` — the response carries `stateDiff` only and the\n * caller applies it to the prior snapshot (from connect/observe). For\n * apps with non-trivial state, the diff is orders of magnitude\n * smaller than the full state, and resending the snapshot on every\n * dispatch wastes bandwidth and (for LLM callers) context budget.\n *\n * Set `true` when the caller doesn't track state incrementally and\n * wants the snapshot back. The legacy `confirmed` and `wait` paths\n * always carry `stateAfter` because their flow is asynchronous and\n * a diff would be ambiguous.\n */\n includeState?: boolean\n}\n\nexport type LapMessageRejectReason =\n | 'human-only'\n | 'user-cancelled'\n | 'timeout'\n | 'invalid'\n | 'schema-error'\n | 'revoked'\n | 'paused'\n\n/**\n * Drain metadata attached to `dispatched` / `confirmed` responses.\n * `effectsObserved` counts update-cycle commits (not individual effects) —\n * it's a proxy for \"how much activity happened during the drain window.\"\n * `errors` surfaces sync throws from `onEffect` and unhandled rejections\n * from effect handlers that fired during the drain window, so the LLM\n * can see when an HTTP handler crashed silently.\n */\nexport type LapDrainMeta = {\n effectsObserved: number\n durationMs: number\n timedOut: boolean\n errors: Array<{ kind: 'error' | 'unhandledrejection'; message: string; stack?: string }>\n}\n\nexport type LapMessageResponse =\n | {\n status: 'dispatched'\n /**\n * Full post-drain state snapshot. Present only when the caller\n * passed `includeState: true` in the request — by default,\n * `stateDiff` is the only state-shaped field on the response\n * because callers can apply the diff to the prior snapshot from\n * `connect` / `observe`. See `LapMessageRequest.includeState`.\n */\n stateAfter?: unknown\n /**\n * Structural diff from pre-dispatch state to post-drain state,\n * in JSON-Patch shape (RFC 6902 subset: `add`, `remove`,\n * `replace`). Empty when the dispatch produced no observable\n * state change. The default state surface for callers — apply\n * incrementally to the snapshot from `connect`/`observe`.\n */\n stateDiff: import('./state-diff.js').StateDiff\n actions: LapActionsResponse['actions']\n drain: LapDrainMeta\n }\n | { status: 'pending-confirmation'; confirmId: string }\n | {\n /**\n * The user approved a `pending-confirmation` message. `stateAfter`\n * is the state snapshot captured when the approve was resolved;\n * effects produced by the approved dispatch may still be in\n * flight. The LLM should follow up with an `observe` call to\n * pick up a drained view and fresh actions — by design the\n * confirm path doesn't carry drain semantics because approval\n * can arrive arbitrarily later than the original request.\n */\n status: 'confirmed'\n stateAfter: unknown\n }\n | { status: 'rejected'; reason: LapMessageRejectReason; detail?: string }\n\nexport type LapConfirmResultRequest = { confirmId: string; timeoutMs?: number }\nexport type LapConfirmResultResponse =\n | { status: 'confirmed'; stateAfter: unknown }\n | { status: 'rejected'; reason: 'user-cancelled' | 'timeout' }\n | { status: 'still-pending' }\n\nexport type LapWaitRequest = { path?: string; timeoutMs?: number }\nexport type LapWaitResponse =\n | { status: 'changed'; stateAfter: unknown }\n | { status: 'timeout'; stateAfter: unknown }\n\nexport type LapQueryDomRequest = { name: string; multiple?: boolean }\nexport type LapQueryDomResponse = {\n elements: Array<{ text: string; attrs: Record<string, string>; path: number[] }>\n}\n\nexport type OutlineNode =\n | { kind: 'heading'; level: number; text: string }\n | { kind: 'text'; text: string }\n | { kind: 'list'; items: OutlineNode[] }\n | { kind: 'item'; text: string; children?: OutlineNode[] }\n | { kind: 'button'; text: string; disabled: boolean; actionVariant: string | null }\n | { kind: 'input'; label: string | null; value: string | null; type: string }\n | { kind: 'link'; text: string; href: string }\n\nexport type LapDescribeVisibleResponse = {\n outline: OutlineNode[]\n /**\n * Where the outline came from:\n * - `'data-agent'`: the app has `data-agent`-tagged zones and the\n * walker scoped the outline to them. The author chose what to\n * surface; trust the result.\n * - `'fallback'`: no `data-agent` tags exist; the walker fell back\n * to a depth- and count-limited semantic walk of the entire\n * root element. Useful for first-pass dogfood targets that\n * haven't tagged their views.\n * - `'truncated'`: same as `'fallback'` but the cap (200 nodes)\n * was hit before the walk finished. The visible content beyond\n * that point is not represented; reach for `query_dom` or state\n * reads if you need more.\n */\n source: 'data-agent' | 'fallback' | 'truncated'\n}\n\n// ── App + context documentation ──────────────────────────────────\n// Static app-level docs (authored once on the component record) and\n// dynamic per-state context docs (pure function of state, served by\n// `/lap/v1/context`). See spec §5.4.\n\nexport type AgentDocs = {\n purpose: string\n overview?: string\n cautions?: string[]\n /**\n * Free-form idiomatic-usage examples authored by the app: typical\n * sequences of dispatches the LLM should know about, like \"to\n * delete a saved matrix: dispatch Confirm/Ask first, then on\n * approve dispatch Cloud/Delete.\" Each entry is one example;\n * order is up to the author.\n */\n examples?: string[]\n}\n\nexport type AgentContext = {\n summary: string\n hints?: string[]\n cautions?: string[]\n}\n\nexport type LapContextResponse = { context: AgentContext }\n\n// ── Unified observe ──────────────────────────────────────────────\n// Single-call bootstrap. Replaces the get_state + list_actions +\n// describe_app trio for the common \"what can I see, what can I do\"\n// question. Returns the dynamic state + actions slice alongside the\n// static description (name/version/messages/docs) and any\n// state-derived context so one round-trip gives the LLM everything it\n// needs to decide its next action.\n\nexport type LapObserveResponse = {\n state: unknown\n actions: LapActionsResponse['actions']\n description: LapDescribeResponse\n context: AgentContext | null\n}\n\n// LAP endpoint catalog — a compile-time map binding each path to its\n// request/response shape. Useful for the bridge's dispatcher and for\n// typed test helpers.\nexport type LapEndpointMap = {\n '/lap/v1/describe': { req: null; res: LapDescribeResponse }\n '/lap/v1/state': { req: LapStateRequest; res: LapStateResponse }\n '/lap/v1/actions': { req: null; res: LapActionsResponse }\n '/lap/v1/message': { req: LapMessageRequest; res: LapMessageResponse }\n '/lap/v1/confirm-result': { req: LapConfirmResultRequest; res: LapConfirmResultResponse }\n '/lap/v1/wait': { req: LapWaitRequest; res: LapWaitResponse }\n '/lap/v1/query-dom': { req: LapQueryDomRequest; res: LapQueryDomResponse }\n '/lap/v1/describe-visible': { req: null; res: LapDescribeVisibleResponse }\n '/lap/v1/context': { req: null; res: LapContextResponse }\n '/lap/v1/observe': { req: null; res: LapObserveResponse }\n}\n\nexport type LapPath = keyof LapEndpointMap\nexport type LapRequest<P extends LapPath> = LapEndpointMap[P]['req']\nexport type LapResponse<P extends LapPath> = LapEndpointMap[P]['res']\n\n// ── Relay WS frames ──────────────────────────────────────────────\n// Bidirectional framing between the LLui runtime in the browser and\n// the @llui/agent server over /agent/ws. See spec §10.5.\n\nexport type LogKind =\n | 'proposed'\n | 'dispatched'\n | 'confirmed'\n | 'rejected'\n | 'blocked'\n | 'read'\n | 'error'\n\nexport type LogEntry = {\n id: string\n at: number\n kind: LogKind\n variant?: string\n intent?: string\n detail?: string\n /**\n * Structural diff from pre-dispatch state to post-drain state, in\n * JSON-Patch shape. Populated only for `kind: 'dispatched'` entries\n * — read entries (get_state / list_actions / observe / …) don't\n * mutate state, and an empty diff would just be noise. Lets the\n * agent reconstruct what each past action did without re-fetching\n * state snapshots.\n */\n stateDiff?: import('./state-diff.js').StateDiff\n}\n\nexport type HelloFrame = {\n t: 'hello'\n appName: string\n appVersion: string\n msgSchema: Record<string, MessageSchemaEntry>\n stateSchema: object\n affordancesSample: object[]\n docs: AgentDocs | null\n schemaHash: string\n}\n\nexport type RpcReplyFrame = { t: 'rpc-reply'; id: string; result: unknown }\nexport type RpcErrorFrame = { t: 'rpc-error'; id: string; code: string; detail?: string }\nexport type ConfirmResolvedFrame = {\n t: 'confirm-resolved'\n confirmId: string\n outcome: 'confirmed' | 'user-cancelled'\n stateAfter?: unknown\n}\nexport type StateUpdateFrame = { t: 'state-update'; path: string; stateAfter: unknown }\nexport type LogAppendFrame = { t: 'log-append'; entry: LogEntry }\n\nexport type ClientFrame =\n | HelloFrame\n | RpcReplyFrame\n | RpcErrorFrame\n | ConfirmResolvedFrame\n | StateUpdateFrame\n | LogAppendFrame\n\nexport type RpcFrame = { t: 'rpc'; id: string; tool: string; args: unknown }\nexport type RevokedFrame = { t: 'revoked' }\nexport type ActiveFrame = { t: 'active' }\n\nexport type ServerFrame = RpcFrame | RevokedFrame | ActiveFrame\n\n// ── Tokens + pairing ─────────────────────────────────────────────\n\ndeclare const TokenBrand: unique symbol\nexport type AgentToken = string & { readonly [TokenBrand]: 'AgentToken' }\n\nexport type TokenStatus =\n | 'awaiting-ws'\n | 'awaiting-claude'\n | 'active'\n | 'pending-resume'\n | 'revoked'\n\nexport type TokenRecord = {\n tid: string\n /**\n * SHA-256 hex of the bearer token. The plaintext token is never\n * stored — incoming requests hash their `Authorization: Bearer …`\n * value and look up by this field. Hash-only storage keeps a leaked\n * store from being a live-token leak. Mirrors the standard session-\n * cookie / API-key pattern.\n */\n tokenHash: string\n uid: string | null\n status: TokenStatus\n createdAt: number\n /**\n * Hard-expiry in milliseconds since epoch. The mint endpoint sets\n * this to `now + hardExpiryMs`; the verify path rejects requests\n * presenting tokens whose record has `expiresAt <= now`. Pre-0.0.35\n * the equivalent value lived inside the JWT payload as `exp` (in\n * seconds); the new opaque-token flow keeps it server-side so the\n * record is the single source of truth.\n */\n expiresAt: number\n lastSeenAt: number\n pendingResumeUntil: number | null\n origin: string\n label: string | null\n}\n\nexport type AgentSession = {\n tid: string\n label: string\n status: 'active' | 'pending-resume' | 'revoked'\n createdAt: number\n lastSeenAt: number\n}\n\n// HTTP envelopes for the mint/resume/revoke/sessions endpoints (non-LAP).\n\nexport type MintRequest = Record<string, never>\nexport type MintResponse = {\n token: AgentToken\n tid: string\n wsUrl: string\n lapUrl: string\n expiresAt: number\n}\n\nexport type ResumeListRequest = { tids: string[] }\nexport type ResumeListResponse = { sessions: AgentSession[] }\n\nexport type ResumeClaimRequest = { tid: string }\nexport type ResumeClaimResponse = { token: AgentToken; wsUrl: string }\n\nexport type RevokeRequest = { tid: string }\nexport type RevokeResponse = { status: 'revoked' }\n\nexport type SessionsResponse = { sessions: AgentSession[] }\n\n// ── Audit ────────────────────────────────────────────────────────\n\nexport type AuditEvent =\n | 'mint'\n | 'claim'\n | 'resume'\n | 'revoke'\n | 'lap-call'\n | 'msg-dispatched'\n | 'msg-blocked'\n | 'confirm-proposed'\n | 'confirm-approved'\n | 'confirm-rejected'\n | 'rate-limited'\n | 'auth-failed'\n\nexport type AuditEntry = {\n at: number\n tid: string | null\n uid: string | null\n event: AuditEvent\n detail: object\n}\n\n// ── Codec exports ─────────────────────────────────────────────────\n//\n// Re-exported here so consumers can `import { ..., type AgentCodec }\n// from '@llui/agent/protocol'`. The implementation lives in\n// `./codecs.ts` to keep the protocol type surface together but the\n// runtime registry/walkers separate.\n\nexport {\n WIRE_TAG,\n WIRE_VALUE,\n CodecRegistry,\n isoDateCodec,\n epochMillisCodec,\n makeDefaultCodecs,\n encodeForWire,\n decodeFromWire,\n type AgentCodec,\n} from './codecs.js'\n"]}
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,mEAAmE;AACnE,iEAAiE;AACjE,iEAAiE;AAshBjE,qEAAqE;AACrE,EAAE;AACF,qEAAqE;AACrE,4DAA4D;AAC5D,mEAAmE;AACnE,qCAAqC;AAErC,OAAO,EACL,QAAQ,EACR,UAAU,EACV,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,cAAc,GAEf,MAAM,aAAa,CAAA","sourcesContent":["// ── LAP — LLui Agent Protocol ────────────────────────────────────\n// JSON over HTTPS between the llui-agent bridge (MCP side) and the\n// @llui/agent server library mounted in the developer's backend.\n// See docs/superpowers/specs/2026-04-19-llui-agent-design.md §7.\n\nexport type LapErrorCode =\n | 'auth-failed'\n | 'revoked'\n | 'paused'\n | 'rate-limited'\n | 'invalid'\n | 'schema-error'\n | 'timeout'\n | 'internal'\n\nexport type LapError = {\n error: {\n code: LapErrorCode\n detail?: string\n retryAfterMs?: number\n }\n}\n\n/**\n * Who can dispatch a Msg variant.\n *\n * - `'shared'` (default) — both UI bindings and the agent can dispatch.\n * - `'human-only'` — UI-only. Agent calls to `/message` for these variants\n * are rejected with `LapMessageRejectReason: 'human-only'`. Use for\n * internal UI events (focus/blur, scroll, hover) the LLM has no business\n * triggering.\n * - `'agent-only'` — no UI binding exists. Reserved for LLM-driven flows\n * like batch operations or \"explain this state\" introspection variants.\n * Lint warns if a view references one via `send({ type: 'X' })`.\n *\n * JSDoc sugar: `@humanOnly` → `'human-only'`, `@agentOnly` → `'agent-only'`.\n * Absence of either tag → `'shared'`. The two tags are mutually exclusive\n * (enforced by `llui/agent-exclusive-annotations` ESLint rule).\n */\nexport type DispatchMode = 'shared' | 'human-only' | 'agent-only'\n\nexport type MessageAnnotations = {\n intent: string | null\n alwaysAffordable: boolean\n requiresConfirm: boolean\n dispatchMode: DispatchMode\n /**\n * Concrete copy-paste example dispatches authored as `@example`\n * JSDoc tags. Multiple tags on one variant become multiple\n * entries (mix typical / edge cases without nesting strings).\n */\n examples: string[]\n /**\n * Non-blocking caution authored as `@warning`. Distinct from\n * `requiresConfirm` (runtime user gate); this informs the LLM at\n * affordance time so it can decide whether the dispatch's\n * downstream is acceptable.\n */\n warning: string | null\n /**\n * Effect kinds this variant emits when dispatched, declared via\n * `@emits(\"kind1\", \"kind2\")`. Lets the agent reason about side\n * effects (cloud writes, analytics, persistent state changes)\n * before dispatching, and chunk multi-step flows accordingly\n * (\"don't dispatch X 100 times — each one fires cloud/save\").\n * Empty when the variant doesn't emit effects or the author hasn't\n * annotated it yet.\n */\n emits: string[]\n}\n\nexport type MessageSchemaEntry = {\n payloadSchema: object\n annotations: MessageAnnotations\n}\n\nexport type LapDescribeResponse = {\n name: string\n version: string\n stateSchema: object\n messages: Record<string, MessageSchemaEntry>\n docs: AgentDocs | null\n conventions: {\n dispatchModel: 'TEA'\n confirmationModel: 'runtime-mediated'\n readSurfaces: readonly (\n | 'state'\n | 'query_dom'\n | 'describe_visible_content'\n | 'describe_context'\n )[]\n }\n schemaHash: string\n}\n\nexport type LapStateRequest = { path?: string }\nexport type LapStateResponse = { state: unknown }\n\nexport type LapActionsResponse = {\n actions: Array<{\n variant: string\n /**\n * Human-readable phrase from `@intent(\"…\")`, or `null` when the\n * variant has no `@intent` annotation. Callers that surface\n * affordances to an LLM should treat `null` as \"this action is\n * undocumented\" — neither synthesise a label from the variant name\n * nor invent one. Pre-`@intent` variants would previously surface\n * as `intent: \"<variant>\"` here, which made unannotated actions\n * indistinguishable from properly-labelled ones; emitting `null`\n * keeps the gap visible.\n */\n intent: string | null\n requiresConfirm: boolean\n /**\n * `'shared'` — both UI and agent can dispatch. `'agent-only'` — no UI\n * binding exists; the agent is the sole dispatcher. `'human-only'`\n * variants never appear here (filtered before serialization).\n */\n dispatchMode: 'shared' | 'agent-only'\n /**\n * Where this affordance came from:\n * - `'binding'` — a tagged event handler is currently\n * mounted in the rendered DOM.\n * - `'always-affordable'` — the app's `agentAffordances(state)`\n * hook listed it as available right now.\n * - `'schema'` — neither of the above; the variant\n * is in the Msg union and annotated `@agentOnly`. The\n * `payloadHint` carries a synthesized example from the\n * compiler-derived field types — copy-paste-ready for\n * `send_message`. Bulk-edit operations land here.\n */\n source: 'binding' | 'always-affordable' | 'schema'\n selectorHint: string | null\n payloadHint: object | null\n /** Cautionary text from `@warning` JSDoc, or null. */\n warning: string | null\n /** Concrete examples from `@example` JSDoc, in source order. */\n examples: string[]\n /**\n * Effect kinds this variant emits, from `@emits(\"k1\", \"k2\")`.\n * Empty when not annotated.\n */\n emits: string[]\n /**\n * Per-field guidance lifted from `@should(\"…\")` JSDoc on payload\n * fields. Path is dot/bracket notation rooted at the payload (e.g.\n * `\"cells[].meta\"`). Surfaces hints that would otherwise be buried\n * inside the schema tree, so callers can read them alongside\n * `examples` without diving into `description.messages.variants`.\n */\n fieldHints: Array<{ path: string; hint: string }>\n }>\n}\n\nexport type LapMessageRequest = {\n msg: { type: string; [k: string]: unknown }\n reason?: string\n /**\n * Backpressure contract for how long `/message` waits before returning:\n * - `drained` (default): dispatch, then loop until the message queue is\n * idle for `drainQuietMs` ms or the 5s hard cap trips. Captures any\n * effect round-trips (http/delay/debounce) that feed back as messages.\n * - `idle`: dispatch + flush + one microtask yield. Captures the\n * synchronous update cycle but not async effects.\n * - `none`: dispatch and return without flushing. For high-throughput\n * fire-and-forget dispatch.\n */\n waitFor?: 'drained' | 'idle' | 'none'\n /**\n * Quiescence window when `waitFor === 'drained'`. Drain completes when\n * no new update cycle fires for this many ms. Default 100ms — long\n * enough for a localhost HTTP round-trip, short enough to be\n * imperceptible. Ignored for `idle` / `none`.\n */\n drainQuietMs?: number\n /**\n * Hard cap on total wait time. When `waitFor === 'drained'`, this is\n * the upper bound on how long the drain loop can run; if reached, the\n * response carries `drain.timedOut: true` with partial results. For\n * `pending-confirmation` messages, this is how long to wait for\n * the user's confirm/reject. Default 5_000ms.\n */\n timeoutMs?: number\n /**\n * Include the full post-drain `stateAfter` snapshot in the response.\n * Default `false` — the response carries `stateDiff` only and the\n * caller applies it to the prior snapshot (from connect/observe). For\n * apps with non-trivial state, the diff is orders of magnitude\n * smaller than the full state, and resending the snapshot on every\n * dispatch wastes bandwidth and (for LLM callers) context budget.\n *\n * Set `true` when the caller doesn't track state incrementally and\n * wants the snapshot back. The legacy `confirmed` and `wait` paths\n * always carry `stateAfter` because their flow is asynchronous and\n * a diff would be ambiguous.\n */\n includeState?: boolean\n}\n\nexport type LapMessageRejectReason =\n | 'human-only'\n | 'user-cancelled'\n | 'timeout'\n | 'invalid'\n | 'schema-error'\n | 'revoked'\n | 'paused'\n\n/**\n * Drain metadata attached to `dispatched` / `confirmed` responses.\n * `effectsObserved` counts update-cycle commits (not individual effects) —\n * it's a proxy for \"how much activity happened during the drain window.\"\n * `errors` surfaces sync throws from `onEffect` and unhandled rejections\n * from effect handlers that fired during the drain window, so the LLM\n * can see when an HTTP handler crashed silently.\n *\n * `warnings` surfaces non-blocking observations from the schema\n * validator — typically `untyped-field` flags raised in strict mode\n * when the agent provided a value for an `'unknown'`-typed field. The\n * dispatch landed (we accepted the value) but the validator couldn't\n * structurally check it, so the agent learns of the gap and can\n * tighten the next try if needed. Lenient mode never emits warnings;\n * the field is omitted in that case.\n */\nexport type LapDrainMeta = {\n effectsObserved: number\n durationMs: number\n timedOut: boolean\n errors: Array<{ kind: 'error' | 'unhandledrejection'; message: string; stack?: string }>\n warnings?: Array<{ path: string; code: string; message: string }>\n}\n\nexport type LapMessageResponse =\n | {\n status: 'dispatched'\n /**\n * Full post-drain state snapshot. Present only when the caller\n * passed `includeState: true` in the request — by default,\n * `stateDiff` is the only state-shaped field on the response\n * because callers can apply the diff to the prior snapshot from\n * `connect` / `observe`. See `LapMessageRequest.includeState`.\n */\n stateAfter?: unknown\n /**\n * Structural diff from pre-dispatch state to post-drain state,\n * in JSON-Patch shape (RFC 6902 subset: `add`, `remove`,\n * `replace`). Empty when the dispatch produced no observable\n * state change. The default state surface for callers — apply\n * incrementally to the snapshot from `connect`/`observe`.\n */\n stateDiff: import('./state-diff.js').StateDiff\n actions: LapActionsResponse['actions']\n drain: LapDrainMeta\n }\n | { status: 'pending-confirmation'; confirmId: string }\n | {\n /**\n * The user approved a `pending-confirmation` message. `stateAfter`\n * is the state snapshot captured when the approve was resolved;\n * effects produced by the approved dispatch may still be in\n * flight. The LLM should follow up with an `observe` call to\n * pick up a drained view and fresh actions — by design the\n * confirm path doesn't carry drain semantics because approval\n * can arrive arbitrarily later than the original request.\n */\n status: 'confirmed'\n stateAfter: unknown\n }\n | { status: 'rejected'; reason: LapMessageRejectReason; detail?: string }\n\nexport type LapConfirmResultRequest = { confirmId: string; timeoutMs?: number }\nexport type LapConfirmResultResponse =\n | { status: 'confirmed'; stateAfter: unknown }\n | { status: 'rejected'; reason: 'user-cancelled' | 'timeout' }\n | { status: 'still-pending' }\n\nexport type LapWaitRequest = { path?: string; timeoutMs?: number }\nexport type LapWaitResponse =\n | { status: 'changed'; stateAfter: unknown }\n | { status: 'timeout'; stateAfter: unknown }\n\nexport type LapQueryDomRequest = { name: string; multiple?: boolean }\nexport type LapQueryDomResponse = {\n elements: Array<{ text: string; attrs: Record<string, string>; path: number[] }>\n}\n\nexport type OutlineNode =\n | { kind: 'heading'; level: number; text: string }\n | { kind: 'text'; text: string }\n | { kind: 'list'; items: OutlineNode[] }\n | { kind: 'item'; text: string; children?: OutlineNode[] }\n | { kind: 'button'; text: string; disabled: boolean; actionVariant: string | null }\n | { kind: 'input'; label: string | null; value: string | null; type: string }\n | { kind: 'link'; text: string; href: string }\n\nexport type LapDescribeVisibleResponse = {\n outline: OutlineNode[]\n /**\n * Where the outline came from:\n * - `'data-agent'`: the app has `data-agent`-tagged zones and the\n * walker scoped the outline to them. The author chose what to\n * surface; trust the result.\n * - `'fallback'`: no `data-agent` tags exist; the walker fell back\n * to a depth- and count-limited semantic walk of the entire\n * root element. Useful for first-pass dogfood targets that\n * haven't tagged their views.\n * - `'truncated'`: same as `'fallback'` but the cap (200 nodes)\n * was hit before the walk finished. The visible content beyond\n * that point is not represented; reach for `query_dom` or state\n * reads if you need more.\n */\n source: 'data-agent' | 'fallback' | 'truncated'\n}\n\n// ── App + context documentation ──────────────────────────────────\n// Static app-level docs (authored once on the component record) and\n// dynamic per-state context docs (pure function of state, served by\n// `/lap/v1/context`). See spec §5.4.\n\nexport type AgentDocs = {\n purpose: string\n overview?: string\n cautions?: string[]\n /**\n * Free-form idiomatic-usage examples authored by the app: typical\n * sequences of dispatches the LLM should know about, like \"to\n * delete a saved matrix: dispatch Confirm/Ask first, then on\n * approve dispatch Cloud/Delete.\" Each entry is one example;\n * order is up to the author.\n */\n examples?: string[]\n}\n\nexport type AgentContext = {\n summary: string\n hints?: string[]\n cautions?: string[]\n}\n\nexport type LapContextResponse = { context: AgentContext }\n\n// ── Unified observe ──────────────────────────────────────────────\n// Single-call bootstrap. Replaces the get_state + list_actions +\n// describe_app trio for the common \"what can I see, what can I do\"\n// question. Returns the dynamic state + actions slice alongside the\n// static description (name/version/messages/docs) and any\n// state-derived context so one round-trip gives the LLM everything it\n// needs to decide its next action.\n\nexport type LapObserveResponse = {\n state: unknown\n actions: LapActionsResponse['actions']\n description: LapDescribeResponse\n context: AgentContext | null\n}\n\n// LAP endpoint catalog — a compile-time map binding each path to its\n// request/response shape. Useful for the bridge's dispatcher and for\n// typed test helpers.\nexport type LapEndpointMap = {\n '/lap/v1/describe': { req: null; res: LapDescribeResponse }\n '/lap/v1/state': { req: LapStateRequest; res: LapStateResponse }\n '/lap/v1/actions': { req: null; res: LapActionsResponse }\n '/lap/v1/message': { req: LapMessageRequest; res: LapMessageResponse }\n '/lap/v1/confirm-result': { req: LapConfirmResultRequest; res: LapConfirmResultResponse }\n '/lap/v1/wait': { req: LapWaitRequest; res: LapWaitResponse }\n '/lap/v1/query-dom': { req: LapQueryDomRequest; res: LapQueryDomResponse }\n '/lap/v1/describe-visible': { req: null; res: LapDescribeVisibleResponse }\n '/lap/v1/context': { req: null; res: LapContextResponse }\n '/lap/v1/observe': { req: null; res: LapObserveResponse }\n}\n\nexport type LapPath = keyof LapEndpointMap\nexport type LapRequest<P extends LapPath> = LapEndpointMap[P]['req']\nexport type LapResponse<P extends LapPath> = LapEndpointMap[P]['res']\n\n// ── Relay WS frames ──────────────────────────────────────────────\n// Bidirectional framing between the LLui runtime in the browser and\n// the @llui/agent server over /agent/ws. See spec §10.5.\n\nexport type LogKind =\n | 'proposed'\n | 'dispatched'\n | 'confirmed'\n | 'rejected'\n | 'blocked'\n | 'read'\n | 'error'\n\nexport type LogEntry = {\n id: string\n at: number\n kind: LogKind\n variant?: string\n intent?: string\n detail?: string\n /**\n * Structural diff from pre-dispatch state to post-drain state, in\n * JSON-Patch shape. Populated only for `kind: 'dispatched'` entries\n * — read entries (get_state / list_actions / observe / …) don't\n * mutate state, and an empty diff would just be noise. Lets the\n * agent reconstruct what each past action did without re-fetching\n * state snapshots.\n */\n stateDiff?: import('./state-diff.js').StateDiff\n}\n\nexport type HelloFrame = {\n t: 'hello'\n appName: string\n appVersion: string\n msgSchema: Record<string, MessageSchemaEntry>\n stateSchema: object\n affordancesSample: object[]\n docs: AgentDocs | null\n schemaHash: string\n}\n\nexport type RpcReplyFrame = { t: 'rpc-reply'; id: string; result: unknown }\nexport type RpcErrorFrame = { t: 'rpc-error'; id: string; code: string; detail?: string }\nexport type ConfirmResolvedFrame = {\n t: 'confirm-resolved'\n confirmId: string\n outcome: 'confirmed' | 'user-cancelled'\n stateAfter?: unknown\n}\nexport type StateUpdateFrame = { t: 'state-update'; path: string; stateAfter: unknown }\nexport type LogAppendFrame = { t: 'log-append'; entry: LogEntry }\n\nexport type ClientFrame =\n | HelloFrame\n | RpcReplyFrame\n | RpcErrorFrame\n | ConfirmResolvedFrame\n | StateUpdateFrame\n | LogAppendFrame\n\nexport type RpcFrame = { t: 'rpc'; id: string; tool: string; args: unknown }\nexport type RevokedFrame = { t: 'revoked' }\nexport type ActiveFrame = { t: 'active' }\n\nexport type ServerFrame = RpcFrame | RevokedFrame | ActiveFrame\n\n// ── Tokens + pairing ─────────────────────────────────────────────\n\ndeclare const TokenBrand: unique symbol\nexport type AgentToken = string & { readonly [TokenBrand]: 'AgentToken' }\n\nexport type TokenStatus =\n | 'awaiting-ws'\n | 'awaiting-claude'\n | 'active'\n | 'pending-resume'\n | 'revoked'\n\nexport type TokenRecord = {\n tid: string\n /**\n * SHA-256 hex of the bearer token. The plaintext token is never\n * stored — incoming requests hash their `Authorization: Bearer …`\n * value and look up by this field. Hash-only storage keeps a leaked\n * store from being a live-token leak. Mirrors the standard session-\n * cookie / API-key pattern.\n */\n tokenHash: string\n uid: string | null\n status: TokenStatus\n createdAt: number\n /**\n * Hard-expiry in milliseconds since epoch. The mint endpoint sets\n * this to `now + hardExpiryMs`; the verify path rejects requests\n * presenting tokens whose record has `expiresAt <= now`. Pre-0.0.35\n * the equivalent value lived inside the JWT payload as `exp` (in\n * seconds); the new opaque-token flow keeps it server-side so the\n * record is the single source of truth.\n */\n expiresAt: number\n lastSeenAt: number\n pendingResumeUntil: number | null\n origin: string\n label: string | null\n}\n\nexport type AgentSession = {\n tid: string\n label: string\n status: 'active' | 'pending-resume' | 'revoked'\n createdAt: number\n lastSeenAt: number\n}\n\n// HTTP envelopes for the mint/resume/revoke/sessions endpoints (non-LAP).\n\nexport type MintRequest = Record<string, never>\nexport type MintResponse = {\n token: AgentToken\n tid: string\n wsUrl: string\n lapUrl: string\n expiresAt: number\n}\n\nexport type ResumeListRequest = { tids: string[] }\nexport type ResumeListResponse = { sessions: AgentSession[] }\n\nexport type ResumeClaimRequest = { tid: string }\nexport type ResumeClaimResponse = { token: AgentToken; wsUrl: string }\n\nexport type RevokeRequest = { tid: string }\nexport type RevokeResponse = { status: 'revoked' }\n\nexport type SessionsResponse = { sessions: AgentSession[] }\n\n// ── Audit ────────────────────────────────────────────────────────\n\nexport type AuditEvent =\n | 'mint'\n | 'claim'\n | 'resume'\n | 'revoke'\n | 'lap-call'\n | 'msg-dispatched'\n | 'msg-blocked'\n | 'confirm-proposed'\n | 'confirm-approved'\n | 'confirm-rejected'\n | 'rate-limited'\n | 'auth-failed'\n\nexport type AuditEntry = {\n at: number\n tid: string | null\n uid: string | null\n event: AuditEvent\n detail: object\n}\n\n// ── Codec exports ─────────────────────────────────────────────────\n//\n// Re-exported here so consumers can `import { ..., type AgentCodec }\n// from '@llui/agent/protocol'`. The implementation lives in\n// `./codecs.ts` to keep the protocol type surface together but the\n// runtime registry/walkers separate.\n\nexport {\n WIRE_TAG,\n WIRE_VALUE,\n CodecRegistry,\n isoDateCodec,\n epochMillisCodec,\n makeDefaultCodecs,\n encodeForWire,\n decodeFromWire,\n type AgentCodec,\n} from './codecs.js'\n"]}
|