@llui/agent 0.0.35 → 0.0.36
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/README.md
CHANGED
|
@@ -27,7 +27,6 @@ import { createLluiAgentServer } from '@llui/agent/server'
|
|
|
27
27
|
import express from 'express'
|
|
28
28
|
|
|
29
29
|
const agent = createLluiAgentServer({
|
|
30
|
-
signingKey: process.env.LLUI_AGENT_KEY!,
|
|
31
30
|
identityResolver: async (req) => req.cookies.user_id ?? null,
|
|
32
31
|
})
|
|
33
32
|
|
|
@@ -118,12 +117,15 @@ export const App = component<State, Msg, Effect>({
|
|
|
118
117
|
|
|
119
118
|
## Annotations reference
|
|
120
119
|
|
|
121
|
-
| Tag | Semantics
|
|
122
|
-
| ------------------- |
|
|
123
|
-
| `@intent("...")` | Human-readable label for Claude + confirmation UI + log
|
|
124
|
-
| `@alwaysAffordable` | Surfaces to Claude even when no binding is currently
|
|
125
|
-
| `@
|
|
126
|
-
| `@
|
|
120
|
+
| Tag | Semantics |
|
|
121
|
+
| ------------------- | -------------------------------------------------------------------- |
|
|
122
|
+
| `@intent("...")` | Human-readable label for Claude + confirmation UI + log |
|
|
123
|
+
| `@alwaysAffordable` | Surfaces to Claude even when no live UI binding is currently mounted |
|
|
124
|
+
| `@agentOnly` | No human UI binding exists; agent is the sole dispatcher |
|
|
125
|
+
| `@requiresConfirm` | Claude must propose; user approves before dispatch |
|
|
126
|
+
| `@humanOnly` | Claude cannot dispatch; not in `list_actions` |
|
|
127
|
+
|
|
128
|
+
**Default (no tag).** A `'shared'` variant — both audiences can dispatch — is offered to the agent only when a live UI binding maps to it (e.g. an `onClick` is currently mounted). When the user navigates away and the corresponding subtree unmounts, the variant disappears from `list_actions`. This mirrors what the human can click. Tag `@alwaysAffordable` (or list the Msg from `agentAffordances(state)`) to keep an agent-driven Msg available regardless of UI state.
|
|
127
129
|
|
|
128
130
|
## App state shape (host integration)
|
|
129
131
|
|
|
@@ -21,13 +21,30 @@ export type ListActionsHost = {
|
|
|
21
21
|
* point — they never reach the LLM.
|
|
22
22
|
*
|
|
23
23
|
* `source` distinguishes WHERE the affordance came from:
|
|
24
|
-
* - `'binding'`
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
24
|
+
* - `'binding'` — a tagged event handler is currently mounted in a
|
|
25
|
+
* live scope (refcount > 0). Variants inside dead branches —
|
|
26
|
+
* `show({when: false})`, unmounted `branch()` cases, removed `each`
|
|
27
|
+
* items — auto-vanish from this set as their lifetimes dispose.
|
|
28
|
+
* This is the framework's "what can the user click right now"
|
|
29
|
+
* answer, and it's the default surface for the agent.
|
|
30
|
+
* - `'always-affordable'` — either the app's `agentAffordances(state)`
|
|
31
|
+
* hook listed the variant, or the variant carries the
|
|
32
|
+
* `@alwaysAffordable` JSDoc tag. Both are the explicit "agent can
|
|
33
|
+
* reach this even when no live UI binding maps to it" knob — bulk
|
|
34
|
+
* seed ops (`Matrix/AddAlternatives`) and similar agent-driven
|
|
35
|
+
* paths typically land here.
|
|
36
|
+
* - `'schema'` — variant is annotated `@agentOnly` (the canonical
|
|
37
|
+
* "no UI button maps to this; the agent is the only dispatcher")
|
|
38
|
+
* and isn't already covered above. The payload is schema-synthesized
|
|
39
|
+
* and the agent fills it in.
|
|
40
|
+
*
|
|
41
|
+
* `'shared'` variants WITHOUT a live binding, without
|
|
42
|
+
* `agentAffordances` mention, and without `@alwaysAffordable` are
|
|
43
|
+
* **deliberately hidden**. They're reachable through UI navigation —
|
|
44
|
+
* the human user can't click them right now, and dispatching them
|
|
45
|
+
* blindly would mutate state that drives `show()`/`branch()` gates,
|
|
46
|
+
* popping hidden UI subtrees into view in places the user didn't
|
|
47
|
+
* navigate to.
|
|
31
48
|
*/
|
|
32
49
|
export type ListActionsResult = {
|
|
33
50
|
actions: Array<{
|
|
@@ -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
|
|
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,CAqH1E"}
|
|
@@ -30,7 +30,8 @@ export function handleListActions(host) {
|
|
|
30
30
|
fieldHints: variantSchema ? collectFieldHints(variantSchema) : [],
|
|
31
31
|
});
|
|
32
32
|
}
|
|
33
|
-
// From
|
|
33
|
+
// From `agentAffordances(state)` — the integrator's explicit list of
|
|
34
|
+
// currently-reachable Msgs, with concrete payloads they author.
|
|
34
35
|
for (const msg of affordances) {
|
|
35
36
|
const ann = annotations[msg.type];
|
|
36
37
|
if (ann?.dispatchMode === 'human-only')
|
|
@@ -52,47 +53,64 @@ export function handleListActions(host) {
|
|
|
52
53
|
fieldHints: variantSchema ? collectFieldHints(variantSchema) : [],
|
|
53
54
|
});
|
|
54
55
|
}
|
|
55
|
-
// From
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
56
|
+
// From `@alwaysAffordable` annotations — the per-variant equivalent
|
|
57
|
+
// of `agentAffordances`. Variants tagged this way surface regardless
|
|
58
|
+
// of whether a live binding maps to them, so bulk seed ops
|
|
59
|
+
// (`Matrix/AddAlternatives`) and similar agent-driven paths are
|
|
60
|
+
// available even when their UI counterparts (if any) aren't mounted.
|
|
61
|
+
// The payload is schema-synthesized — `agentAffordances` is the way
|
|
62
|
+
// to ship a concrete pre-filled payload alongside the affordance.
|
|
63
|
+
for (const [variant, ann] of Object.entries(annotations)) {
|
|
64
|
+
if (seen.has(variant))
|
|
65
|
+
continue;
|
|
66
|
+
if (ann.dispatchMode === 'human-only')
|
|
67
|
+
continue;
|
|
68
|
+
if (!ann.alwaysAffordable)
|
|
69
|
+
continue;
|
|
70
|
+
seen.add(variant);
|
|
71
|
+
const fields = schema?.variants[variant];
|
|
72
|
+
out.push({
|
|
73
|
+
variant,
|
|
74
|
+
intent: ann.intent,
|
|
75
|
+
requiresConfirm: ann.requiresConfirm,
|
|
76
|
+
dispatchMode: ann.dispatchMode === 'agent-only' ? 'agent-only' : 'shared',
|
|
77
|
+
source: 'always-affordable',
|
|
78
|
+
selectorHint: null,
|
|
79
|
+
payloadHint: fields ? synthesizePayload(variant, fields) : null,
|
|
80
|
+
warning: ann.warning,
|
|
81
|
+
examples: ann.examples,
|
|
82
|
+
emits: ann.emits,
|
|
83
|
+
fieldHints: fields ? collectFieldHints(fields) : [],
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
// From schema — variants that aren't already surfaced above and that
|
|
87
|
+
// the author marked `@agentOnly`. The canonical "no UI button maps
|
|
88
|
+
// to this; the agent is the only dispatcher." Bulk edits, imports,
|
|
89
|
+
// admin operations that have no human-facing affordance at all.
|
|
69
90
|
//
|
|
70
|
-
// `'shared'` variants WITHOUT
|
|
71
|
-
//
|
|
72
|
-
//
|
|
73
|
-
//
|
|
91
|
+
// `'shared'` variants WITHOUT a live binding stay hidden here — they
|
|
92
|
+
// are reachable through UI navigation, and dispatching them while
|
|
93
|
+
// their UI subtree is unmounted would pop hidden state in places the
|
|
94
|
+
// user didn't navigate to. The explicit knobs are `@alwaysAffordable`
|
|
95
|
+
// (handled above) or `agentAffordances(state)`.
|
|
74
96
|
if (schema) {
|
|
75
97
|
for (const [variant, fields] of Object.entries(schema.variants)) {
|
|
76
98
|
if (seen.has(variant))
|
|
77
99
|
continue;
|
|
78
100
|
const ann = annotations[variant];
|
|
79
|
-
if (ann?.dispatchMode
|
|
80
|
-
continue;
|
|
81
|
-
const isAgentOnly = ann?.dispatchMode === 'agent-only';
|
|
82
|
-
const isDocumentedShared = ann?.dispatchMode !== 'agent-only' && Boolean(ann?.intent);
|
|
83
|
-
if (!isAgentOnly && !isDocumentedShared)
|
|
101
|
+
if (ann?.dispatchMode !== 'agent-only')
|
|
84
102
|
continue;
|
|
85
103
|
out.push({
|
|
86
104
|
variant,
|
|
87
|
-
intent: ann
|
|
88
|
-
requiresConfirm: ann
|
|
89
|
-
dispatchMode:
|
|
105
|
+
intent: ann.intent,
|
|
106
|
+
requiresConfirm: ann.requiresConfirm,
|
|
107
|
+
dispatchMode: 'agent-only',
|
|
90
108
|
source: 'schema',
|
|
91
109
|
selectorHint: null,
|
|
92
110
|
payloadHint: synthesizePayload(variant, fields),
|
|
93
|
-
warning: ann
|
|
94
|
-
examples: ann
|
|
95
|
-
emits: ann
|
|
111
|
+
warning: ann.warning,
|
|
112
|
+
examples: ann.examples,
|
|
113
|
+
emits: ann.emits,
|
|
96
114
|
fieldHints: collectFieldHints(fields),
|
|
97
115
|
});
|
|
98
116
|
}
|
|
@@ -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.\n * - `'always-affordable'` — the app's `agentAffordances(state)` hook\n * listed it as available right now.\n * - `'schema'` — neither of the above; the variant is in the\n * Msg union, annotated `@agentOnly`, and the payload is example-\n * only (no live UI binding maps to it). Bulk-edit operations\n * typically land here.\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 always-affordable\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 schema — variants that aren't already surfaced as bindings\n // or always-affordable, and that the author intentionally documented\n // for agent use. Two cases land here:\n //\n // 1. `@agentOnly` variants — the canonical \"no UI button maps to\n // this; the agent is the only dispatcher.\" Bulk edits, imports,\n // admin operations.\n // 2. `'shared'` variants with `@intent` but no live binding — the\n // author wrote a description for the variant, signalling it's\n // a real agent-callable dispatch even when the corresponding UI\n // affordance is closed (e.g. `Matrix/SetQuantityValue` lives\n // inside the cell editor; without this case, an agent that\n // wants to set one cell is forced to use the bulk\n // `Matrix/SetManyCells` with a 1-element array).\n //\n // `'shared'` variants WITHOUT `@intent` stay hidden — undocumented\n // shared variants are usually internal (effect-result messages,\n // router-internal acks) that aren't intended as agent affordances.\n // `'human-only'` is always filtered.\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 === 'human-only') continue\n const isAgentOnly = ann?.dispatchMode === 'agent-only'\n const isDocumentedShared = ann?.dispatchMode !== 'agent-only' && Boolean(ann?.intent)\n if (!isAgentOnly && !isDocumentedShared) continue\n out.push({\n variant,\n intent: ann?.intent ?? null,\n requiresConfirm: ann?.requiresConfirm ?? false,\n dispatchMode: isAgentOnly ? 'agent-only' : 'shared',\n source: 'schema',\n selectorHint: null,\n payloadHint: synthesizePayload(variant, fields),\n warning: ann?.warning ?? null,\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}\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.\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 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 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}\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.\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 return null\n}\n"]}
|