llui-agent 0.0.16 → 0.0.17
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/tools.d.ts +13 -19
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +13 -196
- package/dist/tools.js.map +1 -1
- package/package.json +2 -2
package/dist/tools.d.ts
CHANGED
|
@@ -1,21 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
/**
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export
|
|
14
|
-
kind: 'meta';
|
|
15
|
-
name: string;
|
|
16
|
-
description: string;
|
|
17
|
-
schema: z.ZodObject<z.ZodRawShape>;
|
|
18
|
-
}
|
|
19
|
-
export type ToolDescriptor = ForwardedToolDescriptor | MetaToolDescriptor;
|
|
1
|
+
import { type McpForwardedToolDescriptor, type McpMetaToolDescriptor } from '@llui/agent/mcp/tools';
|
|
2
|
+
/**
|
|
3
|
+
* Tool catalogue for the bridge. Forwarded tools and disconnect_session
|
|
4
|
+
* are imported from `@llui/agent/mcp/tools` (the single source of truth).
|
|
5
|
+
* `connect_session` is defined here because the bridge requires a `url`
|
|
6
|
+
* argument that the server-side MCP does not — the two surfaces have
|
|
7
|
+
* different signatures.
|
|
8
|
+
*
|
|
9
|
+
* Spec §8.
|
|
10
|
+
*/
|
|
11
|
+
export type { McpForwardedToolDescriptor as ForwardedToolDescriptor };
|
|
12
|
+
export type { McpMetaToolDescriptor as MetaToolDescriptor };
|
|
13
|
+
export type ToolDescriptor = McpForwardedToolDescriptor | McpMetaToolDescriptor;
|
|
20
14
|
export declare const TOOL_DESCRIPTORS: ToolDescriptor[];
|
|
21
15
|
//# sourceMappingURL=tools.d.ts.map
|
package/dist/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,0BAA0B,EAC/B,KAAK,qBAAqB,EAC3B,MAAM,uBAAuB,CAAA;AAE9B;;;;;;;;GAQG;AAGH,YAAY,EAAE,0BAA0B,IAAI,uBAAuB,EAAE,CAAA;AACrE,YAAY,EAAE,qBAAqB,IAAI,kBAAkB,EAAE,CAAA;AAC3D,MAAM,MAAM,cAAc,GAAG,0BAA0B,GAAG,qBAAqB,CAAA;AAa/E,eAAO,MAAM,gBAAgB,EAAE,cAAc,EAI5C,CAAA"}
|
package/dist/tools.js
CHANGED
|
@@ -1,200 +1,17 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
* state" loop from 5 round-trips to 2.
|
|
13
|
-
*
|
|
14
|
-
* - **Legacy / specialized**: `describe_app`, `get_state`,
|
|
15
|
-
* `list_actions`, `wait_for_change`. Kept for back-compat and niche
|
|
16
|
-
* uses (e.g. scoped state reads via JSON pointer, external state
|
|
17
|
-
* pushes). New integrations should prefer `observe`.
|
|
18
|
-
*
|
|
19
|
-
* Spec §8.
|
|
20
|
-
*
|
|
21
|
-
* The catalogue is the single source of truth — Zod schemas drive both
|
|
22
|
-
* runtime input validation and the JSON Schema published in
|
|
23
|
-
* `tools/list`. Forwarded tools also carry their LAP endpoint path so
|
|
24
|
-
* `bridge.ts` can register one generic forwarder that loops over them.
|
|
25
|
-
*/
|
|
26
|
-
const empty = z.object({});
|
|
2
|
+
import { FORWARDED_TOOL_DESCRIPTORS, DISCONNECT_SESSION_DESCRIPTOR, } from '@llui/agent/mcp/tools';
|
|
3
|
+
const CONNECT_SESSION_DESCRIPTOR = {
|
|
4
|
+
kind: 'meta',
|
|
5
|
+
name: 'connect_session',
|
|
6
|
+
description: 'Bind this Claude conversation to a specific LLui app. Call ONCE per chat when the user pastes a connect snippet from the LLui app — the snippet contains the url and token to forward here. The result includes the full observe bundle ({state, actions, description, context}) so you have everything you need to start acting — no separate describe_app / get_state / list_actions / describe_context follow-up is required on the first turn. Use observe later when you want a refreshed snapshot.',
|
|
7
|
+
schema: z.object({
|
|
8
|
+
url: z.string().describe('LAP base URL (e.g. https://app.example/agent/lap/v1)'),
|
|
9
|
+
token: z.string().describe('Bearer token for LAP calls'),
|
|
10
|
+
}),
|
|
11
|
+
};
|
|
27
12
|
export const TOOL_DESCRIPTORS = [
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
description: 'Bind this Claude conversation to a specific LLui app. Call ONCE per chat when the user pastes a connect snippet from the LLui app — the snippet contains the url and token to forward here. The result includes the full observe bundle ({state, actions, description, context}) so you have everything you need to start acting — no separate describe_app / get_state / list_actions / describe_context follow-up is required on the first turn. Use observe later when you want a refreshed snapshot.',
|
|
32
|
-
schema: z.object({
|
|
33
|
-
url: z.string().describe('LAP base URL (e.g. https://app.example/agent/lap/v1)'),
|
|
34
|
-
token: z.string().describe('Bearer token for LAP calls'),
|
|
35
|
-
}),
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
kind: 'meta',
|
|
39
|
-
name: 'disconnect_session',
|
|
40
|
-
description: 'Clear the binding for this Claude conversation. Subsequent LLui tool calls will fail until rebind.',
|
|
41
|
-
schema: empty,
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
kind: 'forward',
|
|
45
|
-
name: 'observe',
|
|
46
|
-
description: 'Unified snapshot — returns {state, actions, description, context} in one call. Use this as the default "what can I see, what can I do" read; prefer it over describe_app + get_state + list_actions. Typical flow: observe → send_message → (repeat). The response includes the static app description (name, version, msgSchema, docs) on every call so first-time callers do not need a separate describe_app.',
|
|
47
|
-
schema: empty,
|
|
48
|
-
lapPath: '/observe',
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
kind: 'forward',
|
|
52
|
-
name: 'describe_app',
|
|
53
|
-
description: "Return the bound app's name, version, state/message schemas, annotations, and static docs. Legacy — prefer `observe`, which includes this as `description`.",
|
|
54
|
-
schema: empty,
|
|
55
|
-
lapPath: '/describe',
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
kind: 'forward',
|
|
59
|
-
name: 'get_state',
|
|
60
|
-
description: 'Return the current app state. Optional `path` (JSON-pointer) to narrow the slice. Legacy for full-state reads — prefer `observe`. Still useful for scoped reads via JSON pointer.',
|
|
61
|
-
schema: z.object({
|
|
62
|
-
path: z.string().optional().describe('Optional JSON-pointer, e.g. "/user/name"'),
|
|
63
|
-
}),
|
|
64
|
-
lapPath: '/state',
|
|
65
|
-
},
|
|
66
|
-
{
|
|
67
|
-
kind: 'forward',
|
|
68
|
-
name: 'query_state',
|
|
69
|
-
description: 'Read a single slice of state via JSON-pointer path. Returns `{found: true, value}` on hit or `{found: false, detail}` on miss (missing key, walking through null, etc.). Cheaper than `observe` when checking one field. Path syntax: `""` (whole state), `"/auth/user"`, `"/items/0/id"`, `"/key~1with~1slash"` (escaped `/`), `"/key~0tilde"` (escaped `~`).',
|
|
70
|
-
schema: z.object({
|
|
71
|
-
path: z.string().describe('JSON-pointer (RFC 6901) — `/auth/user` or `""` for whole state'),
|
|
72
|
-
}),
|
|
73
|
-
lapPath: '/query-state',
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
kind: 'forward',
|
|
77
|
-
name: 'describe_recent_actions',
|
|
78
|
-
description: 'Return the most recent log entries for this session (newest first). Each `dispatched` entry includes a `stateDiff` showing what changed. Useful for self-correction over multi-step flows — read your own past dispatches without re-querying full state. Filter by `kind` (e.g. `"dispatched"`) to skip read-only entries.',
|
|
79
|
-
schema: z.object({
|
|
80
|
-
n: z.number().int().positive().optional().describe('How many entries to return (default 10)'),
|
|
81
|
-
kind: z
|
|
82
|
-
.string()
|
|
83
|
-
.optional()
|
|
84
|
-
.describe('Filter to a specific kind (e.g. "dispatched", "read", "error")'),
|
|
85
|
-
}),
|
|
86
|
-
lapPath: '/recent-actions',
|
|
87
|
-
},
|
|
88
|
-
{
|
|
89
|
-
kind: 'forward',
|
|
90
|
-
name: 'would_dispatch',
|
|
91
|
-
description: 'Predict what dispatching `msg` would do without committing it. Runs the reducer in isolation against current state and returns `{stateDiff, effects}`. Effects are listed but NOT executed — the cloud is not hit, analytics do not fire. Use this to weigh a candidate action before sending: "if I dispatch X, will it change Y?" Pure-reducer assumption: if the reducer branches on Date.now() / localStorage / random, prediction drifts from real dispatch by exactly that impurity.',
|
|
92
|
-
schema: z.object({
|
|
93
|
-
msg: z
|
|
94
|
-
.object({ type: z.string() })
|
|
95
|
-
.passthrough()
|
|
96
|
-
.describe('The candidate message; must have a `type` string'),
|
|
97
|
-
}),
|
|
98
|
-
lapPath: '/would-dispatch',
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
kind: 'forward',
|
|
102
|
-
name: 'list_actions',
|
|
103
|
-
description: 'Return the currently-affordable actions: visible UI bindings plus agent-affordable registry entries, filtered by annotation gates. Legacy — prefer `observe`, which includes this as `actions`.',
|
|
104
|
-
schema: empty,
|
|
105
|
-
lapPath: '/actions',
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
kind: 'forward',
|
|
109
|
-
name: 'send_message',
|
|
110
|
-
description: 'Dispatch a message to the app. Blocks by default until the message queue goes idle (drain semantics — captures http/delay/debounce round-trips that feed back as messages). Returns {status, stateDiff, actions, drain} on dispatched, {status: "pending-confirmation", confirmId} when the variant is @requiresConfirm, or {status: "rejected", reason} on validation failures. By default the response carries `stateDiff` (a JSON-Patch-shaped delta) and not the full post-state — apply the diff to the snapshot you got from `connect`/`observe`. Pass `includeState: true` if you want the full snapshot back (rare; expensive on bandwidth and context for large states). `drain.timedOut: true` means the 5s cap was hit while messages were still arriving — follow up with `observe` to resync. `actions` in the response reflects the new state, so you normally do not need a separate `observe` after a send.',
|
|
111
|
-
schema: z.object({
|
|
112
|
-
msg: z
|
|
113
|
-
.object({ type: z.string() })
|
|
114
|
-
.passthrough()
|
|
115
|
-
.describe('The message to dispatch; must have a `type` string'),
|
|
116
|
-
reason: z
|
|
117
|
-
.string()
|
|
118
|
-
.optional()
|
|
119
|
-
.describe('User-facing rationale (required for confirm-gated variants)'),
|
|
120
|
-
waitFor: z
|
|
121
|
-
.enum(['drained', 'idle', 'none'])
|
|
122
|
-
.optional()
|
|
123
|
-
.describe('"drained" (default) waits for the message queue to go idle; "idle" flushes the update cycle only (no async effects); "none" is fire-and-forget.'),
|
|
124
|
-
drainQuietMs: z
|
|
125
|
-
.number()
|
|
126
|
-
.optional()
|
|
127
|
-
.describe('Quiescence window for waitFor:"drained". Drain completes when no commit fires for this many ms. Default 100.'),
|
|
128
|
-
timeoutMs: z
|
|
129
|
-
.number()
|
|
130
|
-
.optional()
|
|
131
|
-
.describe('Hard cap on total wait. Default 5000. For waitFor:"drained", this bounds how long the drain loop runs; for pending-confirmation, how long to wait for user approval.'),
|
|
132
|
-
includeState: z
|
|
133
|
-
.boolean()
|
|
134
|
-
.optional()
|
|
135
|
-
.describe('Include the full post-drain `stateAfter` snapshot in the response. Default false — `stateDiff` is what callers normally need, and resending the full state on every dispatch wastes bandwidth and context. Set true only when you need a fresh snapshot back (e.g., after a long-running effect that may have produced changes the diff misses).'),
|
|
136
|
-
}),
|
|
137
|
-
lapPath: '/message',
|
|
138
|
-
},
|
|
139
|
-
{
|
|
140
|
-
kind: 'forward',
|
|
141
|
-
name: 'get_confirm_result',
|
|
142
|
-
description: 'Poll a pending-confirmation by confirmId. Returns confirmed / rejected / still-pending.',
|
|
143
|
-
schema: z.object({
|
|
144
|
-
confirmId: z.string(),
|
|
145
|
-
timeoutMs: z.number().optional(),
|
|
146
|
-
}),
|
|
147
|
-
lapPath: '/confirm-result',
|
|
148
|
-
},
|
|
149
|
-
{
|
|
150
|
-
kind: 'forward',
|
|
151
|
-
name: 'wait_for_change',
|
|
152
|
-
description: 'Long-poll for a state change. Returns changed / timeout. Specialized — use for external state pushes (WebSocket messages, timers) that arrive while Claude is idle. For the normal send-then-read loop, `send_message` with `waitFor:"drained"` already waits for effect round-trips.',
|
|
153
|
-
schema: z.object({
|
|
154
|
-
path: z
|
|
155
|
-
.string()
|
|
156
|
-
.optional()
|
|
157
|
-
.describe('Optional JSON-pointer to narrow which state changes trigger resolution'),
|
|
158
|
-
timeoutMs: z.number().optional(),
|
|
159
|
-
}),
|
|
160
|
-
lapPath: '/wait',
|
|
161
|
-
},
|
|
162
|
-
{
|
|
163
|
-
kind: 'forward',
|
|
164
|
-
name: 'narrate',
|
|
165
|
-
description: 'Push a one-line prose update into the in-app activity feed without dispatching a Msg. Use BEFORE long-running actions ("Looking at your cart now…", "Calling the pricing API — should take ~2s"), to surface inferred reasoning ("I notice you have an unsaved draft in `notes` — assuming you want to keep it"), or to acknowledge user input before acting on it. Keep narration single-line and present-tense; the user reads it in the activity panel inline with your dispatched actions. The text shows up as a `narrate`-kind entry — distinct from `dispatched` and `read`. Returns { ok: true } once the host runtime has the entry.',
|
|
166
|
-
schema: z.object({
|
|
167
|
-
text: z.string().describe('The narration prose, ideally one sentence. Required.'),
|
|
168
|
-
intent: z
|
|
169
|
-
.string()
|
|
170
|
-
.optional()
|
|
171
|
-
.describe('Short label shown next to the narration (e.g. "Thinking", "Notice", "Plan"). Defaults to "Agent narrated".'),
|
|
172
|
-
}),
|
|
173
|
-
lapPath: '/narrate',
|
|
174
|
-
},
|
|
175
|
-
{
|
|
176
|
-
kind: 'forward',
|
|
177
|
-
name: 'query_dom',
|
|
178
|
-
description: 'Read elements tagged with data-agent="<name>" in the rendered UI.',
|
|
179
|
-
schema: z.object({
|
|
180
|
-
name: z.string(),
|
|
181
|
-
multiple: z.boolean().optional(),
|
|
182
|
-
}),
|
|
183
|
-
lapPath: '/query-dom',
|
|
184
|
-
},
|
|
185
|
-
{
|
|
186
|
-
kind: 'forward',
|
|
187
|
-
name: 'describe_visible_content',
|
|
188
|
-
description: 'Return a structured outline of the currently-visible data-agent-tagged subtrees.',
|
|
189
|
-
schema: empty,
|
|
190
|
-
lapPath: '/describe-visible',
|
|
191
|
-
},
|
|
192
|
-
{
|
|
193
|
-
kind: 'forward',
|
|
194
|
-
name: 'describe_context',
|
|
195
|
-
description: 'Return the current per-state narrative docs (agentContext) — what the user is trying to do right now.',
|
|
196
|
-
schema: empty,
|
|
197
|
-
lapPath: '/context',
|
|
198
|
-
},
|
|
13
|
+
CONNECT_SESSION_DESCRIPTOR,
|
|
14
|
+
DISCONNECT_SESSION_DESCRIPTOR,
|
|
15
|
+
...FORWARDED_TOOL_DESCRIPTORS,
|
|
199
16
|
];
|
|
200
17
|
//# sourceMappingURL=tools.js.map
|
package/dist/tools.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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","sourcesContent":["import { z } from 'zod'\n\n/**\n * Tool catalogue exposed to Claude through the MCP bridge. Two tiers:\n *\n * - **Efficient path (recommended)**: `observe` + `send_message`.\n * `observe` returns state + actions + description + context in a\n * single LAP call, replacing the old describe_app + get_state +\n * list_actions trio. `send_message` defaults to `waitFor:'drained'`,\n * blocking until the message queue goes idle (http/delay/debounce\n * round-trips complete) and returning the new state + actions +\n * drain meta. Together these cut the \"check state → act → check\n * state\" loop from 5 round-trips to 2.\n *\n * - **Legacy / specialized**: `describe_app`, `get_state`,\n * `list_actions`, `wait_for_change`. Kept for back-compat and niche\n * uses (e.g. scoped state reads via JSON pointer, external state\n * pushes). New integrations should prefer `observe`.\n *\n * Spec §8.\n *\n * The catalogue is the single source of truth — Zod schemas drive both\n * runtime input validation and the JSON Schema published in\n * `tools/list`. Forwarded tools also carry their LAP endpoint path so\n * `bridge.ts` can register one generic forwarder that loops over them.\n */\n\nconst empty = z.object({})\n\n/** Descriptor for a tool that forwards directly to the bound LAP server. */\nexport interface ForwardedToolDescriptor {\n kind: 'forward'\n name: string\n description: string\n /** Zod schema defining the tool's input shape. */\n schema: z.ZodObject<z.ZodRawShape>\n /** LAP endpoint path (relative to the binding's base URL). */\n lapPath: string\n}\n\n/** Descriptor for a tool whose handler is implemented in the bridge itself. */\nexport interface MetaToolDescriptor {\n kind: 'meta'\n name: string\n description: string\n schema: z.ZodObject<z.ZodRawShape>\n}\n\nexport type ToolDescriptor = ForwardedToolDescriptor | MetaToolDescriptor\n\nexport const TOOL_DESCRIPTORS: ToolDescriptor[] = [\n {\n kind: 'meta',\n name: 'connect_session',\n description:\n 'Bind this Claude conversation to a specific LLui app. Call ONCE per chat when the user pastes a connect snippet from the LLui app — the snippet contains the url and token to forward here. The result includes the full observe bundle ({state, actions, description, context}) so you have everything you need to start acting — no separate describe_app / get_state / list_actions / describe_context follow-up is required on the first turn. Use observe later when you want a refreshed snapshot.',\n schema: z.object({\n url: z.string().describe('LAP base URL (e.g. https://app.example/agent/lap/v1)'),\n token: z.string().describe('Bearer token for LAP calls'),\n }),\n },\n {\n kind: 'meta',\n name: 'disconnect_session',\n description:\n 'Clear the binding for this Claude conversation. Subsequent LLui tool calls will fail until rebind.',\n schema: empty,\n },\n {\n kind: 'forward',\n name: 'observe',\n description:\n 'Unified snapshot — returns {state, actions, description, context} in one call. Use this as the default \"what can I see, what can I do\" read; prefer it over describe_app + get_state + list_actions. Typical flow: observe → send_message → (repeat). The response includes the static app description (name, version, msgSchema, docs) on every call so first-time callers do not need a separate describe_app.',\n schema: empty,\n lapPath: '/observe',\n },\n {\n kind: 'forward',\n name: 'describe_app',\n description:\n \"Return the bound app's name, version, state/message schemas, annotations, and static docs. Legacy — prefer `observe`, which includes this as `description`.\",\n schema: empty,\n lapPath: '/describe',\n },\n {\n kind: 'forward',\n name: 'get_state',\n description:\n 'Return the current app state. Optional `path` (JSON-pointer) to narrow the slice. Legacy for full-state reads — prefer `observe`. Still useful for scoped reads via JSON pointer.',\n schema: z.object({\n path: z.string().optional().describe('Optional JSON-pointer, e.g. \"/user/name\"'),\n }),\n lapPath: '/state',\n },\n {\n kind: 'forward',\n name: 'query_state',\n description:\n 'Read a single slice of state via JSON-pointer path. Returns `{found: true, value}` on hit or `{found: false, detail}` on miss (missing key, walking through null, etc.). Cheaper than `observe` when checking one field. Path syntax: `\"\"` (whole state), `\"/auth/user\"`, `\"/items/0/id\"`, `\"/key~1with~1slash\"` (escaped `/`), `\"/key~0tilde\"` (escaped `~`).',\n schema: z.object({\n path: z.string().describe('JSON-pointer (RFC 6901) — `/auth/user` or `\"\"` for whole state'),\n }),\n lapPath: '/query-state',\n },\n {\n kind: 'forward',\n name: 'describe_recent_actions',\n description:\n 'Return the most recent log entries for this session (newest first). Each `dispatched` entry includes a `stateDiff` showing what changed. Useful for self-correction over multi-step flows — read your own past dispatches without re-querying full state. Filter by `kind` (e.g. `\"dispatched\"`) to skip read-only entries.',\n schema: z.object({\n n: z.number().int().positive().optional().describe('How many entries to return (default 10)'),\n kind: z\n .string()\n .optional()\n .describe('Filter to a specific kind (e.g. \"dispatched\", \"read\", \"error\")'),\n }),\n lapPath: '/recent-actions',\n },\n {\n kind: 'forward',\n name: 'would_dispatch',\n description:\n 'Predict what dispatching `msg` would do without committing it. Runs the reducer in isolation against current state and returns `{stateDiff, effects}`. Effects are listed but NOT executed — the cloud is not hit, analytics do not fire. Use this to weigh a candidate action before sending: \"if I dispatch X, will it change Y?\" Pure-reducer assumption: if the reducer branches on Date.now() / localStorage / random, prediction drifts from real dispatch by exactly that impurity.',\n schema: z.object({\n msg: z\n .object({ type: z.string() })\n .passthrough()\n .describe('The candidate message; must have a `type` string'),\n }),\n lapPath: '/would-dispatch',\n },\n {\n kind: 'forward',\n name: 'list_actions',\n description:\n 'Return the currently-affordable actions: visible UI bindings plus agent-affordable registry entries, filtered by annotation gates. Legacy — prefer `observe`, which includes this as `actions`.',\n schema: empty,\n lapPath: '/actions',\n },\n {\n kind: 'forward',\n name: 'send_message',\n description:\n 'Dispatch a message to the app. Blocks by default until the message queue goes idle (drain semantics — captures http/delay/debounce round-trips that feed back as messages). Returns {status, stateDiff, actions, drain} on dispatched, {status: \"pending-confirmation\", confirmId} when the variant is @requiresConfirm, or {status: \"rejected\", reason} on validation failures. By default the response carries `stateDiff` (a JSON-Patch-shaped delta) and not the full post-state — apply the diff to the snapshot you got from `connect`/`observe`. Pass `includeState: true` if you want the full snapshot back (rare; expensive on bandwidth and context for large states). `drain.timedOut: true` means the 5s cap was hit while messages were still arriving — follow up with `observe` to resync. `actions` in the response reflects the new state, so you normally do not need a separate `observe` after a send.',\n schema: z.object({\n msg: z\n .object({ type: z.string() })\n .passthrough()\n .describe('The message to dispatch; must have a `type` string'),\n reason: z\n .string()\n .optional()\n .describe('User-facing rationale (required for confirm-gated variants)'),\n waitFor: z\n .enum(['drained', 'idle', 'none'])\n .optional()\n .describe(\n '\"drained\" (default) waits for the message queue to go idle; \"idle\" flushes the update cycle only (no async effects); \"none\" is fire-and-forget.',\n ),\n drainQuietMs: z\n .number()\n .optional()\n .describe(\n 'Quiescence window for waitFor:\"drained\". Drain completes when no commit fires for this many ms. Default 100.',\n ),\n timeoutMs: z\n .number()\n .optional()\n .describe(\n 'Hard cap on total wait. Default 5000. For waitFor:\"drained\", this bounds how long the drain loop runs; for pending-confirmation, how long to wait for user approval.',\n ),\n includeState: z\n .boolean()\n .optional()\n .describe(\n 'Include the full post-drain `stateAfter` snapshot in the response. Default false — `stateDiff` is what callers normally need, and resending the full state on every dispatch wastes bandwidth and context. Set true only when you need a fresh snapshot back (e.g., after a long-running effect that may have produced changes the diff misses).',\n ),\n }),\n lapPath: '/message',\n },\n {\n kind: 'forward',\n name: 'get_confirm_result',\n description:\n 'Poll a pending-confirmation by confirmId. Returns confirmed / rejected / still-pending.',\n schema: z.object({\n confirmId: z.string(),\n timeoutMs: z.number().optional(),\n }),\n lapPath: '/confirm-result',\n },\n {\n kind: 'forward',\n name: 'wait_for_change',\n description:\n 'Long-poll for a state change. Returns changed / timeout. Specialized — use for external state pushes (WebSocket messages, timers) that arrive while Claude is idle. For the normal send-then-read loop, `send_message` with `waitFor:\"drained\"` already waits for effect round-trips.',\n schema: z.object({\n path: z\n .string()\n .optional()\n .describe('Optional JSON-pointer to narrow which state changes trigger resolution'),\n timeoutMs: z.number().optional(),\n }),\n lapPath: '/wait',\n },\n {\n kind: 'forward',\n name: 'narrate',\n description:\n 'Push a one-line prose update into the in-app activity feed without dispatching a Msg. Use BEFORE long-running actions (\"Looking at your cart now…\", \"Calling the pricing API — should take ~2s\"), to surface inferred reasoning (\"I notice you have an unsaved draft in `notes` — assuming you want to keep it\"), or to acknowledge user input before acting on it. Keep narration single-line and present-tense; the user reads it in the activity panel inline with your dispatched actions. The text shows up as a `narrate`-kind entry — distinct from `dispatched` and `read`. Returns { ok: true } once the host runtime has the entry.',\n schema: z.object({\n text: z.string().describe('The narration prose, ideally one sentence. Required.'),\n intent: z\n .string()\n .optional()\n .describe(\n 'Short label shown next to the narration (e.g. \"Thinking\", \"Notice\", \"Plan\"). Defaults to \"Agent narrated\".',\n ),\n }),\n lapPath: '/narrate',\n },\n {\n kind: 'forward',\n name: 'query_dom',\n description: 'Read elements tagged with data-agent=\"<name>\" in the rendered UI.',\n schema: z.object({\n name: z.string(),\n multiple: z.boolean().optional(),\n }),\n lapPath: '/query-dom',\n },\n {\n kind: 'forward',\n name: 'describe_visible_content',\n description: 'Return a structured outline of the currently-visible data-agent-tagged subtrees.',\n schema: empty,\n lapPath: '/describe-visible',\n },\n {\n kind: 'forward',\n name: 'describe_context',\n description:\n 'Return the current per-state narrative docs (agentContext) — what the user is trying to do right now.',\n schema: empty,\n lapPath: '/context',\n },\n]\n"]}
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EACL,0BAA0B,EAC1B,6BAA6B,GAG9B,MAAM,uBAAuB,CAAA;AAiB9B,MAAM,0BAA0B,GAA0B;IACxD,IAAI,EAAE,MAAM;IACZ,IAAI,EAAE,iBAAiB;IACvB,WAAW,EACT,0eAA0e;IAC5e,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,sDAAsD,CAAC;QAChF,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4BAA4B,CAAC;KACzD,CAAC;CACH,CAAA;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAqB;IAChD,0BAA0B;IAC1B,6BAA6B;IAC7B,GAAG,0BAA0B;CAC9B,CAAA","sourcesContent":["import { z } from 'zod'\nimport {\n FORWARDED_TOOL_DESCRIPTORS,\n DISCONNECT_SESSION_DESCRIPTOR,\n type McpForwardedToolDescriptor,\n type McpMetaToolDescriptor,\n} from '@llui/agent/mcp/tools'\n\n/**\n * Tool catalogue for the bridge. Forwarded tools and disconnect_session\n * are imported from `@llui/agent/mcp/tools` (the single source of truth).\n * `connect_session` is defined here because the bridge requires a `url`\n * argument that the server-side MCP does not — the two surfaces have\n * different signatures.\n *\n * Spec §8.\n */\n\n// Re-export types so callers don't need a second import.\nexport type { McpForwardedToolDescriptor as ForwardedToolDescriptor }\nexport type { McpMetaToolDescriptor as MetaToolDescriptor }\nexport type ToolDescriptor = McpForwardedToolDescriptor | McpMetaToolDescriptor\n\nconst CONNECT_SESSION_DESCRIPTOR: McpMetaToolDescriptor = {\n kind: 'meta',\n name: 'connect_session',\n description:\n 'Bind this Claude conversation to a specific LLui app. Call ONCE per chat when the user pastes a connect snippet from the LLui app — the snippet contains the url and token to forward here. The result includes the full observe bundle ({state, actions, description, context}) so you have everything you need to start acting — no separate describe_app / get_state / list_actions / describe_context follow-up is required on the first turn. Use observe later when you want a refreshed snapshot.',\n schema: z.object({\n url: z.string().describe('LAP base URL (e.g. https://app.example/agent/lap/v1)'),\n token: z.string().describe('Bearer token for LAP calls'),\n }),\n}\n\nexport const TOOL_DESCRIPTORS: ToolDescriptor[] = [\n CONNECT_SESSION_DESCRIPTOR,\n DISCONNECT_SESSION_DESCRIPTOR,\n ...FORWARDED_TOOL_DESCRIPTORS,\n]\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "llui-agent",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.17",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"llui-agent": "./dist/cli.js"
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
23
23
|
"zod": "^4.0.0",
|
|
24
|
-
"@llui/agent": "0.0.
|
|
24
|
+
"@llui/agent": "0.0.53"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@types/node": "^22.0.0"
|