page-agent-sdk 1.4.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -29
- package/README.zh-CN.md +27 -27
- package/dist/page-agent-sdk.css +1 -1
- package/dist/page-agent-sdk.iife.js +79 -79
- package/dist/page-agent-sdk.js +181 -176
- package/dist/page-agent-sdk.umd.cjs +17 -17
- package/package.json +2 -2
- package/skills/page-agent-sdk-integrate/SKILL.md +18 -18
- package/skills/page-agent-sdk-integrate/references/advanced.md +29 -29
- package/skills/page-agent-sdk-integrate/references/api.md +24 -24
- package/skills/page-agent-sdk-integrate/references/options.md +8 -8
- package/skills/page-agent-sdk-integrate/references/quickstart.md +14 -14
- package/skills/page-agent-sdk-integrate/references/use-cases.md +27 -27
- package/types/index.d.ts +46 -36
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: page-agent-sdk-integrate
|
|
3
|
-
description: Integrate the page-agent-sdk npm package into a web app so an AI agent can read/write structured page data (
|
|
3
|
+
description: Integrate the page-agent-sdk npm package into a web app so an AI agent can read/write structured page data (data slots) via schema-validated tools. Use when the user wants to add/embed the SDK, declare dataSlots + zod schemas, configure the LLM, mount the chat dialog, subscribe to events (onEvent / sdk.hook), run headless (ui:false) with a custom UI, or troubleshoot common integration issues (DeepSeek 400 tool_call_id, MCP injecting 0 tools, etc).
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Integrate page-agent-sdk
|
|
@@ -9,7 +9,7 @@ Help the user embed `page-agent-sdk` so an AI agent safely edits their page's st
|
|
|
9
9
|
|
|
10
10
|
## Core concept
|
|
11
11
|
|
|
12
|
-
The SDK is a **standardized JSON-operation agent**: the integrator declares writable `window` paths + zod schemas; the agent edits them via `
|
|
12
|
+
The SDK is a **standardized JSON-operation agent**: the integrator declares writable `window` paths + zod schemas; the agent edits them via `set_data_slot` / `edit_data_slot` (jsonPath patches), validated by schema, scoped to the registry, with snapshot rollback. "Editing JSON" becomes structured + validatable + rollbackable, NOT free-form LLM text.
|
|
13
13
|
|
|
14
14
|
## Workflow
|
|
15
15
|
|
|
@@ -23,7 +23,7 @@ The SDK is a **standardized JSON-operation agent**: the integrator declares writ
|
|
|
23
23
|
|
|
24
24
|
See `demo/plain.html` for a framework-agnostic importmap + esm.sh example.
|
|
25
25
|
|
|
26
|
-
### 2. Declare
|
|
26
|
+
### 2. Declare dataSlots + schemas (the key step)
|
|
27
27
|
|
|
28
28
|
Put the page data on `window` (e.g. `window.app = { title, theme }`), then declare each writable path with a zod schema. The agent can ONLY touch declared paths; `set`/`edit` are schema-validated (invalid → structured error, no write).
|
|
29
29
|
|
|
@@ -39,7 +39,7 @@ createChatSdk({
|
|
|
39
39
|
container: '#root',
|
|
40
40
|
llm: { apiKey, baseUrl: 'https://api.deepseek.com/v1', model: 'deepseek-chat' },
|
|
41
41
|
systemPrompt: 'You are a page assistant; read/write window.app via tools.',
|
|
42
|
-
|
|
42
|
+
dataSlots: [
|
|
43
43
|
{ path: 'app.title', description: '页面标题', schema: z.string() },
|
|
44
44
|
{ path: 'app.theme', description: '主题', schema: z.enum(['light','dark']) },
|
|
45
45
|
{ path: 'app.items', description: '列表项数组', schema: z.array(z.object({ name: z.string(), price: z.number() })) },
|
|
@@ -47,7 +47,7 @@ createChatSdk({
|
|
|
47
47
|
}).mount()
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
For large JSON, prefer `
|
|
50
|
+
For large JSON, prefer `edit_data_slot` (jsonPath patch: set/remove/merge/append) over `set_data_slot` (whole value) — avoids re-sending the entire blob.
|
|
51
51
|
|
|
52
52
|
### 3. Configure the LLM
|
|
53
53
|
|
|
@@ -59,7 +59,7 @@ Two complementary ways to react to SDK changes from the host page:
|
|
|
59
59
|
|
|
60
60
|
```ts
|
|
61
61
|
const sdk = createChatSdk({
|
|
62
|
-
onEvent(e) { if (e.type === '
|
|
62
|
+
onEvent(e) { if (e.type === 'data_slot_change') renderUI() }, // constructor-time, single
|
|
63
63
|
// ...
|
|
64
64
|
}).mount()
|
|
65
65
|
|
|
@@ -68,7 +68,7 @@ const off = sdk.hook((e) => { if (e.type === 'tool_call') analytics.track(e.name
|
|
|
68
68
|
// off() to unsubscribe
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
-
Event types: `
|
|
71
|
+
Event types: `data_slot_change` / `message_update` / `tool_call` / `tool_result` / `text` / `round_start` / `done` / `error` (+ stream events in stream mode). `approval_request` is NOT forwarded (UI handles it).
|
|
72
72
|
|
|
73
73
|
### 5. Headless mode (custom UI, framework-agnostic)
|
|
74
74
|
|
|
@@ -76,23 +76,23 @@ Event types: `window_prop_change` / `message_update` / `tool_call` / `tool_resul
|
|
|
76
76
|
|
|
77
77
|
### 6. Capabilities & presets
|
|
78
78
|
|
|
79
|
-
- `capabilities: {
|
|
79
|
+
- `capabilities: { dataSlotOps:false, fetch:false, planning:false, skills:false, vfs:false, summarization:false, memory:false, subagent:false }` — turn off unused built-ins to save tokens/size. `verify` is the reverse (off by default; `capabilities.verify:true` enables write-back self-check).
|
|
80
80
|
- `presets.pageBuilder` / `researcher` / `minimal` — spread into `createChatSdk` for common scenarios.
|
|
81
81
|
|
|
82
82
|
## Common use cases (match the user's scenario, then read [references/use-cases.md](references/use-cases.md) for full code)
|
|
83
83
|
|
|
84
84
|
| Scenario | Key setup |
|
|
85
85
|
|---|---|
|
|
86
|
-
| **Low-code page builder** | `
|
|
87
|
-
| **Form designer** | `
|
|
88
|
-
| **CMS batch ops** | `
|
|
86
|
+
| **Low-code page builder** | `dataSlots` = component tree; `edit_data_slot` jsonPath patches; `onEvent` → canvas refresh; `checkpoint` + `approval` |
|
|
87
|
+
| **Form designer** | `dataSlots` = field definitions with enum/required schemas; schema validation prevents malformed forms |
|
|
88
|
+
| **CMS batch ops** | `eval_script` for bulk loops; `search_data_slot` to filter; `edit_data_slot` for targeted edits |
|
|
89
89
|
| **Ops config console** | `approval:{tools:[set,edit]}` human-confirm; `capabilities.verify:true` write-back read; `checkpoint` |
|
|
90
|
-
| **AI-native assistant** | `capabilities:{
|
|
91
|
-
| **Research agent** | `capabilities:{
|
|
92
|
-
| **Headless / server-side** | `ui:false` + `storage:'memory'` + `capabilities:{
|
|
90
|
+
| **AI-native assistant** | `capabilities:{dataSlotOps:false,fetch:false}` + custom `tools` (your product API) |
|
|
91
|
+
| **Research agent** | `capabilities:{dataSlotOps:false}`; `subagent:{allowedTools:['fetch_document']}`; `contextPreset:'conservative'` |
|
|
92
|
+
| **Headless / server-side** | `ui:false` + `storage:'memory'` + `capabilities:{dataSlotOps:false,fetch:false}`; drive via `sdk.send` |
|
|
93
93
|
| **Multi-agent on one page** | same `id` + `shareContext:true` → multiple dialogs share one `AgentCore` |
|
|
94
94
|
| **MCP integration** | `mcp:[{transport,url}]` remote tool servers; `@modelcontextprotocol/sdk` optional peerDep |
|
|
95
|
-
| **Lazy-loaded components (dynamic schemas)** | `sdk.
|
|
95
|
+
| **Lazy-loaded components (dynamic schemas)** | `sdk.addDataSlot(spec)` on component mount / `removeDataSlot` on unmount; tools pick up new registrations immediately, no rebuild. See [references/advanced.md §0](references/advanced.md) |
|
|
96
96
|
|
|
97
97
|
When the user describes a scenario, map it to the row above and load `references/use-cases.md` for the matching numbered case (1→10) with copy-paste code. For dynamic/lazy-loaded component schemas, custom tools/skills/subagents/MCP, load [references/advanced.md](references/advanced.md).
|
|
98
98
|
|
|
@@ -102,9 +102,9 @@ Detailed docs live in this skill's `references/` folder — load the one matchin
|
|
|
102
102
|
|
|
103
103
|
- **[references/quickstart.md](references/quickstart.md)** — progressive setup from 5-line CDN to full-featured (Stages 0→6). Read when the user wants a step-by-step "from simple to complete" walkthrough.
|
|
104
104
|
- **[references/options.md](references/options.md)** — every `createChatSdk` option: type, default, purpose & when to use. Read when the user asks "what does option X do" or needs to tune behavior.
|
|
105
|
-
- **[references/api.md](references/api.md)** — instance methods (`mount`/`send`/`stream`/`inspect`/`switchSession`/`hook`/checkpoints), `defineTool`/`defineSkill`/`presets`, built-in
|
|
105
|
+
- **[references/api.md](references/api.md)** — instance methods (`mount`/`send`/`stream`/`inspect`/`switchSession`/`hook`/checkpoints), `defineTool`/`defineSkill`/`presets`, built-in data slot tools, and the full `SdkEvent` type table. Read when the user asks about APIs, tools, or events.
|
|
106
106
|
- **[references/use-cases.md](references/use-cases.md)** — 10 end-to-end scenarios (low-code builder / form designer / CMS batch / ops console / AI-native / research / server-side / multi-agent / MCP / lazy-loaded dynamic components). Read when the user wants a concrete pattern for their use case.
|
|
107
|
-
- **[references/advanced.md](references/advanced.md)** — detailed examples for the extensibility surfaces: **dynamic
|
|
107
|
+
- **[references/advanced.md](references/advanced.md)** — detailed examples for the extensibility surfaces: **dynamic dataSlots (`sdk.addDataSlot`/`removeDataSlot` for lazy-loaded components)**, custom `defineTool` (with error handling + coexisting with dataSlotOps), `defineSkill` (inline content + remote doc), subagents (ad-hoc `spawn_agent`/`spawn_agents` + pre-declared `subagents` → `use_<id>`), MCP (http/sse/websocket + auth + dev gotcha). Read when the user asks "how to add custom tools / skills / subagents / MCP" or "lazy-load components with different schemas".
|
|
108
108
|
|
|
109
109
|
Project-level docs (in the repo, not bundled in this skill):
|
|
110
110
|
- `doc/usage-guide.md` (zh) / `doc/usage-guide.en.md` — full options reference
|
|
@@ -118,4 +118,4 @@ Project-level docs (in the repo, not bundled in this skill):
|
|
|
118
118
|
- **ChatOpenAI params**: use `apiKey` (not `openAIApiKey`), `model` (not `modelName`); `baseUrl` goes via `configuration.baseURL`.
|
|
119
119
|
- **MCP injects 0 tools on first cold visit**: `vite.config.ts` `optimizeDeps.include` pre-declares the SDK sub-paths; if you fork the config, keep those entries or the first MCP page load injects nothing (reload fixes it).
|
|
120
120
|
- **`.env` `VITE_AI_SYSTEM_PROMPT` must be single-line** (dotenv doesn't support multi-line values).
|
|
121
|
-
- **Server-side (Node.js)**: works with `ui:false` + `storage:'memory'` + `capabilities:{
|
|
121
|
+
- **Server-side (Node.js)**: works with `ui:false` + `storage:'memory'` + `capabilities:{dataSlotOps:false,fetch:false}`; `mount()`/`unmount()` guard `window`/`document` access. Provide `globalThis.window` if you enable dataSlotOps in Node.
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
# Advanced examples — custom tools, skills, subagents, MCP, dynamic
|
|
1
|
+
# Advanced examples — custom tools, skills, subagents, MCP, dynamic dataSlots
|
|
2
2
|
|
|
3
3
|
Detailed, copy-paste examples for the extensibility surfaces. Read the section matching the user's need.
|
|
4
4
|
|
|
5
|
-
## 0. Dynamic
|
|
5
|
+
## 0. Dynamic dataSlots (lazy-loaded components) — `sdk.addDataSlot` / `removeDataSlot`
|
|
6
6
|
|
|
7
|
-
When components are lazy-loaded with **different schemas each**, don't declare all `
|
|
7
|
+
When components are lazy-loaded with **different schemas each**, don't declare all `dataSlots` upfront. Register them at runtime as components mount/unmount. The agent's data slot tools pick up new registrations immediately (no agent rebuild).
|
|
8
8
|
|
|
9
9
|
```ts
|
|
10
10
|
const sdk = createChatSdk({
|
|
11
11
|
container: '#chat', llm: { ... },
|
|
12
|
-
systemPrompt: '
|
|
13
|
-
|
|
12
|
+
systemPrompt: '你是页面助手,按组件类型操作数据槽.app.components.<id>。',
|
|
13
|
+
dataSlots: [
|
|
14
14
|
// statically-declared ones (always present)
|
|
15
15
|
{ path: 'app.config', description: '全局配置', schema: z.record(z.any()) },
|
|
16
16
|
],
|
|
@@ -18,40 +18,40 @@ const sdk = createChatSdk({
|
|
|
18
18
|
|
|
19
19
|
// 组件懒加载时动态注册其 schema(结构各异)
|
|
20
20
|
function onComponentMount(comp: { id: string; type: string; schema: z.ZodType }) {
|
|
21
|
-
sdk.
|
|
22
|
-
// 立即生效:AI 现在能 set/
|
|
21
|
+
sdk.addDataSlot({ path: `app.components.${comp.id}`, description: `${comp.type} 组件`, schema: comp.schema })
|
|
22
|
+
// 立即生效:AI 现在能 set/edit_data_slot 这个 path,按其 schema 校验
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
// 组件卸载时移除(快照栈一并清理)
|
|
26
26
|
function onComponentUnmount(id: string) {
|
|
27
|
-
sdk.
|
|
27
|
+
sdk.removeDataSlot(`app.components.${id}`)
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
// 查看当前所有注册项(反映动态增删)
|
|
31
|
-
const current:
|
|
31
|
+
const current: DataSlotSpec[] = sdk.listDataSlots()
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
Notes:
|
|
35
|
-
- `
|
|
36
|
-
- 动态注册的属性**不自动纳入 checkpoint 快照**(checkpoint 的
|
|
37
|
-
- `inspect().
|
|
38
|
-
- `capabilities.
|
|
35
|
+
- `addDataSlot` 覆盖同名 path 时保留旧快照栈;按新 schema 校验。
|
|
36
|
+
- 动态注册的属性**不自动纳入 checkpoint 快照**(checkpoint 的 slotPaths 在构造时固定);如需回滚动态组件,自行管理或重建。
|
|
37
|
+
- `inspect().dataSlots` 与 `verify`(默认 `createWriteBackCheck`)均反映动态注册的最新 schemas(verify 每次 check 实时取 `listDataSlots()`)。
|
|
38
|
+
- `capabilities.dataSlotOps:false` 时 `addDataSlot`/`removeDataSlot` 为 no-op(并 warn)。
|
|
39
39
|
|
|
40
|
-
**完整可运行示例**:`examples/dynamic-demo/`(dev 启动后访问 `/examples/dynamic-demo/`)—— 演示加载/卸载结构各异的组件(banner/card/stat/chart),挂载即 `
|
|
40
|
+
**完整可运行示例**:`examples/dynamic-demo/`(dev 启动后访问 `/examples/dynamic-demo/`)—— 演示加载/卸载结构各异的组件(banner/card/stat/chart),挂载即 `addDataSlot` 注册其 schema,AI 立即可按各自 schema 操作,卸载即 `removeDataSlot`;右侧实时显示 `sdk.listDataSlots()` 反映动态增删。
|
|
41
41
|
|
|
42
42
|
### 动态场景下「压缩后不丢信息」的保障(内置,无需额外配置)
|
|
43
43
|
|
|
44
44
|
动态组件随时增删,长会话压缩后 LLM 可能基于过时记忆操作已卸载的组件、或不知道新组件已注册。SDK 内置两道保障:
|
|
45
45
|
|
|
46
|
-
- **A. 压缩时注入注册表快照**:`summarization` 中间件压缩 older 轮次时,自动把当前 `
|
|
47
|
-
- **C. preserveLastToolResults**:`contextOptions.preserveLastToolResults`(默认 `['
|
|
46
|
+
- **A. 压缩时注入注册表快照**:`summarization` 中间件压缩 older 轮次时,自动把当前 `listDataSlots()` 的 `path + description` 作为一段附进摘要 system 消息(不进压缩)。LLM 即便忘了历史 `describe`,每轮仍看得到「当前有哪些可操作 path」,不会再去操作已卸载的组件。`dataSlotOps` 关闭时返回空,无影响。
|
|
47
|
+
- **C. preserveLastToolResults**:`contextOptions.preserveLastToolResults`(默认 `['describe_data_slot','list_data_slots']`)指定这些工具的步骤 `result` 在跨轮摘要时额外保留摘要片段进 summaryMsg。即便 older 轮被摘要,关键字段说明仍在摘要里,LLM 不必反复 `describe`。设为 `[]` 关闭。
|
|
48
48
|
|
|
49
49
|
```ts
|
|
50
50
|
// 默认即开启 A + C;如需关闭或自定义:
|
|
51
51
|
createChatSdk({
|
|
52
52
|
contextOptions: {
|
|
53
53
|
preserveLastToolResults: [], // 关闭 C(不保留工具结果摘要)
|
|
54
|
-
//
|
|
54
|
+
// getRegisteredSlots 由 SDK 内部注入(来自 sdk.listDataSlots),无需手动传
|
|
55
55
|
},
|
|
56
56
|
// ...
|
|
57
57
|
})
|
|
@@ -71,7 +71,7 @@ createChatSdk({
|
|
|
71
71
|
|
|
72
72
|
## 1. Custom tools (`defineTool`)
|
|
73
73
|
|
|
74
|
-
Custom tools extend the agent beyond built-in `
|
|
74
|
+
Custom tools extend the agent beyond built-in `dataSlotOps`/`fetch`. Use them to expose your product's API to the AI.
|
|
75
75
|
|
|
76
76
|
### Minimal
|
|
77
77
|
|
|
@@ -105,25 +105,25 @@ const updatePrice = defineTool({
|
|
|
105
105
|
})
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
### Coexisting with
|
|
108
|
+
### Coexisting with dataSlotOps
|
|
109
109
|
|
|
110
|
-
Mix custom tools with built-in
|
|
110
|
+
Mix custom tools with built-in data slot tools:
|
|
111
111
|
|
|
112
112
|
```ts
|
|
113
113
|
createChatSdk({
|
|
114
114
|
container: '#chat', llm: { ... },
|
|
115
|
-
|
|
116
|
-
tools: [lookupOrder, updatePrice], // custom + built-in
|
|
115
|
+
dataSlots: [{ path: 'app.config', description: '配置', schema: z.record(z.any()) }],
|
|
116
|
+
tools: [lookupOrder, updatePrice], // custom + built-in dataSlotOps together
|
|
117
117
|
}).mount()
|
|
118
118
|
```
|
|
119
119
|
|
|
120
|
-
### Pure custom-tool agent (no
|
|
120
|
+
### Pure custom-tool agent (no dataSlotOps)
|
|
121
121
|
|
|
122
122
|
```ts
|
|
123
123
|
createChatSdk({
|
|
124
124
|
container: '#chat', llm: { ... },
|
|
125
125
|
tools: [lookupOrder, updatePrice],
|
|
126
|
-
capabilities: {
|
|
126
|
+
capabilities: { dataSlotOps: false, fetch: false }, // drop built-ins
|
|
127
127
|
}).mount()
|
|
128
128
|
```
|
|
129
129
|
|
|
@@ -177,7 +177,7 @@ createChatSdk({
|
|
|
177
177
|
container: '#chat', llm: { ... },
|
|
178
178
|
systemPrompt: '多源对比时用 spawn_agents 并行委派。',
|
|
179
179
|
subagent: {
|
|
180
|
-
allowedTools: ['fetch_document', '
|
|
180
|
+
allowedTools: ['fetch_document', 'get_data_slot'], // read-only subset (no spawn → no recursion)
|
|
181
181
|
maxDepth: 1, // physical recursion cut (default 1)
|
|
182
182
|
maxParallel: 3, // max parallel subagents in spawn_agents
|
|
183
183
|
temperature: 0.2, // subagent temperature (default inherits main)
|
|
@@ -199,13 +199,13 @@ createChatSdk({
|
|
|
199
199
|
{
|
|
200
200
|
id: 'researcher',
|
|
201
201
|
description: '调研专家:搜集资料、对比方案(只读)',
|
|
202
|
-
tools: ['fetch_document', '
|
|
202
|
+
tools: ['fetch_document', 'get_data_slot'], // read-only
|
|
203
203
|
temperature: 0.2,
|
|
204
204
|
},
|
|
205
205
|
{
|
|
206
206
|
id: 'reviewer',
|
|
207
207
|
description: '审查专家:检查代码/配置的安全与性能问题',
|
|
208
|
-
tools: ['
|
|
208
|
+
tools: ['get_data_slot', 'search_data_slot'],
|
|
209
209
|
systemPrompt: '你是审查专家,只报告问题不改数据。',
|
|
210
210
|
temperature: 0.1,
|
|
211
211
|
},
|
|
@@ -270,13 +270,13 @@ createChatSdk({
|
|
|
270
270
|
container: '#chat',
|
|
271
271
|
llm: { apiKey, baseUrl, model },
|
|
272
272
|
systemPrompt: '...',
|
|
273
|
-
|
|
273
|
+
dataSlots: [{ path: 'app.data', description: '...', schema: z.record(z.any()) }],
|
|
274
274
|
tools: [lookupOrder, updatePrice], // custom tools
|
|
275
275
|
skills: [apiDesignSkill, brandSkill], // progressive skills
|
|
276
276
|
subagents: [{ id: 'researcher', description: '...', tools: ['fetch_document'] }], // pre-declared
|
|
277
277
|
mcp: [{ transport: 'http', url: '...' }], // external tools
|
|
278
278
|
capabilities: { verify: true }, // self-check
|
|
279
|
-
approval: { tools: ['
|
|
279
|
+
approval: { tools: ['set_data_slot'] }, // human confirm writes
|
|
280
280
|
checkpoint: true, // rollback
|
|
281
281
|
}).mount()
|
|
282
282
|
```
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# API reference — instance methods, tool/skill definition,
|
|
1
|
+
# API reference — instance methods, tool/skill definition, data slot tools, events
|
|
2
2
|
|
|
3
3
|
## ChatSdk instance (`createChatSdk(...)` return)
|
|
4
4
|
|
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
| `messages` | `AgentMessage[]` (reactive) | The conversation. Headless reads this to render. UI shares the same array (single source). |
|
|
10
10
|
| `send(message)` | `(msg: string) => Promise<string>` | Send a user message (invoke mode, no stream events). Returns final content. |
|
|
11
11
|
| `stream` | `(messages, onEvent, signal?) => Promise<string>` | Low-level stream. UI uses this internally; headless can call directly for streaming. |
|
|
12
|
-
| `inspect()` | `() => AgentInfo` | Inspect agent: tools/skills/
|
|
12
|
+
| `inspect()` | `() => AgentInfo` | Inspect agent: tools/skills/dataSlots/middleware/todos/mcp.servers (each tool's `source`: `builtin`/`mcp:<name>`/`user`). DebugDrawer uses this. |
|
|
13
13
|
| `switchSession(id?)` | `(id?: string) => Promise<string>` | Switch session context (load or create by id). Requires `storage` enabled. |
|
|
14
14
|
| `hook(handler)` | `(h: SdkEventHandler) => () => void` | Runtime event subscription (multi-listener, returns unsubscribe). Complements `onEvent`. |
|
|
15
|
-
| `
|
|
16
|
-
| `
|
|
17
|
-
| `
|
|
15
|
+
| `addDataSlot(spec)` | `(spec: DataSlotSpec) => void` | Runtime register/override a data slot (lazy-loaded components). Takes effect immediately, no rebuild. Needs `dataSlotOps` enabled. |
|
|
16
|
+
| `removeDataSlot(path)` | `(path: string) => boolean` | Remove a registered data slot (component unmount); returns whether it existed. Clears its snapshot stack. |
|
|
17
|
+
| `listDataSlots()` | `() => DataSlotSpec[]` | List currently-registered data slots (reflects dynamic add/remove). |
|
|
18
18
|
| `restoreLastCheckpoint()` | `() => boolean` | Restore last good checkpoint (needs `checkpoint` enabled). |
|
|
19
19
|
| `listCheckpoints()` | `() => CheckpointMeta[]` | List available checkpoints. |
|
|
20
20
|
|
|
@@ -73,36 +73,36 @@ createChatSdk({
|
|
|
73
73
|
}).mount()
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
-
`reliableWriteRules` — standardized "reliable write rules": read before write (`
|
|
76
|
+
`reliableWriteRules` — standardized "reliable write rules": read before write (`get_data_slot`), list in dynamic scenarios, fields per `describe_data_slot`, retry on schema-validation errors, prefer `edit_data_slot` incremental patches. Recommended for any scenario involving window writes.
|
|
77
77
|
|
|
78
|
-
## Built-in
|
|
78
|
+
## Built-in data slot tools (auto-injected when `capabilities.dataSlotOps`)
|
|
79
79
|
|
|
80
80
|
| Tool | Purpose |
|
|
81
81
|
|---|---|
|
|
82
|
-
| `
|
|
83
|
-
| `
|
|
84
|
-
| `
|
|
85
|
-
| `
|
|
86
|
-
| `
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
90
|
-
| `
|
|
91
|
-
| `
|
|
92
|
-
| `
|
|
93
|
-
| `
|
|
94
|
-
|
|
95
|
-
**Key rule**: `set`/`edit`/`delete` only affect **declared** `
|
|
82
|
+
| `list_data_slots` | List declared paths + descriptions |
|
|
83
|
+
| `describe_data_slot` | Show a path's schema |
|
|
84
|
+
| `get_data_slot` | Read a path (or ancestor/descendant sub-paths of registered props) |
|
|
85
|
+
| `get_slot_paths` | Batch-read multiple paths |
|
|
86
|
+
| `set_data_slot` | Write a whole path (schema-validated, scoped to registry) |
|
|
87
|
+
| `edit_data_slot` | Patch by `jsonPath` (set/remove/merge/append) — avoids re-sending large JSON |
|
|
88
|
+
| `delete_data_slot` | Delete a path |
|
|
89
|
+
| `snapshot_data_slot` | Manual snapshot |
|
|
90
|
+
| `list_data_snapshots` | List snapshots |
|
|
91
|
+
| `restore_data_snapshot` | Restore (no id = most recent) |
|
|
92
|
+
| `query_data_slot` / `search_data_slot` | JSONPath query / full-text search |
|
|
93
|
+
| `eval_script` | Sandboxed script on data (for batch ops) |
|
|
94
|
+
|
|
95
|
+
**Key rule**: `set`/`edit`/`delete` only affect **declared** `dataSlots` paths. Invalid schema → structured error, no write. `edit` writes in-place (preserves Vue reactive refs).
|
|
96
96
|
|
|
97
97
|
### jsonPath edit operations
|
|
98
98
|
|
|
99
|
-
`
|
|
99
|
+
`edit_data_slot({ path, jsonPath, op, value })`:
|
|
100
100
|
- `set` — set a sub-path
|
|
101
101
|
- `remove` — remove a sub-path / array element
|
|
102
102
|
- `merge` — shallow-merge an object
|
|
103
103
|
- `append` — append to an array
|
|
104
104
|
|
|
105
|
-
Example: `
|
|
105
|
+
Example: `edit_data_slot({ path: 'app.items', jsonPath: '0.price', op: 'set', value: 9.9 })` — precise local edit, no full re-send.
|
|
106
106
|
|
|
107
107
|
## Built-in fetch tools (`capabilities.fetch`)
|
|
108
108
|
|
|
@@ -119,7 +119,7 @@ Example: `edit_window_prop({ path: 'app.items', jsonPath: '0.price', op: 'set',
|
|
|
119
119
|
| `tool_result` | `name, result, status` | Tool returns (`status`: `done`/`error`) |
|
|
120
120
|
| `subagent` | `taskId, label, kind, name, args?, result?, status?` | Subagent tool progress (forwarded to UI, NOT into main LLM context) |
|
|
121
121
|
| `done` | `content` | Agent round completes |
|
|
122
|
-
| `
|
|
122
|
+
| `data_slot_change` | `path, operation, value?` | A data slot was written (`operation`: `set`/`edit`/`delete`/`restore`) |
|
|
123
123
|
| `message_update` | `count` | The `messages` array changed |
|
|
124
124
|
| `error` | `message` | An error occurred (abort excluded) |
|
|
125
125
|
|
|
@@ -19,15 +19,15 @@ Full reference for `createChatSdk(options)`. Grouped by purpose. Required: `llm`
|
|
|
19
19
|
| `ui` | `boolean \| 'default'` | `true` | `false` = headless (no built-in dialog; you build UI from `sdk.messages` + `sdk.send`). `'default'` = built-in `ChatDialog`. |
|
|
20
20
|
| `streaming` | `boolean` | `true` | Stream tokens live. `false` = wait for full reply. Headless `sdk.send` always uses invoke (no stream events, but window/message/error still fire). |
|
|
21
21
|
|
|
22
|
-
##
|
|
22
|
+
## data slot operation (the core)
|
|
23
23
|
|
|
24
24
|
| Option | Type | Default | Purpose / when |
|
|
25
25
|
|---|---|---|---|
|
|
26
|
-
| `
|
|
27
|
-
| `maxSnapshots` | `number` | 20 | Per-path snapshot stack depth for `
|
|
26
|
+
| `dataSlots` | `DataSlotSpec[]` | `[]` | Declare writable `window` paths + zod schemas. The agent can ONLY touch declared paths; `set`/`edit` are schema-validated. **This is the key integration step.** |
|
|
27
|
+
| `maxSnapshots` | `number` | 20 | Per-path snapshot stack depth for `restore_data_snapshot`. |
|
|
28
28
|
| `permissions` | `PermissionRule[]` | off | Scope whitelist (first-match-wins) for fine-grained per-path/tool rules. Default off (all declared paths writable). |
|
|
29
29
|
|
|
30
|
-
`
|
|
30
|
+
`DataSlotSpec = { path: string; description: string; schema?: z.ZodType }`. `description` is shown to the AI — write it clearly so the agent knows what each path means.
|
|
31
31
|
|
|
32
32
|
## Tools, skills, memory
|
|
33
33
|
|
|
@@ -44,8 +44,8 @@ Full reference for `createChatSdk(options)`. Grouped by purpose. Required: `llm`
|
|
|
44
44
|
|
|
45
45
|
| Flag | Off when... |
|
|
46
46
|
|---|---|
|
|
47
|
-
| `
|
|
48
|
-
| `fetch` | No web fetching needed. ⚠️ turning off `
|
|
47
|
+
| `dataSlotOps` | Pure research agent, no page edits (also drops data slot tools from subagents). |
|
|
48
|
+
| `fetch` | No web fetching needed. ⚠️ turning off `dataSlotOps` also strips subagent data slot tools. |
|
|
49
49
|
| `planning` | Don't want `write_todos` planning. |
|
|
50
50
|
| `skills` | Don't want progressive skill loading. |
|
|
51
51
|
| `vfs` | No in-memory workspace; ⚠️ large tool results then truncate instead of offloading. |
|
|
@@ -70,7 +70,7 @@ Full reference for `createChatSdk(options)`. Grouped by purpose. Required: `llm`
|
|
|
70
70
|
| Option | Type | Default | Purpose / when |
|
|
71
71
|
|---|---|---|---|
|
|
72
72
|
| `contextPreset` | `'auto'\|'conservative'\|'aggressive'` | `auto` | `conservative` = save cost; `aggressive` = save context. `contextOptions` fine-tunes further. |
|
|
73
|
-
| `contextOptions` | `object` | — | Detailed compression params (overrides preset). `false` disables compression. Key fields: `windowRounds`, `summaryThresholdRounds`, `contextWindow`, `summaryThresholdRatio`, `windowRatio`, `enableRecall`, `recallTopK`, `enableLLMSummary`, `preserveLastToolResults` (default `['
|
|
73
|
+
| `contextOptions` | `object` | — | Detailed compression params (overrides preset). `false` disables compression. Key fields: `windowRounds`, `summaryThresholdRounds`, `contextWindow`, `summaryThresholdRatio`, `windowRatio`, `enableRecall`, `recallTopK`, `enableLLMSummary`, `preserveLastToolResults` (default `['describe_data_slot','list_data_slots']` — keep these tools' result summaries in the compressed summary so field descriptions survive compression; set `[]` to disable). `getRegisteredSlots` is injected internally by the SDK (from `sdk.listDataSlots`) to embed a live registry snapshot in the summary — no need to set it manually. |
|
|
74
74
|
| `summaryLlm` | `BaseChatModel \| LLMConfig` | main `llm` | Use a cheaper/faster model for summarization. |
|
|
75
75
|
| `summaryTemperature` | `number` | 0.3 | Summary model temperature. |
|
|
76
76
|
| `summaryMaxTokens` | `number` | 1024 | Summary output cap. |
|
|
@@ -97,7 +97,7 @@ Full reference for `createChatSdk(options)`. Grouped by purpose. Required: `llm`
|
|
|
97
97
|
|
|
98
98
|
## Checkpoint (session rollback)
|
|
99
99
|
|
|
100
|
-
`checkpoint: true \| { maxCheckpoints?, auto? }` — per-round snapshot of (messages +
|
|
100
|
+
`checkpoint: true \| { maxCheckpoints?, auto? }` — per-round snapshot of (messages + data slots + vfs + todos). `restoreLastCheckpoint()` / LLM tool `restore_last_checkpoint` / UI button. Distinct from dataSlotOps per-path snapshots (checkpoint = whole-session rollback).
|
|
101
101
|
|
|
102
102
|
## Persistence (storage)
|
|
103
103
|
|
|
@@ -20,7 +20,7 @@ Drop into any HTML page. The built-in dialog mounts itself. (`systemPrompt` is o
|
|
|
20
20
|
container: '#root',
|
|
21
21
|
llm: { apiKey: 'sk-...', baseUrl: 'https://api.deepseek.com/v1', model: 'deepseek-chat' },
|
|
22
22
|
systemPrompt: 'You are a page assistant. Read/write window.app via tools.',
|
|
23
|
-
|
|
23
|
+
dataSlots: [
|
|
24
24
|
{ path: 'app.title', description: '标题', schema: ChatSdk.z.string() },
|
|
25
25
|
{ path: 'app.theme', description: '主题', schema: ChatSdk.z.enum(['light','dark']) },
|
|
26
26
|
],
|
|
@@ -28,7 +28,7 @@ Drop into any HTML page. The built-in dialog mounts itself. (`systemPrompt` is o
|
|
|
28
28
|
</script>
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
Talk to it: "change theme to dark" → AI calls `
|
|
31
|
+
Talk to it: "change theme to dark" → AI calls `set_data_slot` → `window.app.theme === 'dark'`.
|
|
32
32
|
|
|
33
33
|
## Stage 2 — npm + module project
|
|
34
34
|
|
|
@@ -46,7 +46,7 @@ const sdk = createChatSdk({
|
|
|
46
46
|
container: '#root',
|
|
47
47
|
llm: { apiKey: import.meta.env.VITE_AI_API_KEY, baseUrl: 'https://api.deepseek.com/v1', model: 'deepseek-chat' },
|
|
48
48
|
systemPrompt: 'You are a page assistant. Read/write window.app via tools.',
|
|
49
|
-
|
|
49
|
+
dataSlots: [
|
|
50
50
|
{ path: 'app.title', description: '标题', schema: z.string() },
|
|
51
51
|
{ path: 'app.theme', description: '主题', schema: z.enum(['light','dark']) },
|
|
52
52
|
{ path: 'app.items', description: '列表项', schema: z.array(z.object({ name: z.string(), price: z.number() })) },
|
|
@@ -61,10 +61,10 @@ Subscribe via `onEvent` (constructor) or `sdk.hook` (runtime, multi-listener, ca
|
|
|
61
61
|
```ts
|
|
62
62
|
const sdk = createChatSdk({
|
|
63
63
|
onEvent(e) {
|
|
64
|
-
if (e.type === '
|
|
64
|
+
if (e.type === 'data_slot_change') renderUI() // host page reactive refresh
|
|
65
65
|
if (e.type === 'error') console.error(e.message)
|
|
66
66
|
},
|
|
67
|
-
// ...llm,
|
|
67
|
+
// ...llm, dataSlots...
|
|
68
68
|
}).mount()
|
|
69
69
|
|
|
70
70
|
// runtime listener (e.g. analytics), cancellable
|
|
@@ -79,7 +79,7 @@ No built-in dialog; drive the reactive `messages` array yourself.
|
|
|
79
79
|
```ts
|
|
80
80
|
const sdk = createChatSdk({
|
|
81
81
|
ui: false, // headless
|
|
82
|
-
llm: { ... }, systemPrompt: '...',
|
|
82
|
+
llm: { ... }, systemPrompt: '...', dataSlots: [...],
|
|
83
83
|
}).mount()
|
|
84
84
|
|
|
85
85
|
// your own UI reads sdk.messages (reactive) and calls sdk.send
|
|
@@ -92,10 +92,10 @@ Reusable `ChatDialog` / `MessageContent` / `CodePreview` components + `useChat`
|
|
|
92
92
|
|
|
93
93
|
```ts
|
|
94
94
|
createChatSdk({
|
|
95
|
-
// ...llm,
|
|
95
|
+
// ...llm, dataSlots...
|
|
96
96
|
capabilities: { verify: true }, // write-back self-check before agent returns
|
|
97
97
|
verify: { maxAttempts: 2 }, // auto-correct on failure (default check = write-back read + schema)
|
|
98
|
-
approval: { tools: ['
|
|
98
|
+
approval: { tools: ['set_data_slot', 'edit_data_slot'] }, // human-confirm before writes
|
|
99
99
|
checkpoint: true, // session-level rollback on bad edits
|
|
100
100
|
maxParallelTools: 1, // serial tool calls (safe for stateful middleware)
|
|
101
101
|
contextPreset: 'conservative', // save cost on long sessions
|
|
@@ -108,14 +108,14 @@ createChatSdk({
|
|
|
108
108
|
createChatSdk({
|
|
109
109
|
id: 'my-page-agent', // STABLE id (multi-agent isolation); omit = random + warn
|
|
110
110
|
storage: 'indexed', // persist messages/vfs/todos/memory to IndexedDB
|
|
111
|
-
// ...llm,
|
|
111
|
+
// ...llm, dataSlots...
|
|
112
112
|
}).mount()
|
|
113
113
|
|
|
114
114
|
// later, switch session:
|
|
115
115
|
await sdk.switchSession('session-abc') // load or create
|
|
116
116
|
```
|
|
117
117
|
|
|
118
|
-
## Stage 7 — Dynamic
|
|
118
|
+
## Stage 7 — Dynamic dataSlots (lazy-loaded components)
|
|
119
119
|
|
|
120
120
|
Components loaded on demand with **different schemas each** — register at mount, unregister at unmount. No need to pre-declare every possible component at `createChatSdk`. The agent picks up new registrations immediately (no rebuild); `summarization` also embeds a live registry snapshot in compressed summaries so the agent won't act on stale memory.
|
|
121
121
|
|
|
@@ -123,13 +123,13 @@ Components loaded on demand with **different schemas each** — register at moun
|
|
|
123
123
|
const sdk = createChatSdk({
|
|
124
124
|
container: '#root', llm: { ... },
|
|
125
125
|
// only the static container is pre-declared; per-component paths are dynamic
|
|
126
|
-
|
|
126
|
+
dataSlots: [{ path: 'app.components', description: '动态组件容器(按 id 存)', schema: z.record(z.string(), z.any()) }],
|
|
127
127
|
}).mount()
|
|
128
128
|
|
|
129
129
|
// component mounts (lazy) → register its schema, immediately operative
|
|
130
130
|
function mountComp(comp: { id: string; type: 'banner' | 'card' | 'stat' | 'chart' }) {
|
|
131
131
|
window.app.components[comp.id] = reactive(comp)
|
|
132
|
-
sdk.
|
|
132
|
+
sdk.addDataSlot({
|
|
133
133
|
path: `app.components.${comp.id}`,
|
|
134
134
|
description: `${typeDescriptions[comp.type]}`, // ← give the LLM field-level detail (it can't see the zod schema)
|
|
135
135
|
schema: compSchemas[comp.type], // ← validation guardrail
|
|
@@ -138,9 +138,9 @@ function mountComp(comp: { id: string; type: 'banner' | 'card' | 'stat' | 'chart
|
|
|
138
138
|
// component unmounts → unregister (snapshot stack cleaned too)
|
|
139
139
|
function unmountComp(id: string) {
|
|
140
140
|
delete window.app.components[id]
|
|
141
|
-
sdk.
|
|
141
|
+
sdk.removeDataSlot(`app.components.${id}`)
|
|
142
142
|
}
|
|
143
|
-
sdk.
|
|
143
|
+
sdk.listDataSlots() // live registry (reflects dynamic add/remove)
|
|
144
144
|
```
|
|
145
145
|
|
|
146
146
|
> Key points: `description` is the LLM's only source of field structure (write it in detail); `schema` is the validation guardrail (the LLM never sees it). Write operations return the current operable path list; long-session compression keeps a live registry snapshot + preserved `describe`/`list` results so the agent never loses track of dynamic components.
|