page-agent-sdk 1.1.2 → 1.1.4
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 +4 -0
- package/README.zh-CN.md +4 -0
- package/package.json +1 -1
- package/skills/page-agent-sdk-integrate/SKILL.md +26 -1
- package/skills/page-agent-sdk-integrate/references/advanced.md +215 -0
- package/skills/page-agent-sdk-integrate/references/api.md +119 -0
- package/skills/page-agent-sdk-integrate/references/options.md +124 -0
- package/skills/page-agent-sdk-integrate/references/quickstart.md +122 -0
- package/skills/page-agent-sdk-integrate/references/use-cases.md +200 -0
package/README.md
CHANGED
|
@@ -40,9 +40,13 @@ At its core, it gives the AI a **standardized, safe JSON-operation channel**. AI
|
|
|
40
40
|
| 📰 **CMS ops** | "Prefix these products with 'Limited', mark under ¥100 red" | JSONPath filter + sandbox script batch edit |
|
|
41
41
|
| 🖥 **Ops console** | "Raise A's threshold to 30%, turn off switch B" | Whitelist + human-confirm to edit config, read-back verify |
|
|
42
42
|
| 🤖 **AI-native assistant** | "Change this chart's legend to bars" | Conversational ops on product data, no UI needed |
|
|
43
|
+
| 🔬 **Research agent** | "Compare 3 solutions and recommend one" | Parallel subagents investigate each, return only conclusions |
|
|
44
|
+
| 🧩 **Headless / server-side** | "Run the agent in Node.js" | `ui:false` + `storage:'memory'`, drive via `sdk.send` |
|
|
43
45
|
|
|
44
46
|
> `examples/nested-demo` is a full low-code example: nested block tree + human confirm + one-click rollback.
|
|
45
47
|
|
|
48
|
+
**Full end-to-end scenarios with copy-paste code** (9 cases: low-code builder / form designer / CMS batch / ops console / AI-native / research / server-side / multi-agent / MCP) live in the bundled Agent Skill at `skills/page-agent-sdk-integrate/references/use-cases.md` (also shipped in the npm package). See [Skills for AI tools](#skills-for-ai-tools-for-integrators) below to install the skill.
|
|
49
|
+
|
|
46
50
|
## 30-second quickstart
|
|
47
51
|
|
|
48
52
|
```bash
|
package/README.zh-CN.md
CHANGED
|
@@ -40,9 +40,13 @@
|
|
|
40
40
|
| 📰 **CMS 运营** | 「这批商品标题加『限时』前缀、低于 100 元的标红」 | JSONPath 筛选 + 沙箱脚本批量改 |
|
|
41
41
|
| 🖥 **运维配置台** | 「A 实验阈值调到 30%、关掉 B 开关」 | 白名单 + 人工确认改配置,写后读回校验 |
|
|
42
42
|
| 🤖 **AI 原生助手** | 「把这张看板的图例改成柱状」 | 对话操作产品自有数据,免做 UI |
|
|
43
|
+
| 🔬 **调研 agent** | 「对比 3 个方案,推荐哪个」 | 并行子 agent 各调研一个,只回结论 |
|
|
44
|
+
| 🧩 **Headless / 服务端** | 「在 Node.js 里跑 agent」 | `ui:false` + `storage:'memory'`,用 `sdk.send` 驱动 |
|
|
43
45
|
|
|
44
46
|
> 仓库 `examples/nested-demo` 即低代码场景完整示例:嵌套区块树 + 人工确认 + 一键回退。
|
|
45
47
|
|
|
48
|
+
**完整端到端场景(含可复制代码,共 9 例:低代码搭建 / 表单设计器 / CMS 批量 / 运维配置台 / AI 原生 / 调研 / 服务端 / 多 agent / MCP)** 见随包附带的 Agent Skill:`skills/page-agent-sdk-integrate/references/use-cases.md`(npm 包内同样包含)。安装 skill 见下文[给 AI 工具使用者的 Skills](#给-ai-工具使用者的-skills集成方安装)。
|
|
49
|
+
|
|
46
50
|
## 30 秒上手
|
|
47
51
|
|
|
48
52
|
```bash
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "page-agent-sdk",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "框架无关的页面内 Agent JS SDK —— 以对话框形态挂载到任意网页,通过自定义 tool 读写宿主 window 属性(GET 抓文档),具备 planning/skills/虚拟工作区/快照回退/context 管理能力。Vue 打包进库,使用者无需安装 Vue。",
|
|
6
6
|
"main": "./dist/page-agent-sdk.umd.cjs",
|
|
@@ -77,9 +77,34 @@ Event types: `window_prop_change` / `message_update` / `tool_call` / `tool_resul
|
|
|
77
77
|
- `capabilities: { windowOps: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).
|
|
78
78
|
- `presets.pageBuilder` / `researcher` / `minimal` — spread into `createChatSdk` for common scenarios.
|
|
79
79
|
|
|
80
|
+
## Common use cases (match the user's scenario, then read [references/use-cases.md](references/use-cases.md) for full code)
|
|
81
|
+
|
|
82
|
+
| Scenario | Key setup |
|
|
83
|
+
|---|---|
|
|
84
|
+
| **Low-code page builder** | `windowProps` = component tree; `edit_window_prop` jsonPath patches; `onEvent` → canvas refresh; `checkpoint` + `approval` |
|
|
85
|
+
| **Form designer** | `windowProps` = field definitions with enum/required schemas; schema validation prevents malformed forms |
|
|
86
|
+
| **CMS batch ops** | `eval_window_script` for bulk loops; `search_window_prop` to filter; `edit_window_prop` for targeted edits |
|
|
87
|
+
| **Ops config console** | `approval:{tools:[set,edit]}` human-confirm; `capabilities.verify:true` write-back read; `checkpoint` |
|
|
88
|
+
| **AI-native assistant** | `capabilities:{windowOps:false,fetch:false}` + custom `tools` (your product API) |
|
|
89
|
+
| **Research agent** | `capabilities:{windowOps:false}`; `subagent:{allowedTools:['fetch_document']}`; `contextPreset:'conservative'` |
|
|
90
|
+
| **Headless / server-side** | `ui:false` + `storage:'memory'` + `capabilities:{windowOps:false,fetch:false}`; drive via `sdk.send` |
|
|
91
|
+
| **Multi-agent on one page** | same `id` + `shareContext:true` → multiple dialogs share one `AgentCore` |
|
|
92
|
+
| **MCP integration** | `mcp:[{transport,url}]` remote tool servers; `@modelcontextprotocol/sdk` optional peerDep |
|
|
93
|
+
|
|
94
|
+
When the user describes a scenario, map it to the row above and load `references/use-cases.md` for the matching numbered case (1→9) with copy-paste code.
|
|
95
|
+
|
|
80
96
|
## References (read as needed)
|
|
81
97
|
|
|
82
|
-
|
|
98
|
+
Detailed docs live in this skill's `references/` folder — load the one matching the user's question:
|
|
99
|
+
|
|
100
|
+
- **[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.
|
|
101
|
+
- **[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.
|
|
102
|
+
- **[references/api.md](references/api.md)** — instance methods (`mount`/`send`/`stream`/`inspect`/`switchSession`/`hook`/checkpoints), `defineTool`/`defineSkill`/`presets`, built-in window tools, and the full `SdkEvent` type table. Read when the user asks about APIs, tools, or events.
|
|
103
|
+
- **[references/use-cases.md](references/use-cases.md)** — 9 end-to-end scenarios (low-code builder / form designer / CMS batch / ops console / AI-native / research / server-side / multi-agent / MCP). Read when the user wants a concrete pattern for their use case.
|
|
104
|
+
- **[references/advanced.md](references/advanced.md)** — detailed examples for the four extensibility surfaces: custom `defineTool` (with error handling + coexisting with windowOps), `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".
|
|
105
|
+
|
|
106
|
+
Project-level docs (in the repo, not bundled in this skill):
|
|
107
|
+
- `doc/usage-guide.md` (zh) / `doc/usage-guide.en.md` — full options reference
|
|
83
108
|
- `examples/<demo>/` — runnable demos (page-demo, nested-demo, subagent-demo, mcp-demo, planner-demo, toolsets-demo, human-confirm-demo)
|
|
84
109
|
- `demo/plain.html` — framework-agnostic CDN integration
|
|
85
110
|
- `CLAUDE.md` — internal dev guide (architecture, conventions)
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# Advanced examples — custom tools, skills, subagents, MCP
|
|
2
|
+
|
|
3
|
+
Detailed, copy-paste examples for the four extensibility surfaces. Read the section matching the user's need.
|
|
4
|
+
|
|
5
|
+
## 1. Custom tools (`defineTool`)
|
|
6
|
+
|
|
7
|
+
Custom tools extend the agent beyond built-in `windowOps`/`fetch`. Use them to expose your product's API to the AI.
|
|
8
|
+
|
|
9
|
+
### Minimal
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { createChatSdk, defineTool, z } from 'page-agent-sdk'
|
|
13
|
+
|
|
14
|
+
const lookupOrder = defineTool({
|
|
15
|
+
name: 'lookup_order',
|
|
16
|
+
description: '查询订单 by id',
|
|
17
|
+
schema: z.object({ orderId: z.string() }),
|
|
18
|
+
handler: async ({ orderId }) => JSON.stringify(await api.getOrder(orderId)),
|
|
19
|
+
})
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### With error handling
|
|
23
|
+
|
|
24
|
+
Return structured errors via `toolError` so the AI can react:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { defineTool, toolError, z } from 'page-agent-sdk'
|
|
28
|
+
|
|
29
|
+
const updatePrice = defineTool({
|
|
30
|
+
name: 'update_price',
|
|
31
|
+
description: '更新商品价格',
|
|
32
|
+
schema: z.object({ sku: z.string(), price: z.number().positive() }),
|
|
33
|
+
handler: async ({ sku, price }) => {
|
|
34
|
+
const ok = await api.setPrice(sku, price)
|
|
35
|
+
if (!ok) return toolError({ path: sku, code: 'NOT_FOUND', message: `SKU ${sku} 不存在` })
|
|
36
|
+
return `已更新 ${sku} 价格为 ${price}`
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Coexisting with windowOps
|
|
42
|
+
|
|
43
|
+
Mix custom tools with built-in window tools:
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
createChatSdk({
|
|
47
|
+
container: '#chat', llm: { ... },
|
|
48
|
+
windowProps: [{ path: 'app.config', description: '配置', schema: z.record(z.any()) }],
|
|
49
|
+
tools: [lookupOrder, updatePrice], // custom + built-in windowOps together
|
|
50
|
+
}).mount()
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Pure custom-tool agent (no windowOps)
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
createChatSdk({
|
|
57
|
+
container: '#chat', llm: { ... },
|
|
58
|
+
tools: [lookupOrder, updatePrice],
|
|
59
|
+
capabilities: { windowOps: false, fetch: false }, // drop built-ins
|
|
60
|
+
}).mount()
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## 2. Skills (`defineSkill`) — progressive disclosure
|
|
64
|
+
|
|
65
|
+
Skills are **loaded on demand** by the agent (not always in context) → saves tokens. The agent sees an index of `name`+`description`, calls `load_skill` to pull the full content when needed.
|
|
66
|
+
|
|
67
|
+
### Inline content skill
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { createChatSdk, defineSkill } from 'page-agent-sdk'
|
|
71
|
+
|
|
72
|
+
const apiDesignSkill = defineSkill({
|
|
73
|
+
name: 'api-design',
|
|
74
|
+
description: '本项目 REST API 设计规范(何时用:设计/评审新接口)',
|
|
75
|
+
getContent: () => `
|
|
76
|
+
- URL 用 kebab-case,统一 /v1 前缀
|
|
77
|
+
- 列表接口必须分页(page+pageSize)
|
|
78
|
+
- 错误返回 { code, message, data: null }
|
|
79
|
+
- 写操作记审计日志
|
|
80
|
+
`,
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
createChatSdk({ container: '#chat', llm: { ... }, skills: [apiDesignSkill] }).mount()
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Remote doc skill (auto-fetched + cached to vfs)
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
const brandSkill = defineSkill({
|
|
90
|
+
name: 'brand-guide',
|
|
91
|
+
description: '品牌视觉规范(何时用:涉及 UI/文案/配色)',
|
|
92
|
+
doc: 'https://my-wiki/brand.md', // SDK fetches + caches to vfs; large docs stay out of context
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
createChatSdk({ container: '#chat', llm: { ... }, skills: [brandSkill] }).mount()
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
> `SkillSpec = { name, description, doc?, getContent? }`. `doc` (http(s):// or `vfs://path`) takes precedence over `getContent`. Write `description` as "what it is + when to use" so the agent knows when to load it.
|
|
99
|
+
|
|
100
|
+
## 3. Subagents — ad-hoc spawn vs pre-declared
|
|
101
|
+
|
|
102
|
+
Subagents run isolated sub-tasks; **only their final conclusion** returns to the main context (saves tokens). Two flavors coexist.
|
|
103
|
+
|
|
104
|
+
### 3a. Ad-hoc `spawn_agent` / `spawn_agents` (default enabled)
|
|
105
|
+
|
|
106
|
+
The main agent decides when to delegate via `spawn_agent` (one) / `spawn_agents` (parallel). Configure the subagent tool subset:
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
createChatSdk({
|
|
110
|
+
container: '#chat', llm: { ... },
|
|
111
|
+
systemPrompt: '多源对比时用 spawn_agents 并行委派。',
|
|
112
|
+
subagent: {
|
|
113
|
+
allowedTools: ['fetch_document', 'get_window_prop'], // read-only subset (no spawn → no recursion)
|
|
114
|
+
maxDepth: 1, // physical recursion cut (default 1)
|
|
115
|
+
maxParallel: 3, // max parallel subagents in spawn_agents
|
|
116
|
+
temperature: 0.2, // subagent temperature (default inherits main)
|
|
117
|
+
},
|
|
118
|
+
}).mount()
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
User: "对比 A/B/C 三个方案" → main agent calls `spawn_agents` with 3 tasks → 3 subagents research in parallel → only conclusions return.
|
|
122
|
+
|
|
123
|
+
### 3b. Pre-declared named subagents (`subagents`) — Claude-Code style
|
|
124
|
+
|
|
125
|
+
Declare fixed roles; each auto-generates a `use_<id>({ task })` delegation tool. The main agent sees the tool description and knows who to delegate to:
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
createChatSdk({
|
|
129
|
+
container: '#chat', llm: { ... },
|
|
130
|
+
systemPrompt: '复杂任务委派给专家子 agent。',
|
|
131
|
+
subagents: [
|
|
132
|
+
{
|
|
133
|
+
id: 'researcher',
|
|
134
|
+
description: '调研专家:搜集资料、对比方案(只读)',
|
|
135
|
+
tools: ['fetch_document', 'get_window_prop'], // read-only
|
|
136
|
+
temperature: 0.2,
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
id: 'reviewer',
|
|
140
|
+
description: '审查专家:检查代码/配置的安全与性能问题',
|
|
141
|
+
tools: ['get_window_prop', 'search_window_prop'],
|
|
142
|
+
systemPrompt: '你是审查专家,只报告问题不改数据。',
|
|
143
|
+
temperature: 0.1,
|
|
144
|
+
},
|
|
145
|
+
],
|
|
146
|
+
}).mount()
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Now the main agent has `use_researcher({ task })` and `use_reviewer({ task })` tools. Each subagent inherits main config where omitted (`llm`, `maxTokens`, `skills`...).
|
|
150
|
+
|
|
151
|
+
> Pre-declared = fixed roles (research/review); ad-hoc `spawn` = temporary free delegation. Both can coexist. `maxDepth` (default 1) physically cuts recursion: at depth+1 ≥ maxDepth, subagents get no spawn tools.
|
|
152
|
+
|
|
153
|
+
## 4. MCP (external tool servers)
|
|
154
|
+
|
|
155
|
+
Connect remote MCP servers; their tools auto-inject into the agent. `Promise.allSettled` → one server down doesn't break others.
|
|
156
|
+
|
|
157
|
+
### HTTP (StreamableHTTP) — recommended
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
createChatSdk({
|
|
161
|
+
container: '#chat', llm: { ... },
|
|
162
|
+
mcp: [
|
|
163
|
+
{ transport: 'http', url: 'https://my-mcp-server/mcp', name: 'my-tools' },
|
|
164
|
+
],
|
|
165
|
+
}).mount()
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### SSE / WebSocket
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
mcp: [
|
|
172
|
+
{ transport: 'sse', url: 'https://another/sse' },
|
|
173
|
+
{ transport: 'websocket', url: 'wss://ws-server/mcp' },
|
|
174
|
+
]
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### With request init (auth headers)
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
mcp: [
|
|
181
|
+
{
|
|
182
|
+
transport: 'http', url: 'https://my-mcp/mcp',
|
|
183
|
+
requestInit: { headers: { Authorization: `Bearer ${token}` } },
|
|
184
|
+
},
|
|
185
|
+
]
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Notes
|
|
189
|
+
|
|
190
|
+
- `@modelcontextprotocol/sdk` is an **optional peerDep** — install it only if you use `mcp`. It's dynamically imported (zero cost when unused).
|
|
191
|
+
- Browser supports **only remote transports** (http/sse/websocket), not stdio.
|
|
192
|
+
- MCP `inputSchema` (JSON Schema) is passed directly to LangChain `tool()` — no conversion.
|
|
193
|
+
- `inspect().mcp.servers` lists connected servers; each tool's `source` shows `mcp:<name>`.
|
|
194
|
+
|
|
195
|
+
### Dev gotcha
|
|
196
|
+
|
|
197
|
+
If you fork `vite.config.ts`, keep `optimizeDeps.include` pre-declaring the SDK sub-paths (`/client`, `/client/streamableHttp.js`, `/client/sse.js`, `/client/websocket.js`). Otherwise the **first cold visit** to an MCP page injects 0 tools (reload fixes it). The default config already has these.
|
|
198
|
+
|
|
199
|
+
## 5. Combining everything
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
createChatSdk({
|
|
203
|
+
container: '#chat',
|
|
204
|
+
llm: { apiKey, baseUrl, model },
|
|
205
|
+
systemPrompt: '...',
|
|
206
|
+
windowProps: [{ path: 'app.data', description: '...', schema: z.record(z.any()) }],
|
|
207
|
+
tools: [lookupOrder, updatePrice], // custom tools
|
|
208
|
+
skills: [apiDesignSkill, brandSkill], // progressive skills
|
|
209
|
+
subagents: [{ id: 'researcher', description: '...', tools: ['fetch_document'] }], // pre-declared
|
|
210
|
+
mcp: [{ transport: 'http', url: '...' }], // external tools
|
|
211
|
+
capabilities: { verify: true }, // self-check
|
|
212
|
+
approval: { tools: ['set_window_prop'] }, // human confirm writes
|
|
213
|
+
checkpoint: true, // rollback
|
|
214
|
+
}).mount()
|
|
215
|
+
```
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# API reference — instance methods, tool/skill definition, window tools, events
|
|
2
|
+
|
|
3
|
+
## ChatSdk instance (`createChatSdk(...)` return)
|
|
4
|
+
|
|
5
|
+
| Method / field | Signature | Purpose |
|
|
6
|
+
|---|---|---|
|
|
7
|
+
| `mount()` | `() => Promise<void>` | Initialize & render. Await before `send` in headless. |
|
|
8
|
+
| `unmount()` | `() => void` | Tear down UI, listeners, flush storage. |
|
|
9
|
+
| `messages` | `AgentMessage[]` (reactive) | The conversation. Headless reads this to render. UI shares the same array (single source). |
|
|
10
|
+
| `send(message)` | `(msg: string) => Promise<string>` | Send a user message (invoke mode, no stream events). Returns final content. |
|
|
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/windowProps/middleware/todos/mcp.servers (each tool's `source`: `builtin`/`mcp:<name>`/`user`). DebugDrawer uses this. |
|
|
13
|
+
| `switchSession(id?)` | `(id?: string) => Promise<string>` | Switch session context (load or create by id). Requires `storage` enabled. |
|
|
14
|
+
| `hook(handler)` | `(h: SdkEventHandler) => () => void` | Runtime event subscription (multi-listener, returns unsubscribe). Complements `onEvent`. |
|
|
15
|
+
| `restoreLastCheckpoint()` | `() => boolean` | Restore last good checkpoint (needs `checkpoint` enabled). |
|
|
16
|
+
| `listCheckpoints()` | `() => CheckpointMeta[]` | List available checkpoints. |
|
|
17
|
+
|
|
18
|
+
## defineTool (custom tools)
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { defineTool, z } from 'page-agent-sdk'
|
|
22
|
+
|
|
23
|
+
const addTool = defineTool({
|
|
24
|
+
name: 'add',
|
|
25
|
+
description: 'Add two numbers',
|
|
26
|
+
schema: z.object({ a: z.number(), b: z.number() }),
|
|
27
|
+
handler: async ({ a, b }) => `sum: ${a + b}`,
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
createChatSdk({ tools: [addTool], /* llm, ... */ }).mount()
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`handler` receives validated args; return a string (or structured result stringified). Errors via `toolError({ path, code, message })`.
|
|
34
|
+
|
|
35
|
+
## defineSkill (progressive disclosure)
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { defineSkill } from 'page-agent-sdk'
|
|
39
|
+
|
|
40
|
+
const apiSkill = defineSkill({
|
|
41
|
+
name: 'api-design',
|
|
42
|
+
description: 'REST API design conventions for this project (load when designing/reviewing APIs)',
|
|
43
|
+
getContent: () => 'Use kebab-case URLs; version under /v1; ...', // or `doc: 'https://...'` for remote
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
createChatSdk({ skills: [apiSkill], /* ... */ }).mount()
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Skills are loaded on demand by the agent (not always in context) — saves tokens.
|
|
50
|
+
|
|
51
|
+
## presets (scenario bundles)
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { createChatSdk, presets } from 'page-agent-sdk'
|
|
55
|
+
|
|
56
|
+
createChatSdk({ ...presets.pageBuilder, llm, container }).mount()
|
|
57
|
+
// or presets.researcher / presets.minimal
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Spread into options for common scenarios.
|
|
61
|
+
|
|
62
|
+
## Built-in window tools (auto-injected when `capabilities.windowOps`)
|
|
63
|
+
|
|
64
|
+
| Tool | Purpose |
|
|
65
|
+
|---|---|
|
|
66
|
+
| `list_window_props` | List declared paths + descriptions |
|
|
67
|
+
| `describe_window_prop` | Show a path's schema |
|
|
68
|
+
| `get_window_prop` | Read a path (or ancestor/descendant sub-paths of registered props) |
|
|
69
|
+
| `get_window_paths` | Batch-read multiple paths |
|
|
70
|
+
| `set_window_prop` | Write a whole path (schema-validated, scoped to registry) |
|
|
71
|
+
| `edit_window_prop` | Patch by `jsonPath` (set/remove/merge/append) — avoids re-sending large JSON |
|
|
72
|
+
| `delete_window_prop` | Delete a path |
|
|
73
|
+
| `snapshot_window_prop` | Manual snapshot |
|
|
74
|
+
| `list_window_snapshots` | List snapshots |
|
|
75
|
+
| `restore_window_snapshot` | Restore (no id = most recent) |
|
|
76
|
+
| `query_window_prop` / `search_window_prop` | JSONPath query / full-text search |
|
|
77
|
+
| `eval_window_script` | Sandboxed script on data (for batch ops) |
|
|
78
|
+
|
|
79
|
+
**Key rule**: `set`/`edit`/`delete` only affect **declared** `windowProps` paths. Invalid schema → structured error, no write. `edit` writes in-place (preserves Vue reactive refs).
|
|
80
|
+
|
|
81
|
+
### jsonPath edit operations
|
|
82
|
+
|
|
83
|
+
`edit_window_prop({ path, jsonPath, op, value })`:
|
|
84
|
+
- `set` — set a sub-path
|
|
85
|
+
- `remove` — remove a sub-path / array element
|
|
86
|
+
- `merge` — shallow-merge an object
|
|
87
|
+
- `append` — append to an array
|
|
88
|
+
|
|
89
|
+
Example: `edit_window_prop({ path: 'app.items', jsonPath: '0.price', op: 'set', value: 9.9 })` — precise local edit, no full re-send.
|
|
90
|
+
|
|
91
|
+
## Built-in fetch tools (`capabilities.fetch`)
|
|
92
|
+
|
|
93
|
+
`fetch_document` — GET a URL, return cleaned text (HTML→markdown, truncated, offloaded to vfs if large).
|
|
94
|
+
|
|
95
|
+
## SdkEvent types (for `onEvent` / `sdk.hook`)
|
|
96
|
+
|
|
97
|
+
| `type` | Payload | When |
|
|
98
|
+
|---|---|---|
|
|
99
|
+
| `round_start` | `round` | Each agent round begins |
|
|
100
|
+
| `reasoning` | `delta` | Reasoning token (models that emit it) |
|
|
101
|
+
| `text` | `delta` | Streamed text delta (stream mode only) |
|
|
102
|
+
| `tool_call` | `name, args` | A tool is invoked |
|
|
103
|
+
| `tool_result` | `name, result, status` | Tool returns (`status`: `done`/`error`) |
|
|
104
|
+
| `subagent` | `taskId, label, kind, name, args?, result?, status?` | Subagent tool progress (forwarded to UI, NOT into main LLM context) |
|
|
105
|
+
| `done` | `content` | Agent round completes |
|
|
106
|
+
| `window_prop_change` | `path, operation, value?` | A window prop was written (`operation`: `set`/`edit`/`delete`/`restore`) |
|
|
107
|
+
| `message_update` | `count` | The `messages` array changed |
|
|
108
|
+
| `error` | `message` | An error occurred (abort excluded) |
|
|
109
|
+
|
|
110
|
+
`approval_request` is **NOT** forwarded via `onEvent`/`hook` (UI handles it; headless integrators use a custom approval middleware listener).
|
|
111
|
+
|
|
112
|
+
## Exported building blocks (for custom UIs)
|
|
113
|
+
|
|
114
|
+
- `ChatDialog`, `MessageContent`, `CodePreview` — Vue components
|
|
115
|
+
- `useChat(opts)` — composable (streaming/retry/stop/regenerate logic)
|
|
116
|
+
- `createAgent(options)` — the raw harness (if you bypass `createChatSdk`)
|
|
117
|
+
- Middleware factories: `createApprovalMiddleware`, `createVerifyMiddleware`, `createWriteBackCheck`, `createSubagentMiddleware`, `createCheckpointMiddleware`, `createUsageHintsMiddleware`
|
|
118
|
+
- Storage: `createSessionStore`, `createMemoryBackend`, `createWebStorageBackend`, `isQuotaError`
|
|
119
|
+
- JSON helpers: `jpEval`, `searchJson`, `runSandboxedScript`, `toolError`, `zodError`
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# createChatSdk options — what each does & when to use
|
|
2
|
+
|
|
3
|
+
Full reference for `createChatSdk(options)`. Grouped by purpose. Required: `llm`. Everything else is optional with sane defaults.
|
|
4
|
+
|
|
5
|
+
## LLM & identity
|
|
6
|
+
|
|
7
|
+
| Option | Type | Default | Purpose / when |
|
|
8
|
+
|---|---|---|---|
|
|
9
|
+
| `llm` | `LLMConfig \| BaseChatModel` | — (required) | The model. `LLMConfig = { apiKey, baseUrl, model, temperature?, maxTokens? }` (OpenAI-compatible; DeepSeek default). Or pass any LangChain `BaseChatModel` (e.g. `ChatAnthropic`, install its peerDep). |
|
|
10
|
+
| `systemPrompt` | `string` | generic page assistant | Agent identity/instructions. Inject here, not hardcoded. Keep single-line in `.env` (`VITE_AI_SYSTEM_PROMPT`). |
|
|
11
|
+
| `id` | `string` | random + warn | Stable agent id for multi-agent isolation & persistence. **Must pass a stable value** if you use `storage` or run multiple agents on one page. |
|
|
12
|
+
| `title` / `placeholder` | `string` | — | Dialog title / input placeholder (cosmetic). |
|
|
13
|
+
|
|
14
|
+
## UI & mounting
|
|
15
|
+
|
|
16
|
+
| Option | Type | Default | Purpose / when |
|
|
17
|
+
|---|---|---|---|
|
|
18
|
+
| `container` | `string \| HTMLElement` | — | Where the built-in dialog mounts. Required when `ui !== false`. |
|
|
19
|
+
| `ui` | `boolean \| 'default'` | `true` | `false` = headless (no built-in dialog; you build UI from `sdk.messages` + `sdk.send`). `'default'` = built-in `ChatDialog`. |
|
|
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
|
+
|
|
22
|
+
## window operation (the core)
|
|
23
|
+
|
|
24
|
+
| Option | Type | Default | Purpose / when |
|
|
25
|
+
|---|---|---|---|
|
|
26
|
+
| `windowProps` | `WindowPropSpec[]` | `[]` | 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_window_snapshot`. |
|
|
28
|
+
| `permissions` | `PermissionRule[]` | off | Scope whitelist (first-match-wins) for fine-grained per-path/tool rules. Default off (all declared paths writable). |
|
|
29
|
+
|
|
30
|
+
`WindowPropSpec = { 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
|
+
|
|
32
|
+
## Tools, skills, memory
|
|
33
|
+
|
|
34
|
+
| Option | Type | Default | Purpose / when |
|
|
35
|
+
|---|---|---|---|
|
|
36
|
+
| `tools` | `Tool[]` | `[]` | Custom tools beyond built-ins. Use `defineTool({ name, description, schema, handler })`. |
|
|
37
|
+
| `skills` | `SkillSpec[]` | `[]` | Progressive-disclosure skills (`defineSkill({ name, description, prompt }`) loaded on demand by the agent. |
|
|
38
|
+
| `memory` | `string` | — | AGENTS.md-style persistent instructions injected into every prompt (project conventions, hard rules). |
|
|
39
|
+
| `middleware` | `Middleware[]` | `[]` | Custom middleware appended after built-ins. 8 hooks: `beforeAgent`/`wrapModelCall`/`beforeModel`/`afterModel`/`wrapToolCall`/`afterAgent`/`beforeReturn` + `augmentPrompt`/`compressInput`/`tools`. For interception, instrumentation, prompt enhancement. |
|
|
40
|
+
|
|
41
|
+
## Capabilities (turn built-ins on/off)
|
|
42
|
+
|
|
43
|
+
`capabilities: { ... }` — default all `true` except `verify`. Set `false` to drop unused built-ins (saves tokens/size).
|
|
44
|
+
|
|
45
|
+
| Flag | Off when... |
|
|
46
|
+
|---|---|
|
|
47
|
+
| `windowOps` | Pure research agent, no page edits (also drops window tools from subagents). |
|
|
48
|
+
| `fetch` | No web fetching needed. ⚠️ turning off `windowOps` also strips subagent window tools. |
|
|
49
|
+
| `planning` | Don't want `write_todos` planning. |
|
|
50
|
+
| `skills` | Don't want progressive skill loading. |
|
|
51
|
+
| `vfs` | No in-memory workspace; ⚠️ large tool results then truncate instead of offloading. |
|
|
52
|
+
| `summarization` | No context compression; ⚠️ long sessions grow unbounded. |
|
|
53
|
+
| `memory` | No persistent instructions. |
|
|
54
|
+
| `subagent` | No `spawn_agent`/`spawn_agents` delegation. |
|
|
55
|
+
| `verify` (reverse) | **Off by default**; `true` enables write-back self-check before the agent returns (costs tokens). |
|
|
56
|
+
|
|
57
|
+
## Robustness & limits
|
|
58
|
+
|
|
59
|
+
| Option | Type | Default | Purpose / when |
|
|
60
|
+
|---|---|---|---|
|
|
61
|
+
| `maxRetries` | `number` | 2 | Model call retries on network/429/5xx (exponential backoff). 4xx & abort don't retry. |
|
|
62
|
+
| `maxParallelTools` | `number` | 1 | Same-round tool concurrency. `>1` is faster but watch stateful middleware (todos counts). |
|
|
63
|
+
| `maxToolRounds` | `number` | — | Cap agent tool rounds (safety against loops). |
|
|
64
|
+
| `maxMemoryRounds` | `number` | 50 | In-memory dialog rounds cap; oldest compressed to a summary system message (OOM guard). `0` disables. |
|
|
65
|
+
| `contextWindow` / `maxOutputTokens` | `number` | by model name | Override model context/output token limits (affects offload threshold & compression trigger). |
|
|
66
|
+
| `debug` | `boolean` | `false` | Verbose logging / DebugDrawer. |
|
|
67
|
+
|
|
68
|
+
## Context compression
|
|
69
|
+
|
|
70
|
+
| Option | Type | Default | Purpose / when |
|
|
71
|
+
|---|---|---|---|
|
|
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. |
|
|
74
|
+
| `summaryLlm` | `BaseChatModel \| LLMConfig` | main `llm` | Use a cheaper/faster model for summarization. |
|
|
75
|
+
| `summaryTemperature` | `number` | 0.3 | Summary model temperature. |
|
|
76
|
+
| `summaryMaxTokens` | `number` | 1024 | Summary output cap. |
|
|
77
|
+
| `summaryTimeoutMs` | `number` | 15000 | On timeout, fall back to index-based summary (no failure). |
|
|
78
|
+
|
|
79
|
+
## Subagent (delegation)
|
|
80
|
+
|
|
81
|
+
| Option | Type | Default | Purpose / when |
|
|
82
|
+
|---|---|---|---|
|
|
83
|
+
| `subagent` | `object` | enabled | `{ enabled?, allowedTools?, systemPrompt?, temperature?, maxTokens?, skills?, llm?, maxDepth?, maxParallel? }`. `maxDepth` (1) physically cuts recursion. Subagents get a read-only tool subset (no spawn). |
|
|
84
|
+
| `subagents` | `SubagentConfig[]` | `[]` | Pre-declared named subagents → each auto-generates a `use_<id>({ task })` delegation tool (Claude-Code style). Fixed roles (research/review) vs ad-hoc `spawn_agent`. |
|
|
85
|
+
|
|
86
|
+
## Verify (self-check before return)
|
|
87
|
+
|
|
88
|
+
`capabilities.verify: true` enables. `verify: { check?, maxAttempts?, adversarial? }`:
|
|
89
|
+
- `check` omitted → default `createWriteBackCheck()` (scans all writes, reads back + schema-validates; skips legitimately-rejected writes).
|
|
90
|
+
- custom `check: async ({ messages, state }) => ({ ok, feedback? })` — return **actionable** feedback.
|
|
91
|
+
- `adversarial: true` → after check passes, spawn a read-only "refuter" subagent (costs extra rounds; for semantically complex cases).
|
|
92
|
+
- `maxAttempts` (default 2) caps self-correction loops.
|
|
93
|
+
|
|
94
|
+
## Approval (human-in-the-loop)
|
|
95
|
+
|
|
96
|
+
`approval: { tools?, confirm?, ... }` — human confirms before tool execution. Default off; passing `approval` enables. Headless integrators listen for `approval_request` (NOT forwarded via `onEvent`/`hook`) to build their own confirm UI.
|
|
97
|
+
|
|
98
|
+
## Checkpoint (session rollback)
|
|
99
|
+
|
|
100
|
+
`checkpoint: true \| { maxCheckpoints?, auto? }` — per-round snapshot of (messages + window props + vfs + todos). `restoreLastCheckpoint()` / LLM tool `restore_last_checkpoint` / UI button. Distinct from windowOps per-path snapshots (checkpoint = whole-session rollback).
|
|
101
|
+
|
|
102
|
+
## Persistence (storage)
|
|
103
|
+
|
|
104
|
+
| Option | Type | Default | Purpose / when |
|
|
105
|
+
|---|---|---|---|
|
|
106
|
+
| `storage` | `'indexed'\|'session'\|'local'\|'memory'\| StorageConfig \| false` | `false` (off) | Off by default; assign to enable. Persists messages/vfs/todos/memory (NOT window snapshots). Auto-degrades to memory if backend unavailable (private mode / quota). |
|
|
107
|
+
| `session` | `SessionOptions` | — | Session control (resume by id, etc.). |
|
|
108
|
+
| `shareContext` | `boolean` | `false` | `true` → multiple `createChatSdk` with same `id` share one `AgentCore` (same agent, multiple dialog views on a page). |
|
|
109
|
+
|
|
110
|
+
## MCP (external tools)
|
|
111
|
+
|
|
112
|
+
`mcp: [{ transport: 'http'\|'sse'\|'websocket', url, name?, requestInit? }]` — connect remote MCP servers, dynamically inject their tools (`Promise.allSettled` fault-isolated). Browser only supports remote transports (no stdio). `@modelcontextprotocol/sdk` is an optional peerDep, dynamically imported only when used.
|
|
113
|
+
|
|
114
|
+
## Events
|
|
115
|
+
|
|
116
|
+
| Option | Type | Default | Purpose / when |
|
|
117
|
+
|---|---|---|---|
|
|
118
|
+
| `onEvent` | `(e: SdkEvent) => void` | — | Constructor-time event subscription (single). Replaces polling for host-page reactivity. See [api.md](api.md) for event types. |
|
|
119
|
+
|
|
120
|
+
Runtime subscription via `sdk.hook(handler) => () => void` (multi-listener, cancellable) — see [api.md](api.md).
|
|
121
|
+
|
|
122
|
+
## vfs (in-memory workspace)
|
|
123
|
+
|
|
124
|
+
`vfs: { initialFiles?, maxBytes? }` — `maxBytes` default 4MB; LRU-evicts oldest files on overflow. Tool results > 6000 chars auto-offload to vfs (only preview + `vfs_read`/`vfs_grep` reference kept). Disabling `capabilities.vfs` degrades to truncation.
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Quickstart (progressive)
|
|
2
|
+
|
|
3
|
+
From the smallest working setup to a full-featured integration. Read top-down; stop wherever your needs are met.
|
|
4
|
+
|
|
5
|
+
## Stage 0 — Prerequisites
|
|
6
|
+
|
|
7
|
+
- An OpenAI-compatible LLM endpoint (DeepSeek works out of the box). Get an API key.
|
|
8
|
+
- Page data you want the AI to edit, placed on `window` (e.g. `window.app = { ... }`).
|
|
9
|
+
|
|
10
|
+
## Stage 1 — Minimal (5 lines, CDN, no build)
|
|
11
|
+
|
|
12
|
+
Drop into any HTML page. The built-in dialog mounts itself.
|
|
13
|
+
|
|
14
|
+
```html
|
|
15
|
+
<div id="root"></div>
|
|
16
|
+
<script src="https://unpkg.com/page-agent-sdk"></script>
|
|
17
|
+
<script>
|
|
18
|
+
window.app = { title: 'Hello', theme: 'light' }
|
|
19
|
+
ChatSdk.createChatSdk({
|
|
20
|
+
container: '#root',
|
|
21
|
+
llm: { apiKey: 'sk-...', baseUrl: 'https://api.deepseek.com/v1', model: 'deepseek-chat' },
|
|
22
|
+
systemPrompt: 'You are a page assistant. Read/write window.app via tools.',
|
|
23
|
+
windowProps: [
|
|
24
|
+
{ path: 'app.title', description: '标题', schema: ChatSdk.z.string() },
|
|
25
|
+
{ path: 'app.theme', description: '主题', schema: ChatSdk.z.enum(['light','dark']) },
|
|
26
|
+
],
|
|
27
|
+
}).mount()
|
|
28
|
+
</script>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Talk to it: "change theme to dark" → AI calls `set_window_prop` → `window.app.theme === 'dark'`.
|
|
32
|
+
|
|
33
|
+
## Stage 2 — npm + module project
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm i page-agent-sdk zod @langchain/openai @langchain/core
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { createChatSdk, z } from 'page-agent-sdk'
|
|
41
|
+
import 'page-agent-sdk/style.css'
|
|
42
|
+
|
|
43
|
+
window.app = { title: 'Hello', theme: 'light', items: [] }
|
|
44
|
+
|
|
45
|
+
const sdk = createChatSdk({
|
|
46
|
+
container: '#root',
|
|
47
|
+
llm: { apiKey: import.meta.env.VITE_AI_API_KEY, baseUrl: 'https://api.deepseek.com/v1', model: 'deepseek-chat' },
|
|
48
|
+
systemPrompt: 'You are a page assistant. Read/write window.app via tools.',
|
|
49
|
+
windowProps: [
|
|
50
|
+
{ path: 'app.title', description: '标题', schema: z.string() },
|
|
51
|
+
{ path: 'app.theme', description: '主题', schema: z.enum(['light','dark']) },
|
|
52
|
+
{ path: 'app.items', description: '列表项', schema: z.array(z.object({ name: z.string(), price: z.number() })) },
|
|
53
|
+
],
|
|
54
|
+
}).mount()
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Stage 3 — React to changes (replace polling)
|
|
58
|
+
|
|
59
|
+
Subscribe via `onEvent` (constructor) or `sdk.hook` (runtime, multi-listener, cancellable):
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
const sdk = createChatSdk({
|
|
63
|
+
onEvent(e) {
|
|
64
|
+
if (e.type === 'window_prop_change') renderUI() // host page reactive refresh
|
|
65
|
+
if (e.type === 'error') console.error(e.message)
|
|
66
|
+
},
|
|
67
|
+
// ...llm, windowProps...
|
|
68
|
+
}).mount()
|
|
69
|
+
|
|
70
|
+
// runtime listener (e.g. analytics), cancellable
|
|
71
|
+
const off = sdk.hook((e) => { if (e.type === 'tool_call') track(e.name) })
|
|
72
|
+
// off()
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Stage 4 — Headless (custom UI, framework-agnostic)
|
|
76
|
+
|
|
77
|
+
No built-in dialog; drive the reactive `messages` array yourself.
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
const sdk = createChatSdk({
|
|
81
|
+
ui: false, // headless
|
|
82
|
+
llm: { ... }, systemPrompt: '...', windowProps: [...],
|
|
83
|
+
}).mount()
|
|
84
|
+
|
|
85
|
+
// your own UI reads sdk.messages (reactive) and calls sdk.send
|
|
86
|
+
await sdk.send('add a new item: name=Pen, price=3')
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Reusable `ChatDialog` / `MessageContent` / `CodePreview` components + `useChat` composable are also exported if you want to assemble a custom UI from existing parts.
|
|
90
|
+
|
|
91
|
+
## Stage 5 — Tune capabilities & safety
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
createChatSdk({
|
|
95
|
+
// ...llm, windowProps...
|
|
96
|
+
capabilities: { verify: true }, // write-back self-check before agent returns
|
|
97
|
+
verify: { maxAttempts: 2 }, // auto-correct on failure (default check = write-back read + schema)
|
|
98
|
+
approval: { tools: ['set_window_prop', 'edit_window_prop'] }, // human-confirm before writes
|
|
99
|
+
checkpoint: true, // session-level rollback on bad edits
|
|
100
|
+
maxParallelTools: 1, // serial tool calls (safe for stateful middleware)
|
|
101
|
+
contextPreset: 'conservative', // save cost on long sessions
|
|
102
|
+
}).mount()
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Stage 6 — Persist across refresh / multi-session
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
createChatSdk({
|
|
109
|
+
id: 'my-page-agent', // STABLE id (multi-agent isolation); omit = random + warn
|
|
110
|
+
storage: 'indexed', // persist messages/vfs/todos/memory to IndexedDB
|
|
111
|
+
// ...llm, windowProps...
|
|
112
|
+
}).mount()
|
|
113
|
+
|
|
114
|
+
// later, switch session:
|
|
115
|
+
await sdk.switchSession('session-abc') // load or create
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Next
|
|
119
|
+
|
|
120
|
+
- All options: see [options.md](options.md)
|
|
121
|
+
- Instance API + tool/skill definition: see [api.md](api.md)
|
|
122
|
+
- End-to-end scenarios: see [use-cases.md](use-cases.md)
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# Use cases — end-to-end scenarios
|
|
2
|
+
|
|
3
|
+
Concrete integration patterns for common scenarios. Each shows the key `windowProps` + options that matter. Adapt the LLM config to your provider.
|
|
4
|
+
|
|
5
|
+
## 1. Low-code page builder
|
|
6
|
+
|
|
7
|
+
A visual builder where the page is a component tree; the AI edits the tree via jsonPath patches and the canvas re-renders live.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
window.page = {
|
|
11
|
+
components: [
|
|
12
|
+
{ id: 'banner', type: 'banner', props: { title: 'Welcome', bg: '#1f4d3a' } },
|
|
13
|
+
{ id: 'card1', type: 'card', props: { title: '新品', price: 99 } },
|
|
14
|
+
],
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
createChatSdk({
|
|
18
|
+
container: '#chat',
|
|
19
|
+
llm: { apiKey, baseUrl: 'https://api.deepseek.com/v1', model: 'deepseek-chat', temperature: 0.3 },
|
|
20
|
+
systemPrompt: '你是页面搭建助手。用 edit_window_prop 按 jsonPath 增量改 components,不要重传整树。',
|
|
21
|
+
windowProps: [
|
|
22
|
+
{ path: 'page.components', description: '组件树',
|
|
23
|
+
schema: z.array(z.object({
|
|
24
|
+
id: z.string(), type: z.string(),
|
|
25
|
+
props: z.record(z.any()),
|
|
26
|
+
})) },
|
|
27
|
+
],
|
|
28
|
+
onEvent(e) { if (e.type === 'window_prop_change') renderCanvas() }, // canvas reactive refresh
|
|
29
|
+
checkpoint: true, // bad edit → one-click rollback
|
|
30
|
+
approval: { tools: ['set_window_prop', 'edit_window_prop'] }, // confirm writes
|
|
31
|
+
}).mount()
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
User: "顶部 Banner 改深色、主标题加粗、加一张新品卡" → AI calls `edit_window_prop` per component.
|
|
35
|
+
|
|
36
|
+
## 2. Form designer
|
|
37
|
+
|
|
38
|
+
Form schema as data; AI edits field definitions, schema validation prevents malformed forms.
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
window.form = {
|
|
42
|
+
fields: [
|
|
43
|
+
{ name: 'phone', label: '手机号', type: 'text', required: true, validation: 'none' },
|
|
44
|
+
{ name: 'address', label: '地址', type: 'text', required: false, cascade: false },
|
|
45
|
+
],
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
createChatSdk({
|
|
49
|
+
container: '#chat', llm: { ... },
|
|
50
|
+
systemPrompt: '你是表单设计助手。改 form.fields 的字段定义,保持 schema 合法。',
|
|
51
|
+
windowProps: [
|
|
52
|
+
{ path: 'form.fields', description: '字段定义数组',
|
|
53
|
+
schema: z.array(z.object({
|
|
54
|
+
name: z.string(), label: z.string(),
|
|
55
|
+
type: z.enum(['text','number','select','date']),
|
|
56
|
+
required: z.boolean(),
|
|
57
|
+
validation: z.enum(['none','phone','email','idcard']),
|
|
58
|
+
cascade: z.boolean().optional(),
|
|
59
|
+
})) },
|
|
60
|
+
],
|
|
61
|
+
onEvent(e) { if (e.type === 'window_prop_change') renderForm() },
|
|
62
|
+
}).mount()
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
User: "手机号加格式校验、地址改三级联动" → AI patches `form.fields[0].validation='phone'`, `form.fields[1].cascade=true`.
|
|
66
|
+
|
|
67
|
+
## 3. CMS batch operation
|
|
68
|
+
|
|
69
|
+
Bulk-edit a product list; use `eval_window_script` or `search_window_prop` + `edit_window_prop` for batch ops.
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
window.products = [
|
|
73
|
+
{ id: 1, title: '商品A', price: 99, highlight: false },
|
|
74
|
+
{ id: 2, title: '商品B', price: 150, highlight: false },
|
|
75
|
+
// ...hundreds
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
createChatSdk({
|
|
79
|
+
container: '#chat', llm: { ... },
|
|
80
|
+
systemPrompt: '你是运营助手。批量改 products;标题加前缀用 eval_window_script,按条件筛选用 search_window_prop。',
|
|
81
|
+
windowProps: [
|
|
82
|
+
{ path: 'products', description: '商品列表',
|
|
83
|
+
schema: z.array(z.object({
|
|
84
|
+
id: z.number(), title: z.string(), price: z.number(), highlight: z.boolean(),
|
|
85
|
+
})) },
|
|
86
|
+
],
|
|
87
|
+
onEvent(e) { if (e.type === 'window_prop_change') renderTable() },
|
|
88
|
+
}).mount()
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
User: "标题加『限时』前缀、低于 100 元的标红" → AI uses `eval_window_script` for the prefix loop + `search_window_prop` to find `<100` then `edit_window_prop` to set `highlight`.
|
|
92
|
+
|
|
93
|
+
## 4. Ops config console
|
|
94
|
+
|
|
95
|
+
Edit experiment thresholds / feature flags with human confirmation.
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
window.config = {
|
|
99
|
+
expA: { threshold: 0.5, enabled: true },
|
|
100
|
+
featureB: { enabled: false },
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
createChatSdk({
|
|
104
|
+
container: '#chat', llm: { ... },
|
|
105
|
+
systemPrompt: '你是运维助手。改 config 前必须经用户确认。',
|
|
106
|
+
windowProps: [
|
|
107
|
+
{ path: 'config.expA', description: '实验A',
|
|
108
|
+
schema: z.object({ threshold: z.number().min(0).max(1), enabled: z.boolean() }) },
|
|
109
|
+
{ path: 'config.featureB', description: 'B开关',
|
|
110
|
+
schema: z.object({ enabled: z.boolean() }) },
|
|
111
|
+
],
|
|
112
|
+
approval: { tools: ['set_window_prop', 'edit_window_prop'] }, // human-in-the-loop
|
|
113
|
+
checkpoint: true,
|
|
114
|
+
capabilities: { verify: true }, // write-back read + schema check
|
|
115
|
+
}).mount()
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
User: "A 实验阈值调到 30%、关掉 B 开关" → AI proposes writes → user confirms → verify reads back.
|
|
119
|
+
|
|
120
|
+
## 5. AI-native assistant (no page data, custom tools)
|
|
121
|
+
|
|
122
|
+
The agent drives your product's own API via custom tools (no windowOps).
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
const lookupTool = defineTool({
|
|
126
|
+
name: 'lookup_order',
|
|
127
|
+
description: '查询订单',
|
|
128
|
+
schema: z.object({ orderId: z.string() }),
|
|
129
|
+
handler: async ({ orderId }) => JSON.stringify(await api.getOrder(orderId)),
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
createChatSdk({
|
|
133
|
+
container: '#chat',
|
|
134
|
+
llm: { ... },
|
|
135
|
+
systemPrompt: '你是订单助手。用 lookup_order 查询。',
|
|
136
|
+
tools: [lookupTool],
|
|
137
|
+
capabilities: { windowOps: false, fetch: false }, // pure custom-tool agent
|
|
138
|
+
}).mount()
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## 6. Research agent (fetch + subagents, no writes)
|
|
142
|
+
|
|
143
|
+
Pure research: fetch docs, parallel subagents for multi-source investigation.
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
createChatSdk({
|
|
147
|
+
container: '#chat', llm: { ... },
|
|
148
|
+
systemPrompt: '你是调研助手。多源对比用 spawn_agents 并行委派。',
|
|
149
|
+
capabilities: { windowOps: false }, // read-only, no page edits
|
|
150
|
+
subagent: { allowedTools: ['fetch_document'] },
|
|
151
|
+
contextPreset: 'conservative', // long research sessions
|
|
152
|
+
}).mount()
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## 7. Headless server-side (Node.js)
|
|
156
|
+
|
|
157
|
+
Run the agent in Node (no browser). Provide `globalThis.window` only if you enable windowOps.
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
// node mjs
|
|
161
|
+
import { createChatSdk, z } from 'page-agent-sdk'
|
|
162
|
+
|
|
163
|
+
const sdk = createChatSdk({
|
|
164
|
+
ui: false,
|
|
165
|
+
storage: 'memory',
|
|
166
|
+
llm: { apiKey, baseUrl, model },
|
|
167
|
+
systemPrompt: '...',
|
|
168
|
+
capabilities: { windowOps: false, fetch: false },
|
|
169
|
+
tools: [/* your tools */],
|
|
170
|
+
})
|
|
171
|
+
await sdk.mount()
|
|
172
|
+
const reply = await sdk.send('do something')
|
|
173
|
+
console.log(reply)
|
|
174
|
+
sdk.unmount()
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## 8. Multi-agent on one page (shared context)
|
|
178
|
+
|
|
179
|
+
Two dialogs backed by one agent brain.
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
const a = createChatSdk({ id: 'shared', container: '#dlg-a', llm: {...}, shareContext: true, windowProps }).mount()
|
|
183
|
+
const b = createChatSdk({ id: 'shared', container: '#dlg-b', llm: {...}, shareContext: true, windowProps }).mount()
|
|
184
|
+
// a & b share messages/agent/vfs/todos/memory — two views of one agent
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## 9. MCP integration (external tool servers)
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
createChatSdk({
|
|
191
|
+
container: '#chat', llm: { ... },
|
|
192
|
+
mcp: [
|
|
193
|
+
{ transport: 'http', url: 'https://my-mcp-server/mcp' },
|
|
194
|
+
{ transport: 'sse', url: 'https://another/sse' },
|
|
195
|
+
],
|
|
196
|
+
// MCP tools auto-injected; fault-isolated (one server down doesn't break others)
|
|
197
|
+
}).mount()
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
> Note: `@modelcontextprotocol/sdk` is an optional peerDep — install it only if you use `mcp`. Browser supports only remote transports (http/sse/websocket), not stdio.
|