page-agent-sdk 2.9.1 → 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.1",
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",
@@ -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
 
@@ -81,8 +81,12 @@ const sdk = createChatSdk({
81
81
  model: 'deepseek-chat',
82
82
  temperature: 0.3, // low temp recommended for large JSON ops
83
83
  },
84
- // [UI form: built-in dialog (default) / drawer mode (drawer:true) / headless (ui:false)]
85
- drawer: true, // drawer mode: right slide-in + mask; close defaults to hide() preserving history
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
+ },
86
90
  // systemPrompt: describe business + data structure; reliableWriteRules auto-appended with '---' separator (default true)
87
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.',
88
92
  appendReliableWriteRules: true, // default true, auto-appends reliable write rules (read-before-write, fields per describe, retry on validation error, prefer incremental patch)
@@ -97,8 +101,6 @@ const sdk = createChatSdk({
97
101
  }),
98
102
  ],
99
103
  debug: true,
100
- title: '[Business] Agent',
101
- placeholder: 'Try: [example operation]',
102
104
  // onEvent: for non-reactive bind or new-property cases, use data_change to trigger re-render
103
105
  onEvent(e) {
104
106
  if (e.type === 'data_change') {
@@ -172,6 +174,6 @@ Per your business scenario, fill in `systemPrompt` / `skills` / `data.schema`:
172
174
  - **CMS batch ops**: `eval_script` loops; `search_data` filter; `write` patch targeted edits
173
175
  - **Ops config console**: `approval:{tools:['write']}` human-confirm; `capabilities.verify:true` write-back read; `checkpoint`
174
176
  - **AI-native assistant**: `capabilities:{dataOps:false,fetch:false}` + custom `tools` (your product API)
175
- - **Multi-agent on one page**: same `id` + `shareContext:true` → multiple dialogs share one `AgentCore`; or independent `id`s + `drawer:true` + `hide`/`show` for exclusive switching
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
176
178
  - **MCP integration**: `mcp:[{transport,url}]` remote tool servers; `@modelcontextprotocol/sdk` optional peerDep
177
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 {