page-agent-sdk 2.5.1 → 2.6.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.
@@ -17141,7 +17141,7 @@ function Ty(e, r) {
17141
17141
  getTodos: () => g.current?.getState?.()?.todos ?? [],
17142
17142
  messages: f,
17143
17143
  maxCheckpoints: v && typeof v == "object" ? v.maxCheckpoints : void 0
17144
- }) : null, x = !v || typeof v != "object" || v.auto !== !1, S = e.capabilities, C = S?.dataOps !== !1, w = (e.systemPrompt ?? hy) + gy(_), T = C && _ ? Iv(_, {
17144
+ }) : null, x = !v || typeof v != "object" || v.auto !== !1, S = e.capabilities, C = S?.dataOps !== !1, w = (e.systemPrompt ? e.appendReliableWriteRules ? e.systemPrompt + "\n\n" + L_.reliableWriteRules : e.systemPrompt : hy) + gy(_), T = C && _ ? Iv(_, {
17145
17145
  onAudit: e.debug ? (e) => console.log("[page-agent-sdk][data audit]", e) : void 0,
17146
17146
  maxSnapshots: e.maxSnapshots,
17147
17147
  onConflict: o,
@@ -149,6 +149,8 @@ self.onmessage = async (e) => {
149
149
  `):void 0}}}function Vv(){try{if(typeof crypto<`u`&&typeof crypto.randomUUID==`function`)return crypto.randomUUID()}catch{}return Date.now().toString(36)+Math.random().toString(36).slice(2,10)}var Hv=`chat-sdk`,Uv=50*1024*1024,Wv=4*1024*1024,Gv=10*1024*1024,Kv=.9,qv=500,Jv=300,Yv=`__meta__`,Xv=`v:1`,Zv=[`messages`,`vfs`,`todos`,`memory`];function Qv(e){return e===`local`||e===`session`?Wv:Uv}function $v(e){if(typeof e!=`object`||!e)return!1;let t=e;return t.name===`QuotaExceededError`||t.name===`NS_ERROR_DOM_QUOTA_REACHED`||t.code===22||t.code===1014}function ey(e,t,n,r){return[Xv,e,t,n,r].join(`::`)}function ty(e,t,n){return[Xv,e,t,n].join(`::`)+`::`}function ny(e,t){return[Xv,e,t].join(`::`)+`::`}function ry(e){return[Xv,e].join(`::`)+`::`}var iy=null;function ay(e){iy||=new TextEncoder;try{return iy.encode(JSON.stringify(e)).length}catch{return 0}}function oy(e,t,n){let r=e.reduce((e,t)=>e+t.bytes,0);if(r<=t)return[];let i=t*n,a=[...e].sort((e,t)=>e.lastAccessed-t.lastAccessed),o=[];for(let e of a){if(r<=i)break;o.push(e),r-=e.bytes}return o}function sy(){let e=new Map;return{async get(t){return e.has(t)?e.get(t):void 0},async set(t,n){e.set(t,n)},async del(t){e.delete(t)},async scan(t,n){for(let r of Array.from(e.keys()).sort())if(r.startsWith(t)&&n(r,e.get(r))===!1)return},async clearPrefix(t){for(let n of Array.from(e.keys()))n.startsWith(t)&&e.delete(n)}}}function cy(e){return new Promise((t,n)=>{if(typeof indexedDB>`u`){n(Error(`indexedDB 不可用`));return}let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{let e=r.result;e.objectStoreNames.contains(`kv`)||e.createObjectStore(`kv`)},r.onblocked=()=>n(Error(`indexedDB 升级被阻塞,请关闭其他标签页后重试`)),r.onsuccess=()=>{let e=r.result;e.onversionchange=()=>{e.close()},t({get(t){return new Promise((n,r)=>{let i=e.transaction(`kv`,`readonly`).objectStore(`kv`).get(t);i.onsuccess=()=>n(i.result),i.onerror=()=>r(i.error)})},set(t,n){return new Promise((r,i)=>{let a=e.transaction(`kv`,`readwrite`);a.objectStore(`kv`).put(n,t),a.oncomplete=()=>r(),a.onerror=()=>i(a.error),a.onabort=()=>i(a.error)})},del(t){return new Promise((n,r)=>{let i=e.transaction(`kv`,`readwrite`);i.objectStore(`kv`).delete(t),i.oncomplete=()=>n(),i.onerror=()=>r(i.error),i.onabort=()=>r(i.error)})},scan(t,n){return new Promise((r,i)=>{let a=!1,o=e.transaction(`kv`,`readonly`),s=IDBKeyRange.bound(t,t+`￿`),c=o.objectStore(`kv`).openCursor(s);c.onsuccess=()=>{let e=c.result;if(!e||a)return;let r=e.key;if(r.startsWith(t)&&n(r,e.value)===!1){a=!0;return}e.continue()},c.onerror=()=>i(c.error),o.oncomplete=()=>r(),o.onerror=()=>i(o.error),o.onabort=()=>i(o.error)})},clearPrefix(t){return new Promise((n,r)=>{let i=e.transaction(`kv`,`readwrite`),a=IDBKeyRange.bound(t,t+`￿`),o=i.objectStore(`kv`).openCursor(a);o.onsuccess=()=>{let e=o.result;e&&(e.key.startsWith(t)&&e.delete(),e.continue())},o.onerror=()=>r(o.error),i.oncomplete=()=>n(),i.onerror=()=>r(i.error),i.onabort=()=>r(i.error)})}})},r.onerror=()=>n(r.error)})}function ly(e){return{async get(t){let n=e.getItem(t);return n==null?void 0:JSON.parse(n)},async set(t,n){e.setItem(t,JSON.stringify(n))},async del(t){e.removeItem(t)},async scan(t,n){let r=[];for(let n=0;n<e.length;n++){let i=e.key(n);i&&i.startsWith(t)&&r.push(i)}r.sort();for(let t of r){let r=e.getItem(t);if(r!=null&&n(t,JSON.parse(r))===!1)return}},async clearPrefix(t){let n=[];for(let r=0;r<e.length;r++){let i=e.key(r);i&&i.startsWith(t)&&n.push(i)}for(let t of n)e.removeItem(t)}}}function uy(e,t,n){let r=(e.get(t)??Promise.resolve()).then(n,n);return e.set(t,r.then(()=>void 0,()=>void 0)),r}function dy(e={}){let t=e.dbName??Hv,n=e.backend??`indexed`,r=e.maxBytes??Qv(n),i=e.maxBytesPerSession??Gv,a=e.evictionWatermark??Kv,o=e.debounceMs??qv,s=new Set;function c(e){for(let t of s)try{t(e)}catch{}}let l,u=!1,d,f=new Promise(e=>{d=e});(async()=>{try{if(n===`indexed`){if(typeof indexedDB>`u`)throw Error(`indexedDB 不可用`);l=await cy(t),d(!0)}else if(n===`session`){if(typeof sessionStorage>`u`)throw Error(`sessionStorage 不可用`);l=ly(sessionStorage),d(!0)}else if(n===`local`){if(typeof localStorage>`u`)throw Error(`localStorage 不可用`);l=ly(localStorage),d(!0)}else l=sy(),d(!1)}catch(e){l=sy();let t=e instanceof Error?e.message:String(e);d(!1),Promise.resolve().then(()=>c({type:`degraded`,reason:t}))}})();let p=new Map,m=new Map,h=new Map;function g(e,n,r,a){let o=ey(t,r,a,Yv);return uy(h,o,async()=>{let t=async()=>{let t=await l.get(o),s=await l.get(e),u=s==null?0:ay(s),d=ay(n),f=(t?.bytes??0)+(d-u);if(f>i){c({type:`quota`,sessionBytes:f,limit:i});return}await l.set(e,n);let p=Date.now(),m=t??{agentId:r,sessionId:a,createdAt:p,lastAccessed:p,bytes:0};m.bytes+=d-u,m.lastAccessed=p,await l.set(o,m),y()};try{await t()}catch(e){if($v(e)&&!u){u=!0;try{await b()}catch{}l=sy(),Promise.resolve().then(()=>c({type:`degraded`,reason:`存储配额超限(QuotaExceededError),已淘汰最旧会话并降级内存`}));try{await t()}catch{}}}})}function _(e,n,r,i){let a=ey(t,e,n,r),s=p.get(a);s&&s.resolve();let c=m.get(a);return c&&clearTimeout(c),new Promise(t=>{p.set(a,{value:i,agentId:e,sessionId:n,kind:r,resolve:t});let s=setTimeout(()=>{m.delete(a);let e=p.get(a);if(p.delete(a),!e){t();return}g(a,e.value,e.agentId,e.sessionId).then(t,t)},o);m.set(a,s)})}let v=null;function y(){v||=setTimeout(()=>{v=null,b()},Jv)}async function b(){let e=[];await l.scan(ry(t),(t,n)=>{t.endsWith(`::__meta__`)&&e.push(n)});let n=oy(e,r,a);for(let e of n)await l.clearPrefix(ty(t,e.agentId,e.sessionId)),c({type:`evicted`,agentId:e.agentId,sessionId:e.sessionId,bytes:e.bytes})}return{ready:f,async listSessions(e){let n=[];return await l.scan(ny(t,e),(e,t)=>{e.endsWith(`::__meta__`)&&n.push(t)}),n.sort((e,t)=>t.lastAccessed-e.lastAccessed)},async load(e,n){let r=ey(t,e,n,Yv);return await uy(h,r,async()=>{let i=await l.get(r);if(!i)return;let a={messages:[],vfs:{},todos:[],memory:``};for(let r of Zv){let i=await l.get(ey(t,e,n,r));i!=null&&(a[r]=i)}return i.lastAccessed=Date.now(),await l.set(r,i),a})},async save(e,t,n){let r=[];for(let i of Zv)n[i]!==void 0&&r.push(_(e,t,i,n[i]));await Promise.all(r)},async flush(){for(let e of Array.from(m.keys())){let t=m.get(e);t&&(clearTimeout(t),m.delete(e))}let e=Array.from(p.entries());p.clear(),await Promise.all(e.map(async([e,t])=>{await g(e,t.value,t.agentId,t.sessionId),t.resolve()})),c({type:`flush`}),await b()},async deleteSession(e,n){await l.clearPrefix(ty(t,e,n))},async createSession(e,n,r){let i=r??Vv(),a=Date.now(),o={agentId:e,sessionId:i,createdAt:a,lastAccessed:a,bytes:0,title:n};return await l.set(ey(t,e,i,Yv),o),i},onEvent(e){s.add(e)},dispose(){for(let e of m.values())clearTimeout(e);m.clear();for(let e of p.values())e.resolve();p.clear(),v&&=(clearTimeout(v),null),h.clear(),s.clear()}}}var fy=[`你是一个 JSON 操作助手。集成方声明了一个主数据对象(含 zod schema 校验),你通过专用工具安全地读写它来完成任务。`,`所有写操作都经范围控制(仅 schema 声明字段内)与 schema 校验(不合法会返回结构化错误而非写入),并自动留快照可回退。`,`大对象/数组优先用增量 patch(只发改动)而非整体重传,避免输出被截断。`,P_.reliableWriteRules].join(`
150
150
 
151
151
  `);function py(e){if(!e)return``;let t=F_(e.schema);return`\n\n## 可操作数据(字段以 read 工具返回的实际值为准)\n${e.description?e.description+`
152
- `:``}${t}`}var my=50;function hy(e){return e===void 0||e===!1?null:typeof e==`string`?dy({backend:e}):e.enabled===!1?null:dy(e)}function gy(e){return!!e&&typeof e==`object`&&typeof e.invoke==`function`&&typeof e.stream==`function`}var _y=new Map;function vy(e){let t=e.content;return typeof t==`string`?t:Array.isArray(t)?t.map(e=>typeof e==`string`?e:e?.text??``).join(``):String(t??``)}function yy(e){let t=e.summaryLlm??e.llm;if(!t)return;let n=e.summaryTemperature??.3,a=e.summaryMaxTokens??1024,o=e.summaryTimeoutMs??15e3,s;if(gy(t))s=t;else{let r=t;if(!r.apiKey){e.summaryLlm&&console.warn(`[page-agent-sdk][summarization] summaryLlm 已配置但缺 apiKey,摘要回退主 agent 模型或零成本索引摘要`);return}s=new i.ChatOpenAI({apiKey:r.apiKey,model:r.model,temperature:n,maxTokens:a,configuration:r.baseUrl?{baseURL:r.baseUrl}:void 0})}return async e=>{let t=new AbortController,n=setTimeout(()=>t.abort(),o);try{return vy(await s.invoke([new r.SystemMessage(`你是对话历史压缩助手。把下面按轮次索引的对话要点,改写成一段连贯、紧凑的中文摘要,保留关键事实、用户意图与已用工具,不要编造。直接输出摘要正文。`),new r.HumanMessage(e)],{signal:t.signal})).trim()}finally{clearTimeout(n)}}}function by(e,t){return e===`set_data`?`set`:e===`edit_data`?`edit`:e===`delete_data`?`delete`:e===`restore_data`?`restore`:e===`write`?t?.del?`delete`:t?.patch?`edit`:`set`:null}function xy(e,t,n){return{name:`sdk-events`,wrapToolCall:async(t,r)=>{let i=await r(t),a=by(t.name,t.args);return a&&e({type:`data_change`,operation:a,value:n()?.bind}),i},afterAgent:async()=>{e({type:`message_update`,count:t.length})}}}function Sy(e,r){let i=ln(null),a=0;function o(e){return new Promise(t=>{let n=i.value;n&&n.resolve({action:`keep_external`});let r={...e,id:++a,resolve:t};i.value=r,me({type:`conflict`,conflict:r})})}function s(e){let t=i.value;t&&(i.value=null,t.resolve({action:e}))}let c=hy(e.storage);e.debug&&c&&c.onEvent(e=>console.log(`[page-agent-sdk][storage]`,e));let l=gy(e.llm)?void 0:e.llm,u=$h({model:l?.model,contextWindow:e.contextWindow??l?.contextWindow,maxOutputTokens:e.maxOutputTokens??l?.maxOutputTokens});e.debug&&console.log(`[page-agent-sdk][modelCaps]`,u);let d=yy(e);e.debug&&!d&&console.warn(`[page-agent-sdk][summarization] 未构造 llmInvoke(apiKey 缺失?),摘要回退零成本索引摘要`);let f=Yt([]),p=H_(e.vfs?.initialFiles,{persist:c?{save:e=>{B.sessionId&&c&&c.save(r,B.sessionId,{vfs:e})}}:void 0,maxBytes:e.vfs?.maxBytes}),m=yg(),h=Dg(e.memory||``),g={current:null},_=e.data?{...e.data,description:e.data.description??`主数据对象`}:void 0,v=e.checkpoint,y=v!==void 0&&v!==!1,b=y?Wg({getData:()=>O()?.bind,slotPaths:_?[``]:[],vfsStore:p,todosMw:m,getTodos:()=>g.current?.getState?.()?.todos??[],messages:f,maxCheckpoints:v&&typeof v==`object`?v.maxCheckpoints:void 0}):null,x=!v||typeof v!=`object`||v.auto!==!1,S=e.capabilities,C=S?.dataOps!==!1,w=(e.systemPrompt??fy)+py(_),T=C&&_?Nv(_,{onAudit:e.debug?e=>console.log(`[page-agent-sdk][data audit]`,e):void 0,maxSnapshots:e.maxSnapshots,onConflict:o,autoLock:e.autoLock,interceptors:e.interceptors}):[],E=C?Mv(T,e.toolMode):[],D=C&&_?T.controller??null:null,O=()=>D?.get()??_,k=new Map,A=Rv(S,E,Fv);A.forEach(e=>k.set(e.name,`builtin`));let j=[...e.tools||[]];j.forEach(e=>k.set(e.name,`user`));let M=e.humanConfirm!==!1&&(!e.approval||e.approval.humanConfirmTool!==!1),ee=M?Ig():null;ee&&k.set(Pg,`builtin`);let N=y&&b?[(0,n.tool)(async()=>{let e=b.list();return e.length?b.restore()?`已回退到最近一次正常状态(checkpoint #${e[e.length-1].id})。对话历史、主数据、vfs、todos 已整体还原。请基于回退后的状态重新判断并继续。`:`回退失败:无可用 checkpoint。`:`无可用 checkpoint,无法回退。`},{name:`restore_last_checkpoint`,description:`回退到最近一次正常状态(整体还原对话历史 + 主数据 + vfs + todos)。当本轮操作出错、页面被改坏、或走偏时调用,回到本轮起点重新来过。不传参数即回退最近一次。`,schema:t.z.object({}).optional()}),(0,n.tool)(async()=>{let e=b.list();return e.length?`可用 checkpoint:
152
+ `:``}${t}`}var my=50;function hy(e){return e===void 0||e===!1?null:typeof e==`string`?dy({backend:e}):e.enabled===!1?null:dy(e)}function gy(e){return!!e&&typeof e==`object`&&typeof e.invoke==`function`&&typeof e.stream==`function`}var _y=new Map;function vy(e){let t=e.content;return typeof t==`string`?t:Array.isArray(t)?t.map(e=>typeof e==`string`?e:e?.text??``).join(``):String(t??``)}function yy(e){let t=e.summaryLlm??e.llm;if(!t)return;let n=e.summaryTemperature??.3,a=e.summaryMaxTokens??1024,o=e.summaryTimeoutMs??15e3,s;if(gy(t))s=t;else{let r=t;if(!r.apiKey){e.summaryLlm&&console.warn(`[page-agent-sdk][summarization] summaryLlm 已配置但缺 apiKey,摘要回退主 agent 模型或零成本索引摘要`);return}s=new i.ChatOpenAI({apiKey:r.apiKey,model:r.model,temperature:n,maxTokens:a,configuration:r.baseUrl?{baseURL:r.baseUrl}:void 0})}return async e=>{let t=new AbortController,n=setTimeout(()=>t.abort(),o);try{return vy(await s.invoke([new r.SystemMessage(`你是对话历史压缩助手。把下面按轮次索引的对话要点,改写成一段连贯、紧凑的中文摘要,保留关键事实、用户意图与已用工具,不要编造。直接输出摘要正文。`),new r.HumanMessage(e)],{signal:t.signal})).trim()}finally{clearTimeout(n)}}}function by(e,t){return e===`set_data`?`set`:e===`edit_data`?`edit`:e===`delete_data`?`delete`:e===`restore_data`?`restore`:e===`write`?t?.del?`delete`:t?.patch?`edit`:`set`:null}function xy(e,t,n){return{name:`sdk-events`,wrapToolCall:async(t,r)=>{let i=await r(t),a=by(t.name,t.args);return a&&e({type:`data_change`,operation:a,value:n()?.bind}),i},afterAgent:async()=>{e({type:`message_update`,count:t.length})}}}function Sy(e,r){let i=ln(null),a=0;function o(e){return new Promise(t=>{let n=i.value;n&&n.resolve({action:`keep_external`});let r={...e,id:++a,resolve:t};i.value=r,me({type:`conflict`,conflict:r})})}function s(e){let t=i.value;t&&(i.value=null,t.resolve({action:e}))}let c=hy(e.storage);e.debug&&c&&c.onEvent(e=>console.log(`[page-agent-sdk][storage]`,e));let l=gy(e.llm)?void 0:e.llm,u=$h({model:l?.model,contextWindow:e.contextWindow??l?.contextWindow,maxOutputTokens:e.maxOutputTokens??l?.maxOutputTokens});e.debug&&console.log(`[page-agent-sdk][modelCaps]`,u);let d=yy(e);e.debug&&!d&&console.warn(`[page-agent-sdk][summarization] 未构造 llmInvoke(apiKey 缺失?),摘要回退零成本索引摘要`);let f=Yt([]),p=H_(e.vfs?.initialFiles,{persist:c?{save:e=>{B.sessionId&&c&&c.save(r,B.sessionId,{vfs:e})}}:void 0,maxBytes:e.vfs?.maxBytes}),m=yg(),h=Dg(e.memory||``),g={current:null},_=e.data?{...e.data,description:e.data.description??`主数据对象`}:void 0,v=e.checkpoint,y=v!==void 0&&v!==!1,b=y?Wg({getData:()=>O()?.bind,slotPaths:_?[``]:[],vfsStore:p,todosMw:m,getTodos:()=>g.current?.getState?.()?.todos??[],messages:f,maxCheckpoints:v&&typeof v==`object`?v.maxCheckpoints:void 0}):null,x=!v||typeof v!=`object`||v.auto!==!1,S=e.capabilities,C=S?.dataOps!==!1,w=(e.systemPrompt?e.appendReliableWriteRules?e.systemPrompt+`
153
+
154
+ `+P_.reliableWriteRules:e.systemPrompt:fy)+py(_),T=C&&_?Nv(_,{onAudit:e.debug?e=>console.log(`[page-agent-sdk][data audit]`,e):void 0,maxSnapshots:e.maxSnapshots,onConflict:o,autoLock:e.autoLock,interceptors:e.interceptors}):[],E=C?Mv(T,e.toolMode):[],D=C&&_?T.controller??null:null,O=()=>D?.get()??_,k=new Map,A=Rv(S,E,Fv);A.forEach(e=>k.set(e.name,`builtin`));let j=[...e.tools||[]];j.forEach(e=>k.set(e.name,`user`));let M=e.humanConfirm!==!1&&(!e.approval||e.approval.humanConfirmTool!==!1),ee=M?Ig():null;ee&&k.set(Pg,`builtin`);let N=y&&b?[(0,n.tool)(async()=>{let e=b.list();return e.length?b.restore()?`已回退到最近一次正常状态(checkpoint #${e[e.length-1].id})。对话历史、主数据、vfs、todos 已整体还原。请基于回退后的状态重新判断并继续。`:`回退失败:无可用 checkpoint。`:`无可用 checkpoint,无法回退。`},{name:`restore_last_checkpoint`,description:`回退到最近一次正常状态(整体还原对话历史 + 主数据 + vfs + todos)。当本轮操作出错、页面被改坏、或走偏时调用,回到本轮起点重新来过。不传参数即回退最近一次。`,schema:t.z.object({}).optional()}),(0,n.tool)(async()=>{let e=b.list();return e.length?`可用 checkpoint:
153
155
  `+e.map(e=>`#${e.id} [${e.label??`auto`}] 消息数=${e.messageCount} 时间=${new Date(e.timestamp).toLocaleTimeString()}`).join(`
154
156
  `):`无可用 checkpoint。`},{name:`list_checkpoints`,description:`列出可用会话 checkpoint(回退点)。`,schema:t.z.object({}).optional()})]:[];N.forEach(e=>k.set(e.name,`builtin`));let P=[...A,...j,...ee?[ee]:[],...N],F=S?.planning!==!1,I=S?.skills!==!1,te=S?.vfs!==!1,L=S?.summarization!==!1,R=S?.memory!==!1,ne=S?.subagent!==!1,re=e.verify?.maxAttempts??2,z=S?.verify===!0&&e.verify?.enabled!==!1&&re>0;e.verify?.check&&S?.verify!==!0&&console.warn(`[page-agent-sdk][verify] 检测到 verify.check 但 capabilities.verify 未开启,verify 未装载`);let ie=e.subagent,ae=ie?.allowedTools??[],oe=!ne||ie?.enabled===!1?void 0:$g({llm:ie?.llm??e.llm,allTools:P,allowedTools:ae.length?ae:void 0,systemPrompt:ie?.systemPrompt,temperature:ie?.temperature,maxTokens:ie?.maxTokens,skills:ie?.skills,maxDepth:ie?.maxDepth,maxParallel:ie?.maxParallel,debug:e.debug}),se=e.subagents?.length?n_(e.subagents,{llm:e.llm,allTools:P,debug:e.debug}):void 0,ce=[`get_data`,`describe_data`,`read`,`fetch_document`],le=P.filter(e=>ce.includes(e.name)),ue=z?r_({check:e.verify.check??l_({schemas:()=>O()?{"":O().schema}:{},root:()=>O()?.bind}),adversarial:e.verify?.adversarial?{llm:e.llm,tools:le}:void 0}):void 0,de=Bv({...S,humanConfirm:M,subagents:e.subagents?.map(e=>({id:e.id,description:e.description,temperature:e.temperature}))},C&&!!_,e.toolMode),fe=e.onEvent,pe=new Set,me=e=>{if(e.type!==`approval_request`){if(fe)try{fe(e)}catch{}for(let t of pe)try{t(e)}catch{}}},he=[de,...F?[m]:[],...I?[Eg(e.skills||[],{readVfs:te?e=>p.files[e]?.content:void 0})]:[],...te?[Y_(p)]:[],...L?[M_({...L_(e,u.contextWindow),llmInvoke:d,getRegisteredData:()=>O()?[{description:O().description??`主数据对象`}]:[],preserveLastToolResults:(e.contextOptions&&e.contextOptions.preserveLastToolResults)??[`describe_data`,`read`]})]:[],...R?[h]:[],...e.permissions?.length?[Mg(e.permissions)]:[],...y&&x&&b?[Gg(b)]:[],...M?[Lg()]:[],...e.approval&&(e.approval.tools!==void 0||e.approval.confirm)?[Ng(e.approval)]:[],...ue?[ue]:[],...oe?[oe]:[],...se?[se]:[],...e.middleware||[],xy(me,f,O)],ge=e.maxMemoryRounds??my,B={agentId:r,store:c,messages:f,vfsStore:p,listeners:pe,todosMw:m,memoryMw:h,agent:null,initDone:Promise.resolve(),sessionId:``,refCount:0,mcpClosers:[],mcpServers:[],checkpoint:b,dataOpsController:D,pendingConflict:i,liveData:O,applySnapshot(t){t.messages?.length&&f.push(...t.messages),t.vfs&&p.hydrate&&p.hydrate(t.vfs),t.todos?.length&&m.reset(t.todos),t.memory&&!e.memory&&h.reset(t.memory)},afterRound(){ve(),ye()},async send(t){await B.initDone;let n=t;if(e.interceptors?.input)try{let r=e.interceptors.input(t);typeof r==`string`&&(n=r)}catch{}f.push({role:`user`,content:n,timestamp:Date.now()});try{let t=await B.agent.invoke(f);if(e.interceptors?.output)try{let n=e.interceptors.output(t);typeof n==`string`&&(t=n)}catch{}return f.push({role:`assistant`,content:t,timestamp:Date.now()}),B.afterRound(),c&&await c.flush(),t}catch(e){throw me({type:`error`,message:e?.message||String(e)}),e}},async switchSession(t){if(await B.initDone,!c)throw Error(`page-agent-sdk: storage 未开启,无法切换会话(请传 storage 选项)`);s(`keep_external`),p.flush?.(),await c.flush();let n=t??``,i;return n?(i=await c.load(r,n),i||await c.createSession(r,e.session?.title,n)):n=await c.createSession(r,e.session?.title),B.sessionId=n,f.splice(0,f.length),p.clear?.(),m.reset([]),e.memory||h.reset(``),B.agent.debugLogs.value=[],i||=await c.load(r,n),i&&B.applySnapshot(i),e.memory&&c.save(r,B.sessionId,{memory:e.memory}),n},stream:(e,t,n)=>{if(!B.agent)throw Error(`page-agent-sdk: agent 尚未初始化完成,请先 await mount()`);let r=fe?e=>{t?.(e),me(e)}:t;if(n){let e=()=>s(`keep_external`);n.aborted?e():n.addEventListener(`abort`,e,{once:!0})}return B.agent.stream(e,r,n)},release(){if(B.refCount--,B.refCount<=0){c&&(p.flush?.(),c.flush(),c.dispose());let e=B.mcpClosers.splice(0);e.length&&Promise.allSettled(e.map(e=>e())),_y.delete(r)}},resolveConflict:s,getInfo(){return{id:r,model:gy(e.llm)?e.llm.model??e.llm.modelName:e.llm.model,systemPrompt:w,tools:P.map(e=>({name:e.name,description:e.description,schema:e.schema,source:k.get(e.name)||`user`})),skills:(e.skills??[]).map(e=>({name:e.name,description:e.description})),data:O()?{description:O().description,schema:O().schema}:void 0,memory:e.memory??``,middleware:he.map(e=>e.name),todos:(B.agent?.getState?.()?.todos??[]).map(e=>({content:e.content,status:e.status})),subagent:{enabled:!!oe,maxDepth:e.subagent?.maxDepth??1,maxParallel:e.subagent?.maxParallel??4,allowedTools:e.subagent?.allowedTools??[]},verify:{enabled:!!ue,maxAttempts:z?re:0,adversarial:z&&!!e.verify?.adversarial},mcp:{servers:B.mcpServers},lastCompression:B.agent?.getState?.()?.lastCompression,checkpoints:b?{enabled:!0,auto:x,list:b.list()}:void 0}}};async function _e(){if(!c)return;await c.ready;let t=e.session||{};if(t.id){B.sessionId=t.id;let e=await c.load(r,B.sessionId);e?B.applySnapshot(e):await c.createSession(r,t.title,B.sessionId)}else if(t.autoResume!==!1){let n=await c.listSessions(r);if(e.debug&&console.log(`[page-agent-sdk][restore] listSessions`,r,n.length,n.map(e=>e.sessionId)),n.length){B.sessionId=n[0].sessionId;let t=await c.load(r,B.sessionId);t?(B.applySnapshot(t),e.debug&&console.log(`[page-agent-sdk][restore] 恢复会话`,B.sessionId,`${t.messages?.length??0} msgs`)):e.debug&&console.log(`[page-agent-sdk][restore] 会话 meta 存在但快照为空`,B.sessionId)}else B.sessionId=await c.createSession(r,t.title),e.debug&&console.log(`[page-agent-sdk][restore] 新建会话(无历史)`,B.sessionId)}else B.sessionId=await c.createSession(r,t.title);e.memory&&c.save(r,B.sessionId,{memory:e.memory})}function ve(){let e=C_(f,ge);e.trimmed&&f.splice(e.deleteFrom,e.deleteCount,e.summary)}function ye(){if(!B.sessionId||!c)return;let t=JSON.parse(JSON.stringify(f));c.save(r,B.sessionId,{messages:t});let n=B.agent?.getState?.()?.todos??[];c.save(r,B.sessionId,{todos:n}),e.debug&&console.log(`[page-agent-sdk][persist] save`,B.sessionId,`${f.length} msgs`)}return B.initDone=(async()=>{if(await _e(),e.mcp?.length){let t=await Promise.allSettled(e.mcp.map(e=>y_(e)));B.mcpClosers=t.flatMap(e=>e.status===`fulfilled`?[e.value.close]:[]),B.mcpServers=[];let n=[];t.forEach((t,r)=>{let i=e.mcp[r],a=i.name??i.url;t.status===`fulfilled`?(B.mcpServers.push({name:a,url:i.url,toolCount:t.value.tools.length}),t.value.tools.forEach(e=>k.set(e.name,`mcp:${a}`)),n.push(...t.value.tools)):console.warn(`[page-agent-sdk][mcp] server ${a} 连接失败:`,t.reason)}),P.push(...n),e.debug&&console.log(`[page-agent-sdk][mcp] 注入 ${n.length} 个工具,${B.mcpServers.length} 个 server`)}B.agent=_g({...gy(e.llm)?{llm:e.llm}:{apiKey:e.llm.apiKey,baseUrl:e.llm.baseUrl,model:e.llm.model,temperature:e.llm.temperature,maxTokens:e.llm.maxTokens},systemPrompt:w,tools:P,middleware:he,maxToolRounds:e.maxToolRounds,maxRetries:e.maxRetries,maxParallelTools:e.maxParallelTools,contextWindow:u.contextWindow,maxOutputTokens:u.maxOutputTokens,maxVerifyAttempts:z?re:0,debug:e.debug}),g.current=B.agent})(),B}function Cy(e){let t=e.id??Vv();e.id||console.warn(`[page-agent-sdk] 未传 options.id,已生成随机 id "${t}"。刷新后持久化数据无法恢复,请传稳定 id。`);let n=e.streaming??!0,r=e.ui??`default`,i,a=e.shareContext?_y.get(t):void 0;a?i=a:(i=Sy(e,t),e.shareContext&&_y.set(t,i)),i.refCount++;let o=null,s=null,c=null;async function l(){if(await i.initDone,r===!1){i.store&&(s=()=>{i.vfsStore.flush?.(),i.store.flush()},c=()=>{document.visibilityState===`hidden`&&i.store.flush()},typeof window<`u`&&window.addEventListener(`pagehide`,s),typeof document<`u`&&document.addEventListener(`visibilitychange`,c));return}let t=typeof e.container==`string`?document.querySelector(e.container):e.container;if(!t)throw Error(`createChatSdk: 挂载点未找到(${e.container})`);let a=i.agent.debugLogs;o=sl(xi({setup(){return()=>Ls(qh,{fetchStream:n?i.agent.stream:void 0,fetchResponse:n?void 0:(e,t)=>{if(t){let e=()=>i.resolveConflict(`keep_external`);t.aborted?e():t.addEventListener(`abort`,e,{once:!0})}return i.agent.invoke(e,t)},title:e.title,placeholder:e.placeholder,debugLogs:a.value,initialMessages:i.messages,getInfo:()=>i.getInfo(),onUndo:i.checkpoint?()=>i.checkpoint.restore():void 0,canUndo:i.checkpoint?()=>i.checkpoint.canRestore():void 0,onPersist:async()=>{i.afterRound(),i.store&&await i.store.flush()},onClear:()=>{i.store&&(i.sessionId=Vv(),i.vfsStore.clear?.(),i.todosMw.reset([]),e.memory||i.memoryMw.reset(``),i.agent.debugLogs.value=[],i.store.createSession(i.agentId,e.session?.title,i.sessionId))},pendingConflict:i.pendingConflict.value,onResolveConflict:e=>i.resolveConflict(e)})}})),o.mount(t),i.store&&(s=()=>{i.vfsStore.flush?.(),i.store.flush()},c=()=>{document.visibilityState===`hidden`&&i.store.flush()},typeof window<`u`&&window.addEventListener(`pagehide`,s),typeof document<`u`&&document.addEventListener(`visibilitychange`,c))}function u(){i.resolveConflict(`keep_external`),s&&typeof window<`u`&&window.removeEventListener(`pagehide`,s),c&&typeof document<`u`&&document.removeEventListener(`visibilitychange`,c),s=null,c=null,o?.unmount(),o=null,i.release()}return{mount:l,unmount:u,send:i.send,switchSession:i.switchSession,stream:i.stream,inspect:i.getInfo,messages:i.messages,restoreLastCheckpoint:()=>i.checkpoint?.restore()??!1,listCheckpoints:()=>i.checkpoint?.list()??[],hook:e=>(i.listeners.add(e),()=>i.listeners.delete(e)),setData:e=>{if(!i.dataOpsController){console.warn(`[page-agent-sdk] setData 忽略:dataOps 已关闭(capabilities.dataOps:false)`);return}i.dataOpsController.set(e)},getData:()=>i.liveData(),pendingConflict:i.pendingConflict,resolveConflict:e=>i.resolveConflict(e)}}function wy(e){return(0,n.tool)(async t=>{let n=await e.handler(t);return typeof n==`string`?n:JSON.stringify(n)},{name:e.name,description:e.description,schema:e.schema})}e.CONTEXT_PRESETS=I_,e.ChatDialog=qh,e.CodePreview=Zd,e.HUMAN_CONFIRM_TOOL_NAME=Pg,e.MessageContent=$d,e.connectMcp=y_,e.copyText=yl,e.createAgent=_g,e.createApprovalMiddleware=Ng,e.createChatSdk=Cy,e.createCheckpointManager=Wg,e.createCheckpointMiddleware=Gg,e.createDataOps=Nv,e.createHumanConfirmMiddleware=Lg,e.createHumanConfirmTool=Ig,e.createMemoryBackend=sy,e.createSessionStore=dy,e.createSubagentMiddleware=$g,e.createSubagentsMiddleware=n_,e.createUsageHintsMiddleware=Bv,e.createVerifyMiddleware=r_,e.createVfs=H_,e.createWebStorageBackend=ly,e.createWriteBackCheck=l_,e.defineDataToolset=Lv,e.defineSkill=bg,e.defineTool=wy,e.detectGarbledToolCall=fg,e.estimateTokens=eg,e.extractSchemaHint=F_,e.extractText=g_,e.fetchDocTools=Fv,e.fetchTools=Iv,e.filterByToolMode=Mv,e.formatZodIssues=R_,e.isQuotaError=$v,e.jpEval=av,e.jsonParseError=B_,e.offloadPassThroughChars=ng,e.offloadThresholdChars=tg,e.presets=N_,e.resolveContextOptions=L_,e.resolveModelCaps=$h,e.runSandboxedScript=uv,e.searchJson=sv,e.selectBuiltinTools=Rv,e.systemPromptHelpers=P_,e.toolError=$,e.useChat=vl,Object.defineProperty(e,"z",{enumerable:!0,get:function(){return t.z}}),e.zodError=z_});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "page-agent-sdk",
3
- "version": "2.5.1",
3
+ "version": "2.6.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",
@@ -27,7 +27,7 @@ See `demo/plain.html` for a framework-agnostic importmap + esm.sh example.
27
27
 
28
28
  Create a plain/reactive object as the main data, then declare it with a zod schema. The agent can ONLY touch schema-declared top-level fields (ZodObject auto-whitelist); `set`/`edit` are schema-validated (invalid → structured error, no write). Field `.describe()` text is auto-injected into the system prompt so the LLM knows each field's purpose.
29
29
 
30
- > `systemPrompt` is optional — a built-in default is used if omitted (generic JSON-operation assistant + `systemPromptHelpers.reliableWriteRules`: read-before-write, fields per `describe`, retry on validation error, prefer incremental `edit`). Passing your own fully overrides it; append `systemPromptHelpers.reliableWriteRules` yourself if you want the rules.
30
+ > `systemPrompt` is optional — a built-in default is used if omitted (generic JSON-operation assistant + `systemPromptHelpers.reliableWriteRules`: read-before-write, fields per `describe`, retry on validation error, prefer incremental `edit`). Passing your own fully overrides it. Two ways to keep the write rules with a custom prompt: (a) append `systemPromptHelpers.reliableWriteRules` yourself, or (b) set `appendReliableWriteRules: true` to auto-append them (default `false` to avoid duplication).
31
31
 
32
32
  ```ts
33
33
  import { createChatSdk, z } from 'page-agent-sdk'
@@ -74,6 +74,8 @@ createChatSdk({
74
74
 
75
75
  `reliableWriteRules` — standardized "reliable write rules": read before write (`read`), fields per `read({jsonPath})` (returns format hint), retry on schema-validation errors, prefer `write` with `patch` incremental edits. Recommended for any scenario involving data writes.
76
76
 
77
+ Two ways to keep the rules with a custom `systemPrompt`: (a) append manually as above, or (b) set `appendReliableWriteRules: true` to auto-append (default `false`). The default prompt (when `systemPrompt` omitted) already includes them.
78
+
77
79
  ## Built-in data tools (auto-injected when `capabilities.dataOps`)
78
80
 
79
81
  Default `toolMode:'simple'` exposes high-level `read`/`write` + advanced query/snapshot tools (low-level `get`/`set`/`edit`/`delete`/`describe` are hidden, merged into `read`/`write`). `toolMode:'advanced'` exposes all; `toolMode:'minimal'` only `read`/`write`.
@@ -7,7 +7,8 @@ Full reference for `createChatSdk(options)`. Grouped by purpose. Required: `llm`
7
7
  | Option | Type | Default | Purpose / when |
8
8
  |---|---|---|---|
9
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` | 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 (append `systemPromptHelpers.reliableWriteRules` yourself if needed). |
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
+ | `appendReliableWriteRules` | `boolean` | `false` | When `true` and a custom `systemPrompt` is set, auto-append `systemPromptHelpers.reliableWriteRules` to it (avoids forgetting the write rules). No effect when `systemPrompt` is omitted (default prompt already includes them). |
11
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. |
12
13
  | `title` / `placeholder` | `string` | — | Dialog title / input placeholder (cosmetic). |
13
14
 
package/types/index.d.ts CHANGED
@@ -340,6 +340,8 @@ export interface ChatSdkOptions {
340
340
  /** 共享上下文:默认 false;true 时同 id 复用同一核心(messages/agent/工作区) */
341
341
  shareContext?: boolean;
342
342
  systemPrompt?: string;
343
+ /** 自定义 systemPrompt 时是否自动追加 reliableWriteRules(默认 false;不传 systemPrompt 用默认 prompt 时已内置,此项无效) */
344
+ appendReliableWriteRules?: boolean;
343
345
  tools?: any[];
344
346
  skills?: SkillSpec[];
345
347
  memory?: string;