page-agent-sdk 2.9.0 → 2.10.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": "2.9.0",
3
+ "version": "2.10.0",
4
4
  "type": "module",
5
5
  "description": "框架无关的页面内 Agent JS SDK —— 以对话框形态挂载到任意网页,通过自定义 tool 读写宿主预注册的数据槽(经 schema 校验 + jsonPath 增量 patch + 快照回退;GET 抓文档),具备 planning/skills/虚拟工作区/context 管理能力。Vue 打包进库,使用者无需安装 Vue。",
6
6
  "main": "./dist/page-agent-sdk.umd.cjs",
@@ -113,6 +113,7 @@ Detailed docs live in this skill's `references/` folder — load the one matchin
113
113
  - **[references/api.md](references/api.md)** — instance methods (`mount`/`send`/`stream`/`inspect`/`switchSession`/`hook`/`setData`/`getData`/checkpoints), `defineTool`/`defineSkill`/`presets`, built-in data tools, and the full `SdkEvent` type table. Read when the user asks about APIs, tools, or events.
114
114
  - **[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 / dynamic schema via setData). Read when the user wants a concrete pattern for their use case.
115
115
  - **[references/advanced.md](references/advanced.md)** — detailed examples for the extensibility surfaces: **dynamic schema (`sdk.setData` to swap main data at runtime)**, custom `defineTool` (with error handling + coexisting with dataOps), `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 "swap data/schema at runtime".
116
+ - **[references/integration-prompt.md](references/integration-prompt.md)** — a generic integration prompt template to copy into the target project's AI (Cursor / Claude Code) when the skill is NOT installed there. Fill in `[...]` per scenario. Read when the user asks "give me a prompt to integrate the SDK in another project" or wants a copy-paste prompt for a teammate's AI tool.
116
117
 
117
118
  Project-level docs (in the repo, not bundled in this skill):
118
119
  - `doc/usage-guide.md` (zh) / `doc/usage-guide.en.md` — full options reference
@@ -21,6 +21,10 @@
21
21
  | `usage` | `TokenUsage` | Cumulative token usage `{prompt_tokens, completion_tokens, total_tokens}` (accumulated per LLM call). |
22
22
  | `restoreLastCheckpoint()` | `() => boolean` | Restore last good checkpoint (needs `checkpoint` enabled). |
23
23
  | `listCheckpoints()` | `() => CheckpointMeta[]` | List available checkpoints. |
24
+ | `addSkill(skill)` | `(skill: { name, description, prompt \| getContent \| doc }) => void` | Add a user-created skill at runtime. Auto-merges into the skill list, persists via **independent SkillStore** (default indexedDB, separate from `storage` option), takes effect next round. Same-name overwrites. Requires `capabilities.skills` (default on) + `skillStorage` not `false` for persistence. |
25
+ | `removeSkill(name)` | `(name: string) => void` | Remove a user-created skill by name (only user-created, not init-time skills). Removes from SkillStore. No-op if not found. |
26
+ | `listUserSkills()` | `() => string[]` | List names of user-created skills (not init-time skills). Useful for UI panels. |
27
+ | `getUserSkill(name)` | `(name: string) => { name, description, content } \| undefined` | Read a user-created skill's detail (for SkillPanel edit). Returns `undefined` if not found. |
24
28
 
25
29
  ## defineTool (custom tools)
26
30
 
@@ -0,0 +1,179 @@
1
+ # Integration prompt template (generic)
2
+
3
+ Copy this prompt into the target project's Cursor / Claude Code so its AI integrates `page-agent-sdk` following the workflow. Fill in `[...]` per your scenario.
4
+
5
+ > Recommended: install the skill in the target project first (`cp -R node_modules/page-agent-sdk/skills/page-agent-sdk-integrate ~/.claude/skills/`) — the AI auto-integrates per workflow. Use this prompt only when the skill can't be installed.
6
+
7
+ ---
8
+
9
+ ## Your task
10
+
11
+ Integrate `page-agent-sdk` (npm package) into the current project to enable "[business scenario: e.g. low-code page builder / form designer / ops config console / CMS batch ops / AI-native assistant...]". The integrator declares ONE main data object; an AI agent edits it via schema-validated tools `read`/`write` incrementally, with [UI form: built-in dialog / drawer mode / headless custom UI].
12
+
13
+ ## Background
14
+
15
+ - `page-agent-sdk` is a **framework-agnostic JS SDK** (bundles Vue 3.5 internally, no conflict with host; works with Vue2/React/vanilla/Node)
16
+ - Core model: integrator declares **one main data object** `data: { schema, bind, description? }`; the agent reads/writes `bind` via tools (schema validation + whitelist + optimistic lock + snapshot)
17
+ - `bind` is any reactive/plain object; tools read/write it directly, **no `window` dependency**
18
+ - `schema` is a zod schema; field `.describe()` text is auto-injected into the systemPrompt so the LLM knows each field's purpose — no manual field descriptions needed
19
+ - Tools: `read` (read, supports jsonPath/fields/depth projection), `write` (write, merges set/edit/delete + auto optimistic lock + auto snapshot, supports patch jsonPath increments)
20
+ - Full docs: load the `page-agent-sdk-integrate` skill (if installed), or see `node_modules/page-agent-sdk/skills/page-agent-sdk-integrate/`
21
+
22
+ ## Steps
23
+
24
+ ### 1. Install
25
+
26
+ ```bash
27
+ npm i page-agent-sdk zod @langchain/openai @langchain/core
28
+ ```
29
+
30
+ ### 2. Declare the main data object + schema (key step)
31
+
32
+ ```ts
33
+ // dataSchema.ts
34
+ import { z } from 'page-agent-sdk'
35
+
36
+ // [Define schema per business: field names/types/shapes; field .describe() auto-injects into systemPrompt]
37
+ export const mainSchema = z.object({
38
+ title: z.string().describe('Page title'),
39
+ items: z.array(z.object({
40
+ id: z.string().describe('Item id'),
41
+ name: z.string().describe('Name'),
42
+ // ... other fields
43
+ })).describe('List items array'),
44
+ })
45
+
46
+ export type MainData = z.infer<typeof mainSchema>
47
+
48
+ // [Optional] skill content: business field/component docs for Agent load_skill on demand (saves systemPrompt tokens)
49
+ export const builderSkillContent = `# [Business name] Skill
50
+
51
+ Main data = { title, items[] }.
52
+
53
+ ## Fields
54
+ - title: page title
55
+ - items[]: list items, each = { id, name, ... }
56
+
57
+ ## Edit rules
58
+ - Add/remove items: edit items array (append/splice)
59
+ - Prefer incremental patch for single-item edits (only changed fields), avoid re-sending the whole array
60
+ - Validation failures return structured errors; fix per hint and retry
61
+ - jsonPath locates relative to main data root (e.g. items.0.name)`
62
+ ```
63
+
64
+ ### 3. Integrate the SDK
65
+
66
+ ```ts
67
+ import { createChatSdk, defineSkill } from 'page-agent-sdk'
68
+ import 'page-agent-sdk/style.css'
69
+ import { mainSchema, builderSkillContent } from './dataSchema'
70
+
71
+ // [bind: use a reactive object (Vue3 reactive / Vue2 data() return / React useState or ref.current)]
72
+ const mainData = { title: 'Initial title', items: [] }
73
+
74
+ const sdk = createChatSdk({
75
+ container: '#agent-root',
76
+ id: '[stable id, e.g. page-builder]', // multi-agent isolation + persistence namespace
77
+ storage: 'memory',
78
+ llm: {
79
+ apiKey: import.meta.env.VITE_AI_API_KEY || 'YOUR_API_KEY',
80
+ baseUrl: 'https://api.deepseek.com/v1', // OpenAI-compatible protocol; DeepSeek by default
81
+ model: 'deepseek-chat',
82
+ temperature: 0.3, // low temp recommended for large JSON ops
83
+ },
84
+ // [UI form: built-in dialog (default) / drawer mode (dialog.drawer:true) / headless (ui:false)]
85
+ dialog: {
86
+ drawer: true, // drawer mode: right slide-in + mask; close defaults to hide() preserving history
87
+ title: '[Business] Agent',
88
+ placeholder: 'Try: [example operation]',
89
+ },
90
+ // systemPrompt: describe business + data structure; reliableWriteRules auto-appended with '---' separator (default true)
91
+ systemPrompt: 'You are a [business] assistant. Main data = { title, items[] }. To edit, change title or items (add/remove/edit items, adjust fields); [page/UI] updates live. See load_skill("[skill-name]") for fields.',
92
+ appendReliableWriteRules: true, // default true, auto-appends reliable write rules (read-before-write, fields per describe, retry on validation error, prefer incremental patch)
93
+ // data single main object: schema + bind directly bound to object
94
+ data: { schema: mainSchema, bind: mainData, description: '[main data purpose]' },
95
+ // skill: business field docs, Agent load_skill on demand
96
+ skills: [
97
+ defineSkill({
98
+ name: '[skill-name]',
99
+ description: 'Edit [business] data. Use when user requests changes',
100
+ getContent: () => builderSkillContent,
101
+ }),
102
+ ],
103
+ debug: true,
104
+ // onEvent: for non-reactive bind or new-property cases, use data_change to trigger re-render
105
+ onEvent(e) {
106
+ if (e.type === 'data_change') {
107
+ // [non-reactive bind: tick++ to force re-render; reactive bind: not needed or use for audit/联动]
108
+ }
109
+ },
110
+ }).mount()
111
+
112
+ // Drawer mode: call show() to open (hide() to close, preserves history & in-flight generation)
113
+ // sdk.show() / sdk.hide()
114
+ ```
115
+
116
+ ### 4. [Optional] Page render + refresh
117
+
118
+ ```ts
119
+ // Reactive bind (Vue3 reactive / Vue2 data()): Agent write → auto-reactive, no manual refresh
120
+ // Non-reactive bind (plain object): onEvent('data_change') triggers tick, :key="tick" forces component rebuild to read latest bind
121
+ ```
122
+
123
+ ## Options cheat sheet
124
+
125
+ | Option | Value | Purpose |
126
+ |---|---|---|
127
+ | `drawer` | `true` | Drawer mode: right slide-in + mask; close defaults to `hide()` |
128
+ | `ui` | `false` | headless: no dialog, use `sdk.messages` + `send`/`stream` to build your own UI |
129
+ | `data` | `{ schema, bind, description? }` | Main data declaration (key); `bind` directly bound |
130
+ | `systemPrompt` | string | Business description; `reliableWriteRules` auto-appended with `---` |
131
+ | `appendReliableWriteRules` | `true` (default) | Auto-append reliable write rules; set `false` to disable |
132
+ | `skills` | `defineSkill[]` | Business field docs, Agent `load_skill` on demand |
133
+ | `storage` | `'memory'`/`'local'`/... | Persistence (messages/vfs/todos/memory; **does NOT persist bind**) |
134
+ | `llm.temperature` | `0.3` | Low temp recommended for large JSON ops |
135
+ | `onEvent` | `(e) => {}` | Event callback; `data_change` triggers re-render |
136
+ | `checkpoint` | `true` | Session-level rollback (per-round snapshot; one-click restore if broken) |
137
+ | `approval` | `{ tools: ['write'] }` | Human-confirm before write ops (prevent AI mis-edits) |
138
+ | `capabilities` | `{ dataOps:false, fetch:false, ... }` | Turn off unused built-ins to save tokens/size |
139
+
140
+ ## Common pitfalls
141
+
142
+ 1. **bind not persisted**: `storage` persists messages/vfs/todos/memory but **NOT bind**; to restore across refresh, store it yourself + `sdk.setData({ bind: restoredBind })`
143
+ 2. **DeepSeek 400 `missing field tool_call_id`**: handled internally; only relevant for custom tool plumbing (use snake_case)
144
+ 3. **`.env` `VITE_AI_SYSTEM_PROMPT` must be single-line** (dotenv doesn't support multi-line)
145
+ 4. **Large JSON incremental edit**: use `write({ value:180, patch:{ op:'set', jsonPath:'items.0.price' } })` to avoid re-sending the whole blob (truncated by max_tokens)
146
+ 5. **Schema whitelist**: `z.object` auto-enables whitelist (only declared fields exposed); `discriminatedUnion`/`record`/`lazy` non-top-level don't enable (fully open)
147
+ 6. **Vue2 new-property non-reactive**: `write` patch `set` on a new field — Vue2 `Object.defineProperty` won't react → `onEvent('data_change')` `tick++`, use `:key="tick"` to force rebuild
148
+ 7. **MCP cold-start injects 0 tools**: `vite.config.ts` `optimizeDeps.include` pre-declares SDK sub-paths; keep those entries when forking config, else first MCP page load injects nothing (reload fixes)
149
+
150
+ ## Verification checklist
151
+
152
+ - [ ] After `npm i`, `import { createChatSdk, z } from 'page-agent-sdk'` doesn't error
153
+ - [ ] `import 'page-agent-sdk/style.css'` styles load
154
+ - [ ] Agent `read` sees main data structure; `write` patch edits sub-path fields
155
+ - [ ] [Reactive bind] edits → UI reacts; [non-reactive] `data_change` → re-render
156
+ - [ ] [Drawer mode] close then open (`show()`) → history & in-flight generation preserved
157
+ - [ ] Schema validation failure → structured error, no write
158
+
159
+ ## References
160
+
161
+ - `node_modules/page-agent-sdk/skills/page-agent-sdk-integrate/` (integration skill, full docs)
162
+ - `references/quickstart.md` (progressive setup), `references/options.md` (all options), `references/api.md` (API/tools/events)
163
+ - `references/use-cases.md` (10 end-to-end scenarios: low-code/form/CMS/ops/AI-native/research/server-side/multi-agent/MCP/dynamic schema)
164
+ - `references/advanced.md` (custom tools/skills/subagents/MCP/dynamic schema)
165
+ - `node_modules/page-agent-sdk/dist/` (build artifacts)
166
+ - Online: `https://esm.sh/page-agent-sdk@2.9.0` (CDN verify)
167
+
168
+ ## Scenario customization
169
+
170
+ Per your business scenario, fill in `systemPrompt` / `skills` / `data.schema`:
171
+
172
+ - **Low-code page builder**: `data` = component tree; `write` patch jsonPath incremental; `onEvent('data_change')` refresh canvas; `checkpoint` + `approval`
173
+ - **Form designer**: `data` = field definitions (enum/required); schema validation prevents malformed forms
174
+ - **CMS batch ops**: `eval_script` loops; `search_data` filter; `write` patch targeted edits
175
+ - **Ops config console**: `approval:{tools:['write']}` human-confirm; `capabilities.verify:true` write-back read; `checkpoint`
176
+ - **AI-native assistant**: `capabilities:{dataOps:false,fetch:false}` + custom `tools` (your product API)
177
+ - **Multi-agent on one page**: same `id` + `shareContext:true` → multiple dialogs share one `AgentCore`; or independent `id`s + `dialog.drawer:true` + `hide`/`show` for exclusive switching
178
+ - **MCP integration**: `mcp:[{transport,url}]` remote tool servers; `@modelcontextprotocol/sdk` optional peerDep
179
+ - **Dynamic/lazy-loaded schema**: `sdk.setData({ schema, bind })` on component mount to swap main data; tools pick up immediately, no rebuild
@@ -10,7 +10,6 @@ Full reference for `createChatSdk(options)`. Grouped by purpose. Required: `llm`
10
10
  | `systemPrompt` | `string` | built-in default (JSON-operation assistant + reliable write rules) | Agent identity/instructions. Inject here, not hardcoded. Keep single-line in `.env` (`VITE_AI_SYSTEM_PROMPT`). If omitted, a built-in default is used (JSON-operation assistant + `systemPromptHelpers.reliableWriteRules`); passing your own fully overrides it. |
11
11
  | `appendReliableWriteRules` | `boolean` | `true` | When `true` (default) and a custom `systemPrompt` is set, auto-append `systemPromptHelpers.reliableWriteRules` to it with a `---` separator (clearly distinguishes user content from SDK-appended write rules; avoids forgetting the write rules). Set `false` to disable. No effect when `systemPrompt` is omitted (default prompt already includes them). |
12
12
  | `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. |
13
- | `title` / `placeholder` | `string` | — | Dialog title / input placeholder (cosmetic). |
14
13
 
15
14
  ## UI & mounting
16
15
 
@@ -19,6 +18,19 @@ Full reference for `createChatSdk(options)`. Grouped by purpose. Required: `llm`
19
18
  | `container` | `string \| HTMLElement` | — | Where the built-in dialog mounts. Required when `ui !== false`. |
20
19
  | `ui` | `boolean \| 'default'` | `true` | `false` = headless (no built-in dialog; you build UI from `sdk.messages` + `sdk.send`). `'default'` = built-in `ChatDialog`. |
21
20
  | `streaming` | `boolean` | `true` | Stream tokens live. `false` = wait for full reply. Headless `sdk.send` always uses invoke (no stream events, but data/message/error still fire). |
21
+ | `dialog` | `DialogConfig` | — | Grouped dialog UI config (recommended form). See `DialogConfig` fields below. |
22
+
23
+ ### `DialogConfig` fields
24
+
25
+ | Field | Type | Default | Purpose / when |
26
+ |---|---|---|---|
27
+ | `title` | `string` | — | Dialog title (cosmetic). |
28
+ | `placeholder` | `string` | — | Input placeholder (cosmetic). |
29
+ | `drawer` | `boolean` | `false` | Drawer mode: ChatDialog slides in from the right + mask + close button (replaces the collapse arrow). Default `false` (inline, fills container). |
30
+ | `drawerWidth` | `number \| string` | `420` | Drawer mode width (pixels or CSS string, e.g. `500` / `'500px'` / `'40vw'`). Only effective when `drawer: true`. Inline mode width is determined by `container`. |
31
+ | `drawerHidden` | `boolean` | `false` | Drawer mode hidden by default (not shown after `mount`; requires `sdk.show()` to display): for "click button to show chatbox" scenarios. Only effective when `drawer: true`. |
32
+ | `inputRows` | `number` | `2` | Input box rows (visible height). `1` = single row; `2` = 2-row initial height, auto-expands up to max-height:100px; `>2` = taller initial height. |
33
+ | `onClose` | `() => void` | — | Drawer mode close callback (called when mask/close button clicked). Default: `hide()` in drawer mode (keeps agent/history/in-flight generation), `unmount()` otherwise. Pass this to sync external mount state. |
22
34
 
23
35
  ## Data operation (the core)
24
36
 
@@ -39,6 +51,7 @@ Full reference for `createChatSdk(options)`. Grouped by purpose. Required: `llm`
39
51
  |---|---|---|---|
40
52
  | `tools` | `Tool[]` | `[]` | Custom tools beyond built-ins. Use `defineTool({ name, description, schema, handler })`. |
41
53
  | `skills` | `SkillSpec[]` | `[]` | Progressive-disclosure skills (`defineSkill({ name, description, prompt }`) loaded on demand by the agent. |
54
+ | `skillStorage` | `SkillStoreConfig \| false` | `{ backend: 'indexed' }` | **User-created skill independent persistence** (separate from `storage`). Default indexedDB (persists even when `storage: false`); `false` disables persistence (current session only). `id` manually specifies the same id to share skills across pages/agents; omit for per-agent isolation. |
42
55
  | `memory` | `string` | — | AGENTS.md-style persistent instructions injected into every prompt (project conventions, hard rules). |
43
56
  | `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. |
44
57
 
@@ -124,13 +137,28 @@ Full reference for `createChatSdk(options)`. Grouped by purpose. Required: `llm`
124
137
 
125
138
  Runtime subscription via `sdk.hook(handler) => () => void` (multi-listener, cancellable) — see [api.md](api.md).
126
139
 
127
- ## Convenience APIs (export / import / usage)
140
+ ## Convenience APIs (export / import / usage / user skills)
128
141
 
129
142
  | API | Purpose | Notes |
130
143
  |---|---|---|
131
144
  | `sdk.exportData()` | Deep copy of main data `bind` (backup/migration) | Returns `null` if dataOps off / no data; mutating return does not affect original bind |
132
145
  | `sdk.importData(json, opts?)` | Replace `bind` entirely (in-place, preserves reactive ref) | Schema-validated by default → `{ok:false,error}` if invalid; `opts.validate:false` skips; `opts.emit:false` suppresses `data_change` |
133
146
  | `sdk.usage` | Cumulative token usage `{prompt_tokens, completion_tokens, total_tokens}` | Accumulated per LLM call; per-round detail via `onEvent('usage')` |
147
+ | `sdk.addSkill(skill)` | Add a user-created skill at runtime | `skill: { name, description, prompt \| getContent \| doc }`. Auto-merges into the skill list, **persists via independent SkillStore** (default indexedDB, separate from `storage` option — even `storage:false` persists skills), and takes effect next round. Same-name overwrites. Requires `capabilities.skills` (default on) + `skillStorage` not `false` for persistence. |
148
+ | `sdk.removeSkill(name)` | Remove a user-created skill by name | Only removes user-created skills (not the ones passed via `skills` option at init). Removes from SkillStore. No-op if not found. |
149
+ | `sdk.listUserSkills()` | List names of user-created skills | Returns `string[]` (only user-created, not init-time skills). Useful for UI panels. |
150
+ | `sdk.getUserSkill(name)` | Read a user-created skill's detail | Returns `{ name, description, content }` or `undefined` if not found. Used by SkillPanel for editing. |
151
+
152
+ **User-created skills** are skills added at runtime via `sdk.addSkill` (or via the built-in `SkillPanel` UI component). They are persisted via an **independent SkillStore** (default indexedDB), **separate from the `storage` option** — even when `storage: false` (session persistence off), user skills still persist across refreshes. To **share the same set of user skills across pages/agents**, manually specify the same `skillStorage.id`:
153
+
154
+ ```ts
155
+ // Page A
156
+ createChatSdk({ id: 'agent-a', skillStorage: { id: 'shared-skills' }, ... })
157
+ // Page B (different agent, same skill set)
158
+ createChatSdk({ id: 'agent-b', skillStorage: { id: 'shared-skills' }, ... })
159
+ ```
160
+
161
+ Without `skillStorage.id`, skills default to per-agent isolation (`agent::{agentId}`). The built-in `ChatDialog` exposes a "Skill Management" button (header) that opens `SkillPanel` — supports create, **edit** (click a skill to load into the form), and delete. Integrators can also `import { SkillPanel }` directly for custom UIs.
134
162
 
135
163
  ## vfs (in-memory workspace)
136
164
 
@@ -275,9 +275,9 @@ const boxB = document.createElement('div'); document.body.appendChild(boxB)
275
275
  const boxC = document.createElement('div'); document.body.appendChild(boxC)
276
276
 
277
277
  const agents = [
278
- createChatSdk({ id: 'agent-page', container: boxA, drawer: true, storage: 'memory', llm: LLM, data: { schema: pageSchema, bind: pageObj }, systemPrompt: '页面构建助手…' }),
279
- createChatSdk({ id: 'agent-copy', container: boxB, drawer: true, storage: 'memory', llm: LLM, data: { schema: copySchema, bind: copyObj }, systemPrompt: '文案优化助手…' }),
280
- createChatSdk({ id: 'agent-stats', container: boxC, drawer: true, storage: 'memory', llm: LLM, data: { schema: statsSchema, bind: statsObj }, systemPrompt: '数据分析助手…' }),
278
+ createChatSdk({ id: 'agent-page', container: boxA, dialog: { drawer: true }, storage: 'memory', llm: LLM, data: { schema: pageSchema, bind: pageObj }, systemPrompt: '页面构建助手…' }),
279
+ createChatSdk({ id: 'agent-copy', container: boxB, dialog: { drawer: true }, storage: 'memory', llm: LLM, data: { schema: copySchema, bind: copyObj }, systemPrompt: '文案优化助手…' }),
280
+ createChatSdk({ id: 'agent-stats', container: boxC, dialog: { drawer: true }, storage: 'memory', llm: LLM, data: { schema: statsSchema, bind: statsObj }, systemPrompt: '数据分析助手…' }),
281
281
  ]
282
282
  await Promise.all(agents.map(a => a.mount())) // three independent agents ready in parallel
283
283
  agents.slice(1).forEach(a => a.hide()) // show only the first initially
package/types/index.d.ts CHANGED
@@ -137,6 +137,7 @@ export interface McpServerConfig { transport: 'http' | 'sse' | 'websocket'; url:
137
137
  export declare const ChatDialog: DefineComponent<ChatDialogProps>;
138
138
  export declare const MessageContent: DefineComponent<any>;
139
139
  export declare const CodePreview: DefineComponent<any>;
140
+ export declare const SkillPanel: DefineComponent<any>;
140
141
  export declare function useChat(opts?: any): any;
141
142
 
142
143
  // ===== 框架无关 SDK(页面内 Agent)=====
@@ -301,6 +302,15 @@ export interface StorageConfig {
301
302
  evictionWatermark?: number;
302
303
  debounceMs?: number;
303
304
  }
305
+ /** Skill 独立持久化存储配置(与 storage 选项分离) */
306
+ export interface SkillStoreConfig {
307
+ /** 存储 id(命名空间)。手动指定同一 id 即可跨页面/跨 agent 复用同一套用户 skill;不传默认按 agentId 隔离 */
308
+ id?: string;
309
+ /** 后端类型,默认 'indexed'(大容量、跨刷新);'local' 跨页持久;'session' 刷新保留关页清;'memory' 纯内存降级 */
310
+ backend?: StorageBackendType;
311
+ /** DB 命名空间,默认 'chat-sdk'(与 SessionStore 同库,不同 key 前缀) */
312
+ dbName?: string;
313
+ }
304
314
  export interface SessionMeta {
305
315
  agentId: string;
306
316
  sessionId: string;
@@ -362,6 +372,8 @@ export interface ChatSdkOptions {
362
372
  appendReliableWriteRules?: boolean;
363
373
  tools?: any[];
364
374
  skills?: SkillSpec[];
375
+ /** 用户创建 skill 的独立持久化存储(与 storage 选项分离)。默认 `{ backend: 'indexed' }`(即使 storage:false 也持久化);`false` 关闭;`id` 手动指定同一 id 可跨页面/跨 agent 复用 */
376
+ skillStorage?: SkillStoreConfig | false;
365
377
  memory?: string;
366
378
  data?: DataConfig;
367
379
  permissions?: PermissionRule[];
@@ -430,11 +442,19 @@ export interface ChatSdkOptions {
430
442
  onEvent?: SdkEventHandler;
431
443
  /** 流式输出(默认 true);false 时等整段回复再显示 */
432
444
  streaming?: boolean;
445
+ /** Dialog UI config (title/placeholder/drawer/drawerWidth/drawerHidden/inputRows/onClose grouped) */
446
+ dialog?: DialogConfig;
447
+ }
448
+
449
+ /** Dialog UI config (grouped form, recommended) */
450
+ export interface DialogConfig {
433
451
  title?: string;
434
452
  placeholder?: string;
435
- /** Drawer mode: ChatDialog slides in from the right + mask + close button (replaces the collapse arrow); clicking mask/close triggers unmount (with exit animation). Default false (inline, fills container). */
436
453
  drawer?: boolean;
437
- /** Drawer mode close callback: called when mask/close button clicked (default calls unmount with exit animation). Pass this to sync external mount state. */
454
+ drawerWidth?: number | string;
455
+ drawerHidden?: boolean;
456
+ /** Input box rows (visible height); default 2 (2-row initial height, auto-expands up to max-height:100px). 1 = single row; >2 = taller. */
457
+ inputRows?: number;
438
458
  onClose?: () => void;
439
459
  }
440
460
 
@@ -467,6 +487,14 @@ export interface ChatSdk {
467
487
  * 清空 skill 全文缓存与本轮已加载记录,下次 load_skill 重新取最新全文(含 vfs doc)。需开启 skills(默认开)
468
488
  */
469
489
  setSkills(skills: SkillSpec[]): void;
490
+ /** 添加用户创建的 skill(持久化,跨刷新恢复;同名覆盖)。需开启 skills(默认开) */
491
+ addSkill(skill: SkillSpec): void;
492
+ /** 删除用户创建的 skill(仅删用户创建的,不删集成方 initialSkills)。返回是否删除成功 */
493
+ removeSkill(name: string): boolean;
494
+ /** 列出用户创建的 skill 名(仅用户创建的,不含集成方 initialSkills) */
495
+ listUserSkills(): string[];
496
+ /** 读取用户创建的 skill 详情(返回 {name, description, content};不存在返回 undefined) */
497
+ getUserSkill(name: string): { name: string; description: string; content: string } | undefined;
470
498
  /**
471
499
  * 清 skill 全文缓存(动态 skill 内容变化时主动失效)。不传 name 清全部;传 name 清指定。
472
500
  * 下次 load_skill 重新 getContent/readSkillDoc 取最新。需开启 skills(默认开)
@@ -545,6 +573,23 @@ export declare function createSessionStore(config?: StorageConfig): SessionStore
545
573
  export declare function createMemoryBackend(): StorageBackend;
546
574
  export declare function createWebStorageBackend(storage: Storage): StorageBackend;
547
575
  export declare function isQuotaError(err: unknown): boolean;
576
+ /** 创建 Skill 独立持久化存储(与 storage 选项分离;默认 indexedDB,可手动指定 id 跨页复用) */
577
+ export declare function createSkillStore(config?: SkillStoreConfig): SkillStore;
578
+ export interface SkillStore {
579
+ ready: Promise<boolean>;
580
+ list(): Promise<PersistedSkill[]>;
581
+ get(name: string): Promise<PersistedSkill | undefined>;
582
+ put(skill: PersistedSkill): Promise<void>;
583
+ remove(name: string): Promise<boolean>;
584
+ clear(): Promise<void>;
585
+ dispose(): void;
586
+ }
587
+ /** 持久化的用户创建 skill(getContent 函数不可序列化,故 content 直接存字符串) */
588
+ export interface PersistedSkill {
589
+ name: string;
590
+ description: string;
591
+ content: string;
592
+ }
548
593
 
549
594
  // ============ 大 JSON 查询/搜索/沙箱脚本(dataSlotQuery)============
550
595
  export interface JpNode {