page-agent-sdk 1.1.3 → 1.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "page-agent-sdk",
3
- "version": "1.1.3",
3
+ "version": "1.2.0",
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,6 +77,23 @@ 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
+ | **Lazy-loaded components (dynamic schemas)** | `sdk.addWindowProp(spec)` on component mount / `removeWindowProp` on unmount; tools pick up new registrations immediately, no rebuild. See [references/advanced.md §0](references/advanced.md) |
94
+
95
+ 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. For dynamic/lazy-loaded component schemas, custom tools/skills/subagents/MCP, load [references/advanced.md](references/advanced.md).
96
+
80
97
  ## References (read as needed)
81
98
 
82
99
  Detailed docs live in this skill's `references/` folder — load the one matching the user's question:
@@ -85,6 +102,7 @@ Detailed docs live in this skill's `references/` folder — load the one matchin
85
102
  - **[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.
86
103
  - **[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.
87
104
  - **[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.
105
+ - **[references/advanced.md](references/advanced.md)** — detailed examples for the extensibility surfaces: **dynamic windowProps (`sdk.addWindowProp`/`removeWindowProp` for lazy-loaded components)**, 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" or "lazy-load components with different schemas".
88
106
 
89
107
  Project-level docs (in the repo, not bundled in this skill):
90
108
  - `doc/usage-guide.md` (zh) / `doc/usage-guide.en.md` — full options reference
@@ -0,0 +1,250 @@
1
+ # Advanced examples — custom tools, skills, subagents, MCP, dynamic windowProps
2
+
3
+ Detailed, copy-paste examples for the extensibility surfaces. Read the section matching the user's need.
4
+
5
+ ## 0. Dynamic windowProps (lazy-loaded components) — `sdk.addWindowProp` / `removeWindowProp`
6
+
7
+ When components are lazy-loaded with **different schemas each**, don't declare all `windowProps` upfront. Register them at runtime as components mount/unmount. The agent's window tools pick up new registrations immediately (no agent rebuild).
8
+
9
+ ```ts
10
+ const sdk = createChatSdk({
11
+ container: '#chat', llm: { ... },
12
+ systemPrompt: '你是页面助手,按组件类型操作 window.app.components.<id>。',
13
+ windowProps: [
14
+ // statically-declared ones (always present)
15
+ { path: 'app.config', description: '全局配置', schema: z.record(z.any()) },
16
+ ],
17
+ }).mount()
18
+
19
+ // 组件懒加载时动态注册其 schema(结构各异)
20
+ function onComponentMount(comp: { id: string; type: string; schema: z.ZodType }) {
21
+ sdk.addWindowProp({ path: `app.components.${comp.id}`, description: `${comp.type} 组件`, schema: comp.schema })
22
+ // 立即生效:AI 现在能 set/edit_window_prop 这个 path,按其 schema 校验
23
+ }
24
+
25
+ // 组件卸载时移除(快照栈一并清理)
26
+ function onComponentUnmount(id: string) {
27
+ sdk.removeWindowProp(`app.components.${id}`)
28
+ }
29
+
30
+ // 查看当前所有注册项(反映动态增删)
31
+ const current: WindowPropSpec[] = sdk.listWindowProps()
32
+ ```
33
+
34
+ Notes:
35
+ - `addWindowProp` 覆盖同名 path 时保留旧快照栈;按新 schema 校验。
36
+ - 动态注册的属性**不自动纳入 checkpoint 快照**(checkpoint 的 windowPaths 在构造时固定);如需回滚动态组件,自行管理或重建。
37
+ - `inspect().windowProps` 与 `verify`(默认 `createWriteBackCheck`)均反映动态注册的最新 schemas(verify 每次 check 实时取 `listWindowProps()`)。
38
+ - `capabilities.windowOps:false` 时 `addWindowProp`/`removeWindowProp` 为 no-op(并 warn)。
39
+
40
+ ## 1. Custom tools (`defineTool`)
41
+
42
+ Custom tools extend the agent beyond built-in `windowOps`/`fetch`. Use them to expose your product's API to the AI.
43
+
44
+ ### Minimal
45
+
46
+ ```ts
47
+ import { createChatSdk, defineTool, z } from 'page-agent-sdk'
48
+
49
+ const lookupOrder = defineTool({
50
+ name: 'lookup_order',
51
+ description: '查询订单 by id',
52
+ schema: z.object({ orderId: z.string() }),
53
+ handler: async ({ orderId }) => JSON.stringify(await api.getOrder(orderId)),
54
+ })
55
+ ```
56
+
57
+ ### With error handling
58
+
59
+ Return structured errors via `toolError` so the AI can react:
60
+
61
+ ```ts
62
+ import { defineTool, toolError, z } from 'page-agent-sdk'
63
+
64
+ const updatePrice = defineTool({
65
+ name: 'update_price',
66
+ description: '更新商品价格',
67
+ schema: z.object({ sku: z.string(), price: z.number().positive() }),
68
+ handler: async ({ sku, price }) => {
69
+ const ok = await api.setPrice(sku, price)
70
+ if (!ok) return toolError({ path: sku, code: 'NOT_FOUND', message: `SKU ${sku} 不存在` })
71
+ return `已更新 ${sku} 价格为 ${price}`
72
+ },
73
+ })
74
+ ```
75
+
76
+ ### Coexisting with windowOps
77
+
78
+ Mix custom tools with built-in window tools:
79
+
80
+ ```ts
81
+ createChatSdk({
82
+ container: '#chat', llm: { ... },
83
+ windowProps: [{ path: 'app.config', description: '配置', schema: z.record(z.any()) }],
84
+ tools: [lookupOrder, updatePrice], // custom + built-in windowOps together
85
+ }).mount()
86
+ ```
87
+
88
+ ### Pure custom-tool agent (no windowOps)
89
+
90
+ ```ts
91
+ createChatSdk({
92
+ container: '#chat', llm: { ... },
93
+ tools: [lookupOrder, updatePrice],
94
+ capabilities: { windowOps: false, fetch: false }, // drop built-ins
95
+ }).mount()
96
+ ```
97
+
98
+ ## 2. Skills (`defineSkill`) — progressive disclosure
99
+
100
+ 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.
101
+
102
+ ### Inline content skill
103
+
104
+ ```ts
105
+ import { createChatSdk, defineSkill } from 'page-agent-sdk'
106
+
107
+ const apiDesignSkill = defineSkill({
108
+ name: 'api-design',
109
+ description: '本项目 REST API 设计规范(何时用:设计/评审新接口)',
110
+ getContent: () => `
111
+ - URL 用 kebab-case,统一 /v1 前缀
112
+ - 列表接口必须分页(page+pageSize)
113
+ - 错误返回 { code, message, data: null }
114
+ - 写操作记审计日志
115
+ `,
116
+ })
117
+
118
+ createChatSdk({ container: '#chat', llm: { ... }, skills: [apiDesignSkill] }).mount()
119
+ ```
120
+
121
+ ### Remote doc skill (auto-fetched + cached to vfs)
122
+
123
+ ```ts
124
+ const brandSkill = defineSkill({
125
+ name: 'brand-guide',
126
+ description: '品牌视觉规范(何时用:涉及 UI/文案/配色)',
127
+ doc: 'https://my-wiki/brand.md', // SDK fetches + caches to vfs; large docs stay out of context
128
+ })
129
+
130
+ createChatSdk({ container: '#chat', llm: { ... }, skills: [brandSkill] }).mount()
131
+ ```
132
+
133
+ > `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.
134
+
135
+ ## 3. Subagents — ad-hoc spawn vs pre-declared
136
+
137
+ Subagents run isolated sub-tasks; **only their final conclusion** returns to the main context (saves tokens). Two flavors coexist.
138
+
139
+ ### 3a. Ad-hoc `spawn_agent` / `spawn_agents` (default enabled)
140
+
141
+ The main agent decides when to delegate via `spawn_agent` (one) / `spawn_agents` (parallel). Configure the subagent tool subset:
142
+
143
+ ```ts
144
+ createChatSdk({
145
+ container: '#chat', llm: { ... },
146
+ systemPrompt: '多源对比时用 spawn_agents 并行委派。',
147
+ subagent: {
148
+ allowedTools: ['fetch_document', 'get_window_prop'], // read-only subset (no spawn → no recursion)
149
+ maxDepth: 1, // physical recursion cut (default 1)
150
+ maxParallel: 3, // max parallel subagents in spawn_agents
151
+ temperature: 0.2, // subagent temperature (default inherits main)
152
+ },
153
+ }).mount()
154
+ ```
155
+
156
+ User: "对比 A/B/C 三个方案" → main agent calls `spawn_agents` with 3 tasks → 3 subagents research in parallel → only conclusions return.
157
+
158
+ ### 3b. Pre-declared named subagents (`subagents`) — Claude-Code style
159
+
160
+ 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:
161
+
162
+ ```ts
163
+ createChatSdk({
164
+ container: '#chat', llm: { ... },
165
+ systemPrompt: '复杂任务委派给专家子 agent。',
166
+ subagents: [
167
+ {
168
+ id: 'researcher',
169
+ description: '调研专家:搜集资料、对比方案(只读)',
170
+ tools: ['fetch_document', 'get_window_prop'], // read-only
171
+ temperature: 0.2,
172
+ },
173
+ {
174
+ id: 'reviewer',
175
+ description: '审查专家:检查代码/配置的安全与性能问题',
176
+ tools: ['get_window_prop', 'search_window_prop'],
177
+ systemPrompt: '你是审查专家,只报告问题不改数据。',
178
+ temperature: 0.1,
179
+ },
180
+ ],
181
+ }).mount()
182
+ ```
183
+
184
+ Now the main agent has `use_researcher({ task })` and `use_reviewer({ task })` tools. Each subagent inherits main config where omitted (`llm`, `maxTokens`, `skills`...).
185
+
186
+ > 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.
187
+
188
+ ## 4. MCP (external tool servers)
189
+
190
+ Connect remote MCP servers; their tools auto-inject into the agent. `Promise.allSettled` → one server down doesn't break others.
191
+
192
+ ### HTTP (StreamableHTTP) — recommended
193
+
194
+ ```ts
195
+ createChatSdk({
196
+ container: '#chat', llm: { ... },
197
+ mcp: [
198
+ { transport: 'http', url: 'https://my-mcp-server/mcp', name: 'my-tools' },
199
+ ],
200
+ }).mount()
201
+ ```
202
+
203
+ ### SSE / WebSocket
204
+
205
+ ```ts
206
+ mcp: [
207
+ { transport: 'sse', url: 'https://another/sse' },
208
+ { transport: 'websocket', url: 'wss://ws-server/mcp' },
209
+ ]
210
+ ```
211
+
212
+ ### With request init (auth headers)
213
+
214
+ ```ts
215
+ mcp: [
216
+ {
217
+ transport: 'http', url: 'https://my-mcp/mcp',
218
+ requestInit: { headers: { Authorization: `Bearer ${token}` } },
219
+ },
220
+ ]
221
+ ```
222
+
223
+ ### Notes
224
+
225
+ - `@modelcontextprotocol/sdk` is an **optional peerDep** — install it only if you use `mcp`. It's dynamically imported (zero cost when unused).
226
+ - Browser supports **only remote transports** (http/sse/websocket), not stdio.
227
+ - MCP `inputSchema` (JSON Schema) is passed directly to LangChain `tool()` — no conversion.
228
+ - `inspect().mcp.servers` lists connected servers; each tool's `source` shows `mcp:<name>`.
229
+
230
+ ### Dev gotcha
231
+
232
+ 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.
233
+
234
+ ## 5. Combining everything
235
+
236
+ ```ts
237
+ createChatSdk({
238
+ container: '#chat',
239
+ llm: { apiKey, baseUrl, model },
240
+ systemPrompt: '...',
241
+ windowProps: [{ path: 'app.data', description: '...', schema: z.record(z.any()) }],
242
+ tools: [lookupOrder, updatePrice], // custom tools
243
+ skills: [apiDesignSkill, brandSkill], // progressive skills
244
+ subagents: [{ id: 'researcher', description: '...', tools: ['fetch_document'] }], // pre-declared
245
+ mcp: [{ transport: 'http', url: '...' }], // external tools
246
+ capabilities: { verify: true }, // self-check
247
+ approval: { tools: ['set_window_prop'] }, // human confirm writes
248
+ checkpoint: true, // rollback
249
+ }).mount()
250
+ ```
@@ -12,6 +12,9 @@
12
12
  | `inspect()` | `() => AgentInfo` | Inspect agent: tools/skills/windowProps/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
+ | `addWindowProp(spec)` | `(spec: WindowPropSpec) => void` | Runtime register/override a window prop (lazy-loaded components). Takes effect immediately, no rebuild. Needs `windowOps` enabled. |
16
+ | `removeWindowProp(path)` | `(path: string) => boolean` | Remove a registered window prop (component unmount); returns whether it existed. Clears its snapshot stack. |
17
+ | `listWindowProps()` | `() => WindowPropSpec[]` | List currently-registered window props (reflects dynamic add/remove). |
15
18
  | `restoreLastCheckpoint()` | `() => boolean` | Restore last good checkpoint (needs `checkpoint` enabled). |
16
19
  | `listCheckpoints()` | `() => CheckpointMeta[]` | List available checkpoints. |
17
20
 
@@ -39,8 +42,8 @@ import { defineSkill } from 'page-agent-sdk'
39
42
 
40
43
  const apiSkill = defineSkill({
41
44
  name: 'api-design',
42
- description: 'REST API design conventions for this project',
43
- prompt: 'Use kebab-case URLs; version under /v1; ...',
45
+ description: 'REST API design conventions for this project (load when designing/reviewing APIs)',
46
+ getContent: () => 'Use kebab-case URLs; version under /v1; ...', // or `doc: 'https://...'` for remote
44
47
  })
45
48
 
46
49
  createChatSdk({ skills: [apiSkill], /* ... */ }).mount()
package/types/index.d.ts CHANGED
@@ -168,6 +168,18 @@ export interface WindowOpsOptions {
168
168
  whitelist?: boolean;
169
169
  }
170
170
 
171
+ /** window 属性注册表控制器(运行时动态增删;createWindowOps 返回的工具数组上以不可枚举属性 `controller` 挂载) */
172
+ export interface WindowOpsController {
173
+ /** 新增/覆盖一个属性注册项(运行时懒加载组件场景);覆盖时旧快照栈保留 */
174
+ add(spec: WindowPropSpec): void;
175
+ /** 移除一个属性注册项;返回是否确实存在并移除。快照栈一并清理 */
176
+ remove(path: string): boolean;
177
+ /** 列出当前所有注册项(反映动态增删后的最新状态) */
178
+ list(): WindowPropSpec[];
179
+ /** 是否已注册某 path */
180
+ has(path: string): boolean;
181
+ }
182
+
171
183
  export interface PermissionRule {
172
184
  operations: ('read' | 'write')[];
173
185
  scopes: string[];
@@ -383,6 +395,12 @@ export interface ChatSdk {
383
395
  listCheckpoints(): CheckpointMeta[];
384
396
  /** 运行时订阅 SDK 事件(可多个监听器,返回取消函数);与构造时 onEvent 互补 */
385
397
  hook(handler: SdkEventHandler): () => void;
398
+ /** 运行时动态新增/覆盖一个 window 属性注册项(懒加载组件:组件挂载时注册其 schema);立即对 window 工具生效,无需重建 agent。需开启 windowOps */
399
+ addWindowProp(spec: WindowPropSpec): void;
400
+ /** 运行时移除一个 window 属性注册项(组件卸载);返回是否确实存在并移除。快照栈一并清理 */
401
+ removeWindowProp(path: string): boolean;
402
+ /** 列出当前所有已注册 window 属性(反映动态增删后的最新状态) */
403
+ listWindowProps(): WindowPropSpec[];
386
404
  }
387
405
 
388
406
  export declare function createChatSdk(options: ChatSdkOptions): ChatSdk;