@hostanagent/chat-widget 0.2.4 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -4
- package/SDK_ONBOARDING.md +40 -15
- package/dist/agent-chat.js +1 -1
- package/dist/index.js +41 -39
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
# @hostanagent/chat-widget 0.2.
|
|
1
|
+
# @hostanagent/chat-widget 0.2.5
|
|
2
2
|
|
|
3
|
-
HostAnAgent 协议 1.0
|
|
3
|
+
HostAnAgent 协议 1.0 的独立交付包。0.2.5 支持平台标准工具状态,保持既有运行与恢复协议。
|
|
4
4
|
|
|
5
|
-
安装固定归档:`npm install ./hostanagent-chat-widget-0.2.
|
|
5
|
+
安装固定归档:`npm install ./hostanagent-chat-widget-0.2.5.tgz`。依赖 Client Core 的包需同时安装 `hostanagent-client-core-0.2.5.tgz`。
|
|
6
6
|
|
|
7
7
|
完整接入指南随包提供在 `SDK_ONBOARDING.md`,包含服务端 Key、产品登录代理、恢复、工具与组件扩展;无需取得私有源码仓库权限即可阅读已安装包中的指南。
|
|
8
8
|
|
|
9
9
|
公开入口:包根。Client Core 另有 Node 专用 `/bridge`;Widget 另有独立浏览器脚本 `/embed`(dist/agent-chat.js)。Client Core 包根无 DOM 依赖。浏览器只能连接产品登录代理,不持有应用/供应商 Key。挂载或恢复历史不自动安装 ToolRunner。
|
|
10
10
|
|
|
11
|
-
本客户端包及随包文档采用 [MIT](https://opensource.org/license/mit),Copyright (c) 2026 GeekArt Inc.。第三方信息见 THIRD_PARTY_NOTICES.md
|
|
11
|
+
本客户端包及随包文档采用 [MIT](https://opensource.org/license/mit),Copyright (c) 2026 GeekArt Inc.。第三方信息见 THIRD_PARTY_NOTICES.md。软件许可不包含托管服务或模型使用额度。
|
package/SDK_ONBOARDING.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# HostAnAgent SDK / Widget 0.2.
|
|
1
|
+
# HostAnAgent SDK / Widget 0.2.5 接入
|
|
2
2
|
|
|
3
|
-
0.2.
|
|
3
|
+
0.2.5 提供平台标准工具的配置类型、执行状态展示和 ToolRunner 平台调用隔离;Python SDK 保持透传协议并补充对应说明。协议仍为 1.0,四个包统一使用 0.2.5,旧版归档保持不变。服务部署与包发布状态见[网站更新记录](https://hostanagent.com/docs/releases/changelog/)。真实产品工具和付费模型运行随产品接入继续验收。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
四个客户端包及随包文档、示例采用 MIT,Copyright (c) 2026 GeekArt Inc.,完整文本随包提供在 LICENSE。软件授权范围为客户端接入代码,托管服务和模型使用额度另行提供。不要向供应商填写 Key,也不要把 HAA 应用 Key 放进浏览器。
|
|
6
6
|
|
|
7
7
|
## 1. 准备 Agent 与服务端身份
|
|
8
8
|
|
|
@@ -20,12 +20,21 @@ PRODUCT_PUBLIC_ORIGIN=https://your-product.example
|
|
|
20
20
|
|
|
21
21
|
MCP `https://api.hostanagent.com/mcp` 仍只用于配置,不是运行、余额或充值入口。
|
|
22
22
|
|
|
23
|
-
## 2.
|
|
23
|
+
## 2. 安装公开包与运行最小示例
|
|
24
|
+
|
|
25
|
+
从官方分发渠道安装固定版本;Python 后端使用 Python 包,JS 接入按需安装相应 npm 包:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
python -m pip install hostanagent-client==0.2.5
|
|
29
|
+
npm install --save-exact @hostanagent/client-core@0.2.5 @hostanagent/tool-runner@0.2.5 @hostanagent/chat-widget@0.2.5
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
下面的本地产物路径用于独立 mock 示例,也可用于离线分发:
|
|
24
33
|
|
|
25
34
|
在发布目录之外新建目录,将三个 `.tgz` 和 `example/` 复制过来。安装命令使用具体文件名,不依赖仓库、源码别名、Vite 或 latest:
|
|
26
35
|
|
|
27
36
|
```bash
|
|
28
|
-
npm install --save-exact ./hostanagent-client-core-0.2.
|
|
37
|
+
npm install --save-exact ./hostanagent-client-core-0.2.5.tgz ./hostanagent-chat-widget-0.2.5.tgz ./hostanagent-tool-runner-0.2.5.tgz
|
|
29
38
|
node example/server.mjs
|
|
30
39
|
```
|
|
31
40
|
|
|
@@ -89,17 +98,27 @@ const runner = AgentToolRunner.attach(client, {
|
|
|
89
98
|
|
|
90
99
|
## 5. 固定脚本与组件扩展
|
|
91
100
|
|
|
92
|
-
|
|
101
|
+
公共脚本使用固定版本及对应构建的 SRI:
|
|
102
|
+
|
|
103
|
+
```html
|
|
104
|
+
<script src="https://unpkg.com/@hostanagent/chat-widget@0.2.5/dist/agent-chat.js"
|
|
105
|
+
integrity="sha384-f/Z5a1nBJgPwCmUBTTvSFhK8xiG/oFjU3sAIcuj3PTkq0rvTw9+2/9d9pgY5PIx2"
|
|
106
|
+
crossorigin="anonymous"></script>
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
需要自托管时,使用下面的方式:
|
|
110
|
+
|
|
111
|
+
将发布目录 `vendor/agent-chat/0.2.5/agent-chat.js` 和同目录 LICENSE 原样复制到产品静态目录,核对 manifest 的 SHA-256 / SRI。脚本内也保留完整 MIT 声明;再次打包、压缩或分发时应保留该许可注释。该脚本包含 Client Core,没有外部 JS 依赖:
|
|
93
112
|
|
|
94
113
|
```html
|
|
95
114
|
<div id="assistant" style="height:600px"></div>
|
|
96
|
-
<script src="/vendor/agent-chat/0.2.
|
|
115
|
+
<script src="/vendor/agent-chat/0.2.5/agent-chat.js"></script>
|
|
97
116
|
<script>
|
|
98
117
|
AgentChat.mount('#assistant', { endpoint: '/api/agent', agentId: '你的 Agent ID' });
|
|
99
118
|
</script>
|
|
100
119
|
```
|
|
101
120
|
|
|
102
|
-
严格 CSP 的产品将挂载代码放在自己的外部脚本中。传入明确 `agentVersion` 时保留旧接入的无 bootstrap 挂载行为;新产品代理需同时传入 `identityId: metadata.identity_id`(先调用 `bootstrap`),或直接传入已初始化的 `client`。也可 `import { mount } from '@hostanagent/chat-widget'`;模块依赖固定 `@hostanagent/client-core@0.2.
|
|
121
|
+
严格 CSP 的产品将挂载代码放在自己的外部脚本中。传入明确 `agentVersion` 时保留旧接入的无 bootstrap 挂载行为;新产品代理需同时传入 `identityId: metadata.identity_id`(先调用 `bootstrap`),或直接传入已初始化的 `client`。也可 `import { mount } from '@hostanagent/chat-widget'`;模块依赖固定 `@hostanagent/client-core@0.2.5`。
|
|
103
122
|
|
|
104
123
|
支持受控 Markdown(标题、段落、列表、引用、强调、代码块和复制)、安全链接、公开推理、工具/确认卡片;原始 HTML 是纯文本,不加载 Markdown 图片,不执行模型返回的脚本。`renderers` 是产品可信代码,产品负责它的安全性。通用工具 JSON 展示上限 16,000 字符,正文 Markdown 展示上限 200,000 字符。
|
|
105
124
|
|
|
@@ -109,17 +128,23 @@ const runner = AgentToolRunner.attach(client, {
|
|
|
109
128
|
|
|
110
129
|
| 产物 | 版本与入口 | 兼容边界 |
|
|
111
130
|
|---|---|---|
|
|
112
|
-
| Python `hostanagent-client` | 0.2.
|
|
113
|
-
| TS Client Core | 0.2.
|
|
114
|
-
| ToolRunner | 0.2.
|
|
115
|
-
| Widget | 0.2.
|
|
131
|
+
| Python `hostanagent-client` | 0.2.5;`from hostanagent import AgentClient` | Python >=3.10;协议 1.0;仅 httpx 运行依赖 |
|
|
132
|
+
| TS Client Core | 0.2.5;包根、`/bridge`(仅 Node) | 包根无 DOM;Node >=22.12;浏览器 fetch/Streams/crypto |
|
|
133
|
+
| ToolRunner | 0.2.5;包根 | 已整合 D02 连接与可靠回执语义;需显式协调器/handler |
|
|
134
|
+
| Widget | 0.2.5;包根、`/embed` | 协议 1.0;Shadow DOM;浏览器最终证据见交付记录 |
|
|
116
135
|
|
|
117
136
|
0.2.0 增加完整 Run 参数、错误关联 ID、运行发现与 Node 代理、组件扩展,并修复接受成功后的重试重复运行风险。0.2.1 整合主分支 D01/D02/D03/D04,采用同一份规范协议产物,修复新增执行器类型的 NodeNext 导入,并提供 D02 同款显式执行器代理授权。升级 ToolRunner 时按上节核对回执确认、普通异常和业务失败语义。产品应成组升级 JS 三包和脚本,先在 mock 验证,再接真实已授权服务;保留旧 0.2.0/0.2.1 文件。0.2.2 已整合 D05 固定版本和停用规则;修复空会话恢复残留、批量 SSE 回放误判超限,以及发送完成时清除后续编辑或抢走阅读焦点的问题。未知请求未核对前,Client Core 拒绝切换其他 Session,仍使用原 key 恢复;身份切换仍明确丢弃旧身份状态。服务端协议仍为 1.0,生产联合验收另行进行。
|
|
118
137
|
|
|
119
138
|
0.2.3 完成源码包、发布工作流与安装矩阵准备;0.2.4 补齐 GeekArt Inc. 的 MIT 声明及独立脚本许可注释。0.2.3 及更早产物保持原样。
|
|
120
139
|
|
|
121
|
-
从明确提交构建:`pnpm release:pack`(要求干净 checkout);输出 `artifacts/0.2.
|
|
140
|
+
从明确提交构建:`pnpm release:pack`(要求干净 checkout);输出 `artifacts/0.2.5/manifest.json`、三包、固定脚本、示例和产品代理/持久回执模板。manifest 同时记录准确契约摘要、全部文件 SHA-256 和 SRI;安装/浏览器验证先核对完整清单。只复制 Git 跟踪的示例文件,不纳入示例目录内的本地环境文件或依赖目录。已有版本目录会拒绝覆盖。开发中可用 `pnpm release:pack --candidate` 生成独立候选目录并记录实际 source_dirty 状态。运行 `pnpm test:packages <产物目录>`,在系统临时目录通过 tarball 安装、严格 Node 无 DOM / 浏览器类型检查与执行。测试只复制第三方编译器类型工具,不链接任何项目源码。
|
|
141
|
+
|
|
142
|
+
MIT 许可见各包、示例和模板中的 LICENSE;第三方信息见各包 THIRD_PARTY_NOTICES.md。构建和校验不执行上传。另有 `scripts/publish-release.mjs <产物目录>` 核对可发布性与远端同版本摘要,只有显式加入 `--publish` 才上传;版权声明不完整时会在上传前停止。0.2.4 已公开上传;后续版本须保留旧产物,核对包名权限、服务端集成和发布授权后再上传。Railway 仍按已提交源码手动部署。
|
|
143
|
+
|
|
144
|
+
兼容窗口:0.2.0/0.2.1 的固定版本运行接口继续兼容当前协议 1.0;D02 执行器使用 0.2.1 起的接口,D05 元数据与逻辑 Agent 选择需要已整合 D05 的服务端。Python 便捷入口见 wheel 内 README。未来删除字段或改变运行语义必须先发布不兼容版本和迁移说明,不能覆盖同版本产物。公共 CDN 使用上面的固定版本 URL,也可以自行托管同一脚本。
|
|
145
|
+
|
|
146
|
+
## 平台标准工具(0.2.5)
|
|
122
147
|
|
|
123
|
-
|
|
148
|
+
`wait` 与三个聊天历史工具由 Hosting 内部执行,需要在 Agent 的 `platform_tools.enabled` 显式开启并发布新版本,默认历史范围为 `current_session`。跨会话需发布 allowlist 并满足调用者原有权限。Snapshot 中 `execution_owner="platform"` 的调用仅用于展示;不要为它注册 handler、claim 或投递 receipt。`platform_waiting` 表示持久定时等待,`platform_running` 表示平台读取;等待成功不证明产品业务已经完成。
|
|
124
149
|
|
|
125
|
-
|
|
150
|
+
已发布 0.2.4 SDK 对这些附加字段保持兼容,服务端仍拒绝外部领取平台调用。0.2.5 聊天组件提供平台执行的中文状态,ToolRunner 显式跳过平台调用。旧 Agent 不会因客户端升级自动启用工具。平台读取结果包含 `source_ref`/覆盖与分页信息;跨会话源删除会清空依赖会话的云端历史及派生摘要,产品自存副本需自行清理。
|
package/dist/agent-chat.js
CHANGED
|
@@ -74,4 +74,4 @@ SOFTWARE.
|
|
|
74
74
|
.messages{min-height:0;overflow-anchor:none}.heading{min-width:0}.title{overflow-wrap:anywhere}.markdown{white-space:normal}.markdown p{margin:0 0 .35em;white-space:pre-wrap}.markdown h2,.markdown h3,.markdown h4,.markdown h5{margin:.7em 0 .3em}.markdown ul{padding-left:1.5em}.markdown blockquote{margin:.4em 0;padding-left:1em;border-left:3px solid var(--haa-border);color:var(--haa-muted)}.markdown a,.reference a{color:inherit;text-decoration:underline;overflow-wrap:anywhere}.markdown code{font-family:ui-monospace,monospace;background:#8881;border-radius:3px;padding:1px 4px}.code-block{position:relative;min-width:0;border:1px solid var(--haa-border);border-radius:8px;margin:.5em 0;padding:8px;background:#8881}.code-block button{display:block;margin-left:auto;border:0;background:transparent;color:var(--haa-muted);font-size:11px}.code-block pre{margin:5px 0;overflow:auto;white-space:pre}.code-block code{padding:0;background:none}.reference{display:flex;align-items:center;gap:8px;border:1px solid var(--haa-border);border-radius:7px;padding:6px 10px;margin:6px 0;overflow-wrap:anywhere}.reference small{color:var(--haa-muted)}.attachments{max-height:130px;overflow:auto}.composer-foot{gap:8px;flex-wrap:wrap}
|
|
75
75
|
`;function de(p){if(!(/[\u0000-\u0020\u007f\\]/.test(p)||!/^(https?:\/\/|\/(?!\/)|#)/i.test(p)))try{const e=new URL(p,globalThis.location?.href||"https://localhost/");if(["https:","http:"].includes(e.protocol)&&!e.username&&!e.password)return e.href}catch{}}function pe(p,e){const n=/`([^`\n]+)`|\*\*([^*\n]+)\*\*|\*([^*\n]+)\*|\[([^\]\n]+)\]\(([^\s)]+)\)/g;let t=0;for(const o of e.matchAll(n)){p.append(document.createTextNode(e.slice(t,o.index)));const a=document.createElement(o[1]?"code":o[2]?"strong":o[3]?"em":"a");if(a.textContent=o[1]||o[2]||o[3]||o[4],a instanceof HTMLAnchorElement){const c=de(o[5]);c&&(a.href=c,a.target="_blank",a.rel="noopener noreferrer")}p.append(a),t=o.index+o[0].length}p.append(document.createTextNode(e.slice(t)))}function Ae(p){const e=document.createElement("div");e.className="markdown";const n=p.slice(0,2e5).split(`
|
|
76
76
|
`);for(let t=0;t<n.length;t++){if(/^\s*```([\w+-]*)\s*$/.exec(n[t])){const f=document.createElement("div");f.className="code-block";const _=document.createElement("pre"),k=document.createElement("code"),C=[];for(;++t<n.length&&!/^\s*```\s*$/.test(n[t]);)C.push(n[t]);k.textContent=C.join(`
|
|
77
|
-
`),_.append(k);const O=document.createElement("button");O.type="button",O.textContent="复制代码",O.onclick=async()=>{try{await navigator.clipboard.writeText(k.textContent||""),O.textContent="已复制"}catch{O.textContent="复制失败,请手动选择"}},f.append(O,_),e.append(f);continue}const a=/^(#{1,4}) (.+)$/.exec(n[t]),c=/^\s*[-*] (.+)$/.exec(n[t]),l=/^> ?(.*)$/.exec(n[t]);if(c){let f=e.lastElementChild;f?.tagName!=="UL"&&(f=document.createElement("ul"),e.append(f));const _=document.createElement("li");pe(_,c[1]),f.append(_)}else{const f=document.createElement(a?`h${a[1].length+1}`:l?"blockquote":"p");pe(f,a?.[2]??l?.[1]??n[t]),n[t]||f.append(document.createElement("br")),e.append(f)}}return p.length>2e5&&e.append(document.createTextNode("内容过长,显示已截断。")),e}const Te={PROTOCOL_UNSUPPORTED:"服务端协议与当前组件不兼容,请升级客户端。",INSUFFICIENT_BALANCE:"USD 余额不足,未启动模型调用。当前未开放在线充值;测试额度需联系平台管理员人工入账。",COST_BUDGET_EXCEEDED:"本次运行预算不足,请核对 Agent 与本轮 USD 预算。",MODEL_UNAVAILABLE:"此模型当前不可用,请联系应用管理员。不会自动切换模型。",MODEL_UNPRICED:"此模型价格尚未就绪,请联系应用管理员。",AGENT_UNAVAILABLE:"此 Agent 已停用或归档,无法启动新运行。",AGENT_PAUSED:"此 Agent 已停用,无法启动新运行。",AGENT_ARCHIVED:"此 Agent 已归档,无法启动新运行。",AGENT_UNPUBLISHED:"此 Agent 尚未发布,请联系应用管理员先发布版本。",AGENT_FORBIDDEN:"当前登录用户无权使用此 Agent。",SESSION_VERSION_CONFLICT:"会话已绑定原 Agent 版本。升级版本需要明确新建会话。",IDENTITY_CHANGED:"登录身份已变化,请重新打开聊天框。",UNAUTHORIZED:"登录状态已失效,请重新登录。",SEND_OUTCOME_UNKNOWN:"上次发送结果未知,请重试相同内容以核对已有请求。",INPUT_OUTCOME_UNKNOWN:"上次补充输入结果未知,请重试相同内容。"};function W(p){const e=p&&typeof p=="object"&&"code"in p?String(p.code):"";return Te[e]||"请求未完成,请检查连接或联系应用管理员核对状态。"}const ce={queued:"排队中",running:"正在思考",waiting_tool:"等待外部工具",waiting_approval:"等待确认",completed:"本轮已完成",failed:"运行失败",cancelled:"已停止 Agent",expired:"运行已到期",ready:"等待接入方",claimed:"已分派",accepted:"处理中",blocked:"等待前序工具",approval:"需要确认",denied:"已拒绝"};function r(p,e="",n){const t=document.createElement(p);return t.className=e,n!==void 0&&(t.textContent=n),t}async function le(p,e){const n=typeof p=="string"?document.querySelector(p):p;if(!n)throw new Error("聊天框挂载节点不存在");const t=!e.client;let o=e.client;if(!o){const s=e.endpoint||"/api/agent",i=e.agentVersion?void 0:await Ne(s),h=e.identityId??i?.identity_id,g=i?.agents||i?.configuration?.agents||[];let u=e.agentVersion;if(e.sessionId){const w=await new ie({endpoint:s,agentVersion:u||"",identityId:h}).request(`/sessions/${encodeURIComponent(e.sessionId)}`);if(u&&w.agent_version&&u!==w.agent_version)throw new Error("会话已绑定其他版本,请新建会话后升级");u=w.agent_version||u}if(u||(u=(g.find(w=>w.id===e.agentId)||(e.agentId?void 0:g[0]))?.latest_version||void 0),!u)throw new Error("请先配置可用的 Agent 版本");if(e.agentId&&i?.versions&&!i.versions.some(b=>b.id===u&&b.agent_id===e.agentId))throw new Error("此会话版本不属于所选 Agent,请明确新建会话");o=new ie({endpoint:s,agentVersion:u,identityId:h,sessionId:e.sessionId,getContextRefs:e.getContextRefs,getReasoningEffort:e.getReasoningEffort,getLimits:e.getLimits})}const a=o,c=r("div");c.style.height="100%",c.dataset.appearance=e.theme?.appearance||"light",e.theme?.accent&&c.style.setProperty("--haa-accent",e.theme.accent),e.theme?.fontFamily&&c.style.setProperty("--haa-font",e.theme.fontFamily);const l=c.attachShadow({mode:"open"}),f=r("style");f.textContent=Oe,l.append(f);const _=e.mode||"inline",k=r("section",`panel ${_}`);k.setAttribute("aria-label",e.title||"Agent 对话");const C=r("header","header");C.append(r("div","mark","✳"));const O=r("div","heading");O.append(r("div","title",e.title||"Agent 助手"),r("div","subtitle","由你的应用提供工具与上下文"));const B=r("div","status");B.setAttribute("role","status"),B.setAttribute("aria-live","polite");const he=r("span","dot"),ue=r("span","","就绪");B.append(he,ue),C.append(O,B);const Q=r("button","icon-button","×");Q.setAttribute("aria-label","收起聊天框"),_!=="inline"&&C.append(Q);const E=r("div","messages");E.setAttribute("role","log"),E.setAttribute("aria-label","对话记录"),E.tabIndex=0;const fe=r("div","composer-wrap"),K=r("div","notice");K.setAttribute("role","alert");const ee=r("div","run-meta"),ge=r("span"),U=r("button","stop","停止 Agent");U.type="button",ee.append(ge,U);const G=r("form","composer"),v=r("textarea");v.placeholder="描述你希望 Agent 帮你完成的事情…",v.setAttribute("aria-label","输入消息"),v.maxLength=32e3;const te=r("div","composer-foot"),Se=r("span","hint","Enter 发送 · Shift + Enter 换行"),M=r("button","send","发送 ↑");M.type="submit";const ne=r("div","attachments"),z=r("button","secondary","选择资源");z.type="button",z.title="仅发送产品资源引用;不会将图像或音频作为模型输入",e.selectAttachments&&te.append(z),te.append(Se,M),G.append(ne,v,te),fe.append(K,ee,G,r("div","branding","POWERED BY HOST AN AGENT")),k.append(C,E,fe),l.append(k);const L=r("button","launcher","✳ 打开助手");_!=="inline"&&l.append(L),_==="popup"?k.classList.add("hidden"):L.classList.add("hidden"),n.append(c);let q="",ae="",J=!1,I=!1,P=!1,$=!1,Y=!1,oe=0,A=a.identityEpoch,T=[];const j=new Map,se=new Map,me=()=>{k.classList.remove("hidden"),L.classList.add("hidden"),v.focus()},re=()=>{k.classList.add("hidden"),L.classList.remove("hidden"),L.focus()};Q.onclick=re,L.onclick=me,k.addEventListener("keydown",s=>{s.key==="Escape"&&_!=="inline"&&re()});async function X(s){const i=a.identityEpoch;q="";try{await s()}catch(h){i===a.identityEpoch&&(q=W(h))}!I&&i===a.identityEpoch&&(K.textContent=q||(a.state.error?W({code:a.state.errorCode}):""))}U.onclick=()=>{if(P||I)return;const s=A;P=!0,S(a.state),X(()=>a.cancel()).finally(()=>{!I&&s===A&&(P=!1,S(a.state))})};function xe(s){const i=r("div","reference"),h=s.url&&de(s.url);if(h){const g=r("a","",s.title);g.href=h,g.target="_blank",g.rel="noopener noreferrer",i.append(g)}else i.append(r("span","",s.title));return s.description&&i.append(r("small","",s.description)),i}function Z(){ne.replaceChildren(),T.forEach((s,i)=>{const h=xe(s),g=r("button","icon-button","×");g.type="button",g.setAttribute("aria-label",`移除 ${s.title}`),g.onclick=()=>{T.splice(i,1),Z()},h.append(g),ne.append(h)})}z.onclick=()=>{const s=A;Y=!0,S(a.state),X(async()=>{const i=await e.selectAttachments();I||s!==a.identityEpoch||(T=[...T,...i].slice(0,32),Z())}).finally(()=>{!I&&s===A&&(Y=!1,S(a.state))})};function be(s){const i=r("div","tool"),h=r("div","tool-top"),g=s.status==="completed"&&s.result_semantics==="submission"?"任务已提交":ce[s.status]||s.status;h.append(r("span","tool-icon","◇"),r("span","tool-name",s.name),r("span","pill",g)),i.append(h),s.progress&&i.append(r("div","tool-note",s.progress)),s.cancel_requested&&i.append(r("div","tool-note","已请求接入方取消;业务任务状态仍待确认。")),s.result_semantics==="submission"&&i.append(r("div","tool-note","此工具确认任务提交,生成结果由接入方继续管理。"));const u=r("details");if(u.append(r("summary","","查看调用参数"),r("pre","",JSON.stringify(s.arguments,null,2).slice(0,16e3))),i.append(u),u.open=s.status==="approval",s.result!==void 0&&s.result!==null){let b;try{b=e.renderers?.[s.name]?.(s)}catch{}b instanceof HTMLElement?i.append(b):i.append(r("pre","",JSON.stringify(s.result,null,2).slice(0,16e3)))}if(s.error&&i.append(r("div","tool-note",s.error)),s.status==="approval"&&s.approval_id&&s.run_id===a.state.run?.id&&!N.has(a.state.run?.status||"")){i.append(r("div","tool-note","请确认以上参数。接入方仍会验证你的业务权限。"));const b=r("div","actions");for(const[w,V]of[["approve","确认执行"],["deny","拒绝"]]){const R=r("button",w==="approve"?"primary":"secondary",V),d=A;R.onclick=()=>{I||d!==a.identityEpoch||s.run_id!==a.state.run?.id||(R.disabled=!0,X(()=>a.approve({approval_id:s.approval_id,tool_call_id:s.id,arguments_digest:s.arguments_digest,decision:w})).finally(()=>{R.disabled=!1}))},b.append(R)}i.append(b)}return i}function S(s){A!==a.identityEpoch&&(A=a.identityEpoch,v.value="",q="",T=[],J=!1,P=!1,$=!1,Y=!1,se.clear(),j.clear(),ae="",Z(),z.disabled=!1);const i=s.run,h=!!i&&!N.has(i.status);if(ue.textContent=s.connection==="reconnecting"?"正在重连":i?.status==="running"&&i.messages.some(d=>d.status==="streaming"&&d.content)?"正在回答":i?ce[i.status]||i.status:"就绪",he.classList.toggle("busy",h),M.disabled=s.sending||$,z.disabled=s.sending||$||Y,M.textContent=s.sending?"发送中…":h?"补充 ↑":"发送 ↑",K.textContent=q||(s.error?W({code:s.errorCode}):"")||(i?.error?W(i.error):""),ee.classList.toggle("hidden",!i),U.classList.toggle("hidden",!h),U.disabled=P,U.textContent=P?"正在停止…":"停止 Agent",i){const d=Number(i.usage.input_tokens||0)+Number(i.usage.output_tokens||0);ge.textContent=`${i.steps} 个步骤 · ${d.toLocaleString()} tokens${i.queued_inputs?` · ${i.queued_inputs} 条输入待处理`:""}`}const g=JSON.stringify([i?.messages,i?.tool_calls,i?.status]);if(g===ae)return;ae=g;const u=E.scrollHeight-E.scrollTop-E.clientHeight<100,b=E.scrollTop,w=[],V=(d,m,x)=>{const D=JSON.stringify(m),we=j.get(d),ke=we?.signature===D?we.node:x();j.set(d,{signature:D,node:ke}),w.push(ke)};if(i?.messages.length){for(const d of i.messages)if(!(d.role!=="user"&&d.role!=="assistant")){V(`message:${d.id}`,d,()=>{const m=r("article",`message ${d.role} ${d.status}`);if(d.role==="assistant"){const x=r("div","assistant-label");x.append(r("span","","✳"),document.createTextNode("AGENT")),m.append(x)}if(d.role==="assistant"&&d.reasoning&&["reasoning","summary"].includes(d.reasoning_kind||"")){const x=r("details","reasoning"),D=r("summary","",d.reasoning_kind==="summary"?"推理摘要":"思考过程");d.status==="streaming"&&!d.content&&D.append(r("span","reasoning-status","生成中…")),x.open=se.get(d.id)??(d.status==="streaming"&&!d.content),D.onclick=()=>{se.set(d.id,!x.open)},x.append(D,r("div","reasoning-text",d.reasoning)),m.append(x)}if(d.content){const x=r("div","bubble");x.append(Ae(d.content)),m.append(x)}try{for(const x of e.getReferences?.(d)||[])m.append(xe(x))}catch{}return d.status==="interrupted"&&m.append(r("div","interrupted","本段输出未完成")),m});for(const m of i.tool_calls.filter(x=>x.message_id===d.id))V(`tool:${m.id}`,[m,i.status],()=>be(m))}for(const d of i.tool_calls.filter(m=>!m.message_id))V(`tool:${d.id}`,[d,i.status],()=>be(d))}else{const d=r("div","empty"),m=r("div","empty-symbol","✳");m.setAttribute("aria-hidden","true");const x=r("div","core-label","AGENT CORE / READY");x.setAttribute("aria-hidden","true"),d.append(m,x,r("h3","","从一个想法开始"),r("p","","告诉我你的目标。Agent 会结合当前上下文,调用应用提供的工具,与你一起完成。")),w.push(d)}const R=new Set(w);for(const d of Array.from(E.children))R.has(d)||d.remove();for(let d=0;d<w.length;d++)E.children[d]!==w[d]&&E.insertBefore(w[d],E.children[d]||null);for(const[d,m]of j)R.has(m.node)||j.delete(d);E.scrollTop=u?E.scrollHeight:b}const ye=a.subscribe(S);if(v.addEventListener("input",()=>{oe++}),v.addEventListener("compositionstart",()=>{J=!0}),v.addEventListener("compositionend",()=>{J=!1}),v.addEventListener("keydown",s=>{s.key==="Enter"&&!s.shiftKey&&!s.isComposing&&!J&&s.keyCode!==229&&(s.preventDefault(),G.requestSubmit())}),G.onsubmit=s=>{s.preventDefault();const i=v.value.trim();if(!i||a.state.sending||$)return;const h=A,g=oe,u=T.slice();$=!0,S(a.state),X(async()=>{if(a.state.run&&!N.has(a.state.run.status)){if(T.length){q="正在运行时只能补充文本,资源引用请在下一轮提交。";return}await a.appendInput(i)}else{const b=u.length?[...await a.options.getContextRefs?.()||[],...u.map(w=>w.ref)]:void 0;if(I||h!==a.identityEpoch)return;await a.send(i,b?{context_refs:b}:{})}I||h!==a.identityEpoch||(g===oe&&(v.value=""),T=T.filter(b=>!u.includes(b)),Z(),(l.activeElement===v||l.activeElement===M)&&v.focus())}).finally(()=>{!I&&h===A&&($=!1,S(a.state))})},e.sessionId)try{await a.restore(e.sessionId)}catch(s){throw ye(),c.remove(),t&&a.destroy(),s}return{client:a,open:me,close:re,focus:()=>v.focus(),destroy(){I=!0,ye(),c.remove(),t&&a.destroy()}}}const Ce={mount:le};return H.AgentChat=Ce,H.mount=le,Object.defineProperty(H,Symbol.toStringTag,{value:"Module"}),H})({});
|
|
77
|
+
`),_.append(k);const O=document.createElement("button");O.type="button",O.textContent="复制代码",O.onclick=async()=>{try{await navigator.clipboard.writeText(k.textContent||""),O.textContent="已复制"}catch{O.textContent="复制失败,请手动选择"}},f.append(O,_),e.append(f);continue}const a=/^(#{1,4}) (.+)$/.exec(n[t]),c=/^\s*[-*] (.+)$/.exec(n[t]),l=/^> ?(.*)$/.exec(n[t]);if(c){let f=e.lastElementChild;f?.tagName!=="UL"&&(f=document.createElement("ul"),e.append(f));const _=document.createElement("li");pe(_,c[1]),f.append(_)}else{const f=document.createElement(a?`h${a[1].length+1}`:l?"blockquote":"p");pe(f,a?.[2]??l?.[1]??n[t]),n[t]||f.append(document.createElement("br")),e.append(f)}}return p.length>2e5&&e.append(document.createTextNode("内容过长,显示已截断。")),e}const Te={PROTOCOL_UNSUPPORTED:"服务端协议与当前组件不兼容,请升级客户端。",INSUFFICIENT_BALANCE:"USD 余额不足,未启动模型调用。当前未开放在线充值;测试额度需联系平台管理员人工入账。",COST_BUDGET_EXCEEDED:"本次运行预算不足,请核对 Agent 与本轮 USD 预算。",MODEL_UNAVAILABLE:"此模型当前不可用,请联系应用管理员。不会自动切换模型。",MODEL_UNPRICED:"此模型价格尚未就绪,请联系应用管理员。",AGENT_UNAVAILABLE:"此 Agent 已停用或归档,无法启动新运行。",AGENT_PAUSED:"此 Agent 已停用,无法启动新运行。",AGENT_ARCHIVED:"此 Agent 已归档,无法启动新运行。",AGENT_UNPUBLISHED:"此 Agent 尚未发布,请联系应用管理员先发布版本。",AGENT_FORBIDDEN:"当前登录用户无权使用此 Agent。",SESSION_VERSION_CONFLICT:"会话已绑定原 Agent 版本。升级版本需要明确新建会话。",IDENTITY_CHANGED:"登录身份已变化,请重新打开聊天框。",UNAUTHORIZED:"登录状态已失效,请重新登录。",SEND_OUTCOME_UNKNOWN:"上次发送结果未知,请重试相同内容以核对已有请求。",INPUT_OUTCOME_UNKNOWN:"上次补充输入结果未知,请重试相同内容。"};function W(p){const e=p&&typeof p=="object"&&"code"in p?String(p.code):"";return Te[e]||"请求未完成,请检查连接或联系应用管理员核对状态。"}const ce={queued:"排队中",running:"正在思考",waiting_tool:"等待外部工具",waiting_approval:"等待确认",completed:"本轮已完成",failed:"运行失败",cancelled:"已停止 Agent",expired:"运行已到期",platform_waiting:"平台等待中",platform_running:"正在查阅历史",ready:"等待接入方",claimed:"已分派",accepted:"处理中",blocked:"等待前序工具",approval:"需要确认",denied:"已拒绝"};function r(p,e="",n){const t=document.createElement(p);return t.className=e,n!==void 0&&(t.textContent=n),t}async function le(p,e){const n=typeof p=="string"?document.querySelector(p):p;if(!n)throw new Error("聊天框挂载节点不存在");const t=!e.client;let o=e.client;if(!o){const s=e.endpoint||"/api/agent",i=e.agentVersion?void 0:await Ne(s),h=e.identityId??i?.identity_id,g=i?.agents||i?.configuration?.agents||[];let u=e.agentVersion;if(e.sessionId){const w=await new ie({endpoint:s,agentVersion:u||"",identityId:h}).request(`/sessions/${encodeURIComponent(e.sessionId)}`);if(u&&w.agent_version&&u!==w.agent_version)throw new Error("会话已绑定其他版本,请新建会话后升级");u=w.agent_version||u}if(u||(u=(g.find(w=>w.id===e.agentId)||(e.agentId?void 0:g[0]))?.latest_version||void 0),!u)throw new Error("请先配置可用的 Agent 版本");if(e.agentId&&i?.versions&&!i.versions.some(b=>b.id===u&&b.agent_id===e.agentId))throw new Error("此会话版本不属于所选 Agent,请明确新建会话");o=new ie({endpoint:s,agentVersion:u,identityId:h,sessionId:e.sessionId,getContextRefs:e.getContextRefs,getReasoningEffort:e.getReasoningEffort,getLimits:e.getLimits})}const a=o,c=r("div");c.style.height="100%",c.dataset.appearance=e.theme?.appearance||"light",e.theme?.accent&&c.style.setProperty("--haa-accent",e.theme.accent),e.theme?.fontFamily&&c.style.setProperty("--haa-font",e.theme.fontFamily);const l=c.attachShadow({mode:"open"}),f=r("style");f.textContent=Oe,l.append(f);const _=e.mode||"inline",k=r("section",`panel ${_}`);k.setAttribute("aria-label",e.title||"Agent 对话");const C=r("header","header");C.append(r("div","mark","✳"));const O=r("div","heading");O.append(r("div","title",e.title||"Agent 助手"),r("div","subtitle","由你的应用提供工具与上下文"));const B=r("div","status");B.setAttribute("role","status"),B.setAttribute("aria-live","polite");const he=r("span","dot"),ue=r("span","","就绪");B.append(he,ue),C.append(O,B);const Q=r("button","icon-button","×");Q.setAttribute("aria-label","收起聊天框"),_!=="inline"&&C.append(Q);const E=r("div","messages");E.setAttribute("role","log"),E.setAttribute("aria-label","对话记录"),E.tabIndex=0;const fe=r("div","composer-wrap"),K=r("div","notice");K.setAttribute("role","alert");const ee=r("div","run-meta"),ge=r("span"),U=r("button","stop","停止 Agent");U.type="button",ee.append(ge,U);const G=r("form","composer"),v=r("textarea");v.placeholder="描述你希望 Agent 帮你完成的事情…",v.setAttribute("aria-label","输入消息"),v.maxLength=32e3;const te=r("div","composer-foot"),Se=r("span","hint","Enter 发送 · Shift + Enter 换行"),M=r("button","send","发送 ↑");M.type="submit";const ne=r("div","attachments"),z=r("button","secondary","选择资源");z.type="button",z.title="仅发送产品资源引用;不会将图像或音频作为模型输入",e.selectAttachments&&te.append(z),te.append(Se,M),G.append(ne,v,te),fe.append(K,ee,G,r("div","branding","POWERED BY HOST AN AGENT")),k.append(C,E,fe),l.append(k);const L=r("button","launcher","✳ 打开助手");_!=="inline"&&l.append(L),_==="popup"?k.classList.add("hidden"):L.classList.add("hidden"),n.append(c);let q="",ae="",J=!1,I=!1,$=!1,D=!1,Y=!1,oe=0,A=a.identityEpoch,T=[];const j=new Map,se=new Map,me=()=>{k.classList.remove("hidden"),L.classList.add("hidden"),v.focus()},re=()=>{k.classList.add("hidden"),L.classList.remove("hidden"),L.focus()};Q.onclick=re,L.onclick=me,k.addEventListener("keydown",s=>{s.key==="Escape"&&_!=="inline"&&re()});async function X(s){const i=a.identityEpoch;q="";try{await s()}catch(h){i===a.identityEpoch&&(q=W(h))}!I&&i===a.identityEpoch&&(K.textContent=q||(a.state.error?W({code:a.state.errorCode}):""))}U.onclick=()=>{if($||I)return;const s=A;$=!0,S(a.state),X(()=>a.cancel()).finally(()=>{!I&&s===A&&($=!1,S(a.state))})};function xe(s){const i=r("div","reference"),h=s.url&&de(s.url);if(h){const g=r("a","",s.title);g.href=h,g.target="_blank",g.rel="noopener noreferrer",i.append(g)}else i.append(r("span","",s.title));return s.description&&i.append(r("small","",s.description)),i}function Z(){ne.replaceChildren(),T.forEach((s,i)=>{const h=xe(s),g=r("button","icon-button","×");g.type="button",g.setAttribute("aria-label",`移除 ${s.title}`),g.onclick=()=>{T.splice(i,1),Z()},h.append(g),ne.append(h)})}z.onclick=()=>{const s=A;Y=!0,S(a.state),X(async()=>{const i=await e.selectAttachments();I||s!==a.identityEpoch||(T=[...T,...i].slice(0,32),Z())}).finally(()=>{!I&&s===A&&(Y=!1,S(a.state))})};function be(s){const i=r("div","tool"),h=r("div","tool-top"),g=s.status==="completed"&&s.result_semantics==="submission"?"任务已提交":ce[s.status]||s.status;h.append(r("span","tool-icon","◇"),r("span","tool-name",s.name),r("span","pill",g)),i.append(h),s.execution_owner==="platform"&&(i.append(r("div","tool-note",s.name==="wait"?"平台定时等待;等待结束后,Agent 会继续执行。等待成功仅表示时间已到。":"由平台读取授权范围内的聊天历史。")),s.not_before&&s.status==="platform_waiting"&&i.append(r("div","tool-note",`最早继续时间:${new Date(s.not_before*1e3).toLocaleTimeString()}`))),s.progress&&i.append(r("div","tool-note",s.progress)),s.cancel_requested&&i.append(r("div","tool-note","已请求接入方取消;业务任务状态仍待确认。")),s.result_semantics==="submission"&&i.append(r("div","tool-note","此工具确认任务提交,生成结果由接入方继续管理。"));const u=r("details");if(u.append(r("summary","","查看调用参数"),r("pre","",JSON.stringify(s.arguments,null,2).slice(0,16e3))),i.append(u),u.open=s.status==="approval",s.result!==void 0&&s.result!==null){let b;try{b=s.execution_owner==="platform"?void 0:e.renderers?.[s.name]?.(s)}catch{}b instanceof HTMLElement?i.append(b):i.append(r("pre","",JSON.stringify(s.result,null,2).slice(0,16e3)))}if(s.error&&i.append(r("div","tool-note",s.error)),s.execution_owner!=="platform"&&s.status==="approval"&&s.approval_id&&s.run_id===a.state.run?.id&&!N.has(a.state.run?.status||"")){i.append(r("div","tool-note","请确认以上参数。接入方仍会验证你的业务权限。"));const b=r("div","actions");for(const[w,V]of[["approve","确认执行"],["deny","拒绝"]]){const R=r("button",w==="approve"?"primary":"secondary",V),d=A;R.onclick=()=>{I||d!==a.identityEpoch||s.run_id!==a.state.run?.id||(R.disabled=!0,X(()=>a.approve({approval_id:s.approval_id,tool_call_id:s.id,arguments_digest:s.arguments_digest,decision:w})).finally(()=>{R.disabled=!1}))},b.append(R)}i.append(b)}return i}function S(s){A!==a.identityEpoch&&(A=a.identityEpoch,v.value="",q="",T=[],J=!1,$=!1,D=!1,Y=!1,se.clear(),j.clear(),ae="",Z(),z.disabled=!1);const i=s.run,h=!!i&&!N.has(i.status);if(ue.textContent=s.connection==="reconnecting"?"正在重连":i?.status==="running"&&i.messages.some(d=>d.status==="streaming"&&d.content)?"正在回答":i?ce[i.status]||i.status:"就绪",he.classList.toggle("busy",h),M.disabled=s.sending||D,z.disabled=s.sending||D||Y,M.textContent=s.sending?"发送中…":h?"补充 ↑":"发送 ↑",K.textContent=q||(s.error?W({code:s.errorCode}):"")||(i?.error?W(i.error):""),ee.classList.toggle("hidden",!i),U.classList.toggle("hidden",!h),U.disabled=$,U.textContent=$?"正在停止…":"停止 Agent",i){const d=Number(i.usage.input_tokens||0)+Number(i.usage.output_tokens||0);ge.textContent=`${i.steps} 个步骤 · ${d.toLocaleString()} tokens${i.queued_inputs?` · ${i.queued_inputs} 条输入待处理`:""}`}const g=JSON.stringify([i?.messages,i?.tool_calls,i?.status]);if(g===ae)return;ae=g;const u=E.scrollHeight-E.scrollTop-E.clientHeight<100,b=E.scrollTop,w=[],V=(d,m,x)=>{const P=JSON.stringify(m),we=j.get(d),ke=we?.signature===P?we.node:x();j.set(d,{signature:P,node:ke}),w.push(ke)};if(i?.messages.length){for(const d of i.messages)if(!(d.role!=="user"&&d.role!=="assistant")){V(`message:${d.id}`,d,()=>{const m=r("article",`message ${d.role} ${d.status}`);if(d.role==="assistant"){const x=r("div","assistant-label");x.append(r("span","","✳"),document.createTextNode("AGENT")),m.append(x)}if(d.role==="assistant"&&d.reasoning&&["reasoning","summary"].includes(d.reasoning_kind||"")){const x=r("details","reasoning"),P=r("summary","",d.reasoning_kind==="summary"?"推理摘要":"思考过程");d.status==="streaming"&&!d.content&&P.append(r("span","reasoning-status","生成中…")),x.open=se.get(d.id)??(d.status==="streaming"&&!d.content),P.onclick=()=>{se.set(d.id,!x.open)},x.append(P,r("div","reasoning-text",d.reasoning)),m.append(x)}if(d.content){const x=r("div","bubble");x.append(Ae(d.content)),m.append(x)}try{for(const x of e.getReferences?.(d)||[])m.append(xe(x))}catch{}return d.status==="interrupted"&&m.append(r("div","interrupted","本段输出未完成")),m});for(const m of i.tool_calls.filter(x=>x.message_id===d.id))V(`tool:${m.id}`,[m,i.status],()=>be(m))}for(const d of i.tool_calls.filter(m=>!m.message_id))V(`tool:${d.id}`,[d,i.status],()=>be(d))}else{const d=r("div","empty"),m=r("div","empty-symbol","✳");m.setAttribute("aria-hidden","true");const x=r("div","core-label","AGENT CORE / READY");x.setAttribute("aria-hidden","true"),d.append(m,x,r("h3","","从一个想法开始"),r("p","","告诉我你的目标。Agent 会结合当前上下文,调用应用提供的工具,与你一起完成。")),w.push(d)}const R=new Set(w);for(const d of Array.from(E.children))R.has(d)||d.remove();for(let d=0;d<w.length;d++)E.children[d]!==w[d]&&E.insertBefore(w[d],E.children[d]||null);for(const[d,m]of j)R.has(m.node)||j.delete(d);E.scrollTop=u?E.scrollHeight:b}const ye=a.subscribe(S);if(v.addEventListener("input",()=>{oe++}),v.addEventListener("compositionstart",()=>{J=!0}),v.addEventListener("compositionend",()=>{J=!1}),v.addEventListener("keydown",s=>{s.key==="Enter"&&!s.shiftKey&&!s.isComposing&&!J&&s.keyCode!==229&&(s.preventDefault(),G.requestSubmit())}),G.onsubmit=s=>{s.preventDefault();const i=v.value.trim();if(!i||a.state.sending||D)return;const h=A,g=oe,u=T.slice();D=!0,S(a.state),X(async()=>{if(a.state.run&&!N.has(a.state.run.status)){if(T.length){q="正在运行时只能补充文本,资源引用请在下一轮提交。";return}await a.appendInput(i)}else{const b=u.length?[...await a.options.getContextRefs?.()||[],...u.map(w=>w.ref)]:void 0;if(I||h!==a.identityEpoch)return;await a.send(i,b?{context_refs:b}:{})}I||h!==a.identityEpoch||(g===oe&&(v.value=""),T=T.filter(b=>!u.includes(b)),Z(),(l.activeElement===v||l.activeElement===M)&&v.focus())}).finally(()=>{!I&&h===A&&(D=!1,S(a.state))})},e.sessionId)try{await a.restore(e.sessionId)}catch(s){throw ye(),c.remove(),t&&a.destroy(),s}return{client:a,open:me,close:re,focus:()=>v.focus(),destroy(){I=!0,ye(),c.remove(),t&&a.destroy()}}}const Ce={mount:le};return H.AgentChat=Ce,H.mount=le,Object.defineProperty(H,Symbol.toStringTag,{value:"Module"}),H})({});
|
package/dist/index.js
CHANGED
|
@@ -109,14 +109,14 @@ function ke(s) {
|
|
|
109
109
|
for (; ++i < f.length && !/^\s*```\s*$/.test(f[i]); ) C.push(f[i]);
|
|
110
110
|
v.textContent = C.join(`
|
|
111
111
|
`), k.append(v);
|
|
112
|
-
const
|
|
113
|
-
|
|
112
|
+
const _ = document.createElement("button");
|
|
113
|
+
_.type = "button", _.textContent = "复制代码", _.onclick = async () => {
|
|
114
114
|
try {
|
|
115
|
-
await navigator.clipboard.writeText(v.textContent || ""),
|
|
115
|
+
await navigator.clipboard.writeText(v.textContent || ""), _.textContent = "已复制";
|
|
116
116
|
} catch {
|
|
117
|
-
|
|
117
|
+
_.textContent = "复制失败,请手动选择";
|
|
118
118
|
}
|
|
119
|
-
}, m.append(
|
|
119
|
+
}, m.append(_, k), o.append(m);
|
|
120
120
|
continue;
|
|
121
121
|
}
|
|
122
122
|
const r = /^(#{1,4}) (.+)$/.exec(f[i]), w = /^\s*[-*] (.+)$/.exec(f[i]), N = /^> ?(.*)$/.exec(f[i]);
|
|
@@ -162,6 +162,8 @@ const ge = {
|
|
|
162
162
|
failed: "运行失败",
|
|
163
163
|
cancelled: "已停止 Agent",
|
|
164
164
|
expired: "运行已到期",
|
|
165
|
+
platform_waiting: "平台等待中",
|
|
166
|
+
platform_running: "正在查阅历史",
|
|
165
167
|
ready: "等待接入方",
|
|
166
168
|
claimed: "已分派",
|
|
167
169
|
accepted: "处理中",
|
|
@@ -207,20 +209,20 @@ async function Ee(s, o) {
|
|
|
207
209
|
v.setAttribute("aria-label", o.title || "Agent 对话");
|
|
208
210
|
const C = t("header", "header");
|
|
209
211
|
C.append(t("div", "mark", "✳"));
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
+
const _ = t("div", "heading");
|
|
213
|
+
_.append(t("div", "title", o.title || "Agent 助手"), t("div", "subtitle", "由你的应用提供工具与上下文"));
|
|
212
214
|
const F = t("div", "status");
|
|
213
215
|
F.setAttribute("role", "status"), F.setAttribute("aria-live", "polite");
|
|
214
216
|
const oe = t("span", "dot"), re = t("span", "", "就绪");
|
|
215
|
-
F.append(oe, re), C.append(
|
|
217
|
+
F.append(oe, re), C.append(_, F);
|
|
216
218
|
const W = t("button", "icon-button", "×");
|
|
217
219
|
W.setAttribute("aria-label", "收起聊天框"), k !== "inline" && C.append(W);
|
|
218
220
|
const y = t("div", "messages");
|
|
219
221
|
y.setAttribute("role", "log"), y.setAttribute("aria-label", "对话记录"), y.tabIndex = 0;
|
|
220
222
|
const ie = t("div", "composer-wrap"), q = t("div", "notice");
|
|
221
223
|
q.setAttribute("role", "alert");
|
|
222
|
-
const Y = t("div", "run-meta"), se = t("span"),
|
|
223
|
-
|
|
224
|
+
const Y = t("div", "run-meta"), se = t("span"), I = t("button", "stop", "停止 Agent");
|
|
225
|
+
I.type = "button", Y.append(se, I);
|
|
224
226
|
const G = t("form", "composer"), b = t("textarea");
|
|
225
227
|
b.placeholder = "描述你希望 Agent 帮你完成的事情…", b.setAttribute("aria-label", "输入消息"), b.maxLength = 32e3;
|
|
226
228
|
const X = t("div", "composer-foot"), be = t("span", "hint", "Enter 发送 · Shift + Enter 换行"), M = t("button", "send", "发送 ↑");
|
|
@@ -229,7 +231,7 @@ async function Ee(s, o) {
|
|
|
229
231
|
S.type = "button", S.title = "仅发送产品资源引用;不会将图像或音频作为模型输入", o.selectAttachments && X.append(S), X.append(be, M), G.append(Z, b, X), ie.append(q, Y, G, t("div", "branding", "POWERED BY HOST AN AGENT")), v.append(C, y, ie), N.append(v);
|
|
230
232
|
const O = t("button", "launcher", "✳ 打开助手");
|
|
231
233
|
k !== "inline" && N.append(O), k === "popup" ? v.classList.add("hidden") : O.classList.add("hidden"), f.append(w);
|
|
232
|
-
let
|
|
234
|
+
let D = "", Q = "", H = !1, E = !1, U = !1, R = !1, V = !1, ee = 0, A = r.identityEpoch, z = [];
|
|
233
235
|
const P = /* @__PURE__ */ new Map(), te = /* @__PURE__ */ new Map(), de = () => {
|
|
234
236
|
v.classList.remove("hidden"), O.classList.add("hidden"), b.focus();
|
|
235
237
|
}, ae = () => {
|
|
@@ -240,19 +242,19 @@ async function Ee(s, o) {
|
|
|
240
242
|
});
|
|
241
243
|
async function j(e) {
|
|
242
244
|
const a = r.identityEpoch;
|
|
243
|
-
|
|
245
|
+
D = "";
|
|
244
246
|
try {
|
|
245
247
|
await e();
|
|
246
248
|
} catch (d) {
|
|
247
|
-
a === r.identityEpoch && (
|
|
249
|
+
a === r.identityEpoch && (D = K(d));
|
|
248
250
|
}
|
|
249
|
-
!E && a === r.identityEpoch && (q.textContent =
|
|
251
|
+
!E && a === r.identityEpoch && (q.textContent = D || (r.state.error ? K({ code: r.state.errorCode }) : ""));
|
|
250
252
|
}
|
|
251
|
-
|
|
252
|
-
if (
|
|
253
|
-
const e =
|
|
254
|
-
|
|
255
|
-
!E && e ===
|
|
253
|
+
I.onclick = () => {
|
|
254
|
+
if (U || E) return;
|
|
255
|
+
const e = A;
|
|
256
|
+
U = !0, T(r.state), j(() => r.cancel()).finally(() => {
|
|
257
|
+
!E && e === A && (U = !1, T(r.state));
|
|
256
258
|
});
|
|
257
259
|
};
|
|
258
260
|
function pe(e) {
|
|
@@ -272,50 +274,50 @@ async function Ee(s, o) {
|
|
|
272
274
|
});
|
|
273
275
|
}
|
|
274
276
|
S.onclick = () => {
|
|
275
|
-
const e =
|
|
277
|
+
const e = A;
|
|
276
278
|
V = !0, T(r.state), j(async () => {
|
|
277
279
|
const a = await o.selectAttachments();
|
|
278
280
|
E || e !== r.identityEpoch || (z = [...z, ...a].slice(0, 32), J());
|
|
279
281
|
}).finally(() => {
|
|
280
|
-
!E && e ===
|
|
282
|
+
!E && e === A && (V = !1, T(r.state));
|
|
281
283
|
});
|
|
282
284
|
};
|
|
283
285
|
function ce(e) {
|
|
284
286
|
const a = t("div", "tool"), d = t("div", "tool-top"), c = e.status === "completed" && e.result_semantics === "submission" ? "任务已提交" : ge[e.status] || e.status;
|
|
285
|
-
d.append(t("span", "tool-icon", "◇"), t("span", "tool-name", e.name), t("span", "pill", c)), a.append(d), e.progress && a.append(t("div", "tool-note", e.progress)), e.cancel_requested && a.append(t("div", "tool-note", "已请求接入方取消;业务任务状态仍待确认。")), e.result_semantics === "submission" && a.append(t("div", "tool-note", "此工具确认任务提交,生成结果由接入方继续管理。"));
|
|
287
|
+
d.append(t("span", "tool-icon", "◇"), t("span", "tool-name", e.name), t("span", "pill", c)), a.append(d), e.execution_owner === "platform" && (a.append(t("div", "tool-note", e.name === "wait" ? "平台定时等待;等待结束后,Agent 会继续执行。等待成功仅表示时间已到。" : "由平台读取授权范围内的聊天历史。")), e.not_before && e.status === "platform_waiting" && a.append(t("div", "tool-note", `最早继续时间:${new Date(e.not_before * 1e3).toLocaleTimeString()}`))), e.progress && a.append(t("div", "tool-note", e.progress)), e.cancel_requested && a.append(t("div", "tool-note", "已请求接入方取消;业务任务状态仍待确认。")), e.result_semantics === "submission" && a.append(t("div", "tool-note", "此工具确认任务提交,生成结果由接入方继续管理。"));
|
|
286
288
|
const p = t("details");
|
|
287
289
|
if (p.append(t("summary", "", "查看调用参数"), t("pre", "", JSON.stringify(e.arguments, null, 2).slice(0, 16e3))), a.append(p), p.open = e.status === "approval", e.result !== void 0 && e.result !== null) {
|
|
288
290
|
let h;
|
|
289
291
|
try {
|
|
290
|
-
h = o.renderers?.[e.name]?.(e);
|
|
292
|
+
h = e.execution_owner === "platform" ? void 0 : o.renderers?.[e.name]?.(e);
|
|
291
293
|
} catch {
|
|
292
294
|
}
|
|
293
295
|
h instanceof HTMLElement ? a.append(h) : a.append(t("pre", "", JSON.stringify(e.result, null, 2).slice(0, 16e3)));
|
|
294
296
|
}
|
|
295
|
-
if (e.error && a.append(t("div", "tool-note", e.error)), e.status === "approval" && e.approval_id && e.run_id === r.state.run?.id && !ne.has(r.state.run?.status || "")) {
|
|
297
|
+
if (e.error && a.append(t("div", "tool-note", e.error)), e.execution_owner !== "platform" && e.status === "approval" && e.approval_id && e.run_id === r.state.run?.id && !ne.has(r.state.run?.status || "")) {
|
|
296
298
|
a.append(t("div", "tool-note", "请确认以上参数。接入方仍会验证你的业务权限。"));
|
|
297
299
|
const h = t("div", "actions");
|
|
298
300
|
for (const [u, B] of [["approve", "确认执行"], ["deny", "拒绝"]]) {
|
|
299
|
-
const
|
|
300
|
-
|
|
301
|
-
E || n !== r.identityEpoch || e.run_id !== r.state.run?.id || (
|
|
301
|
+
const L = t("button", u === "approve" ? "primary" : "secondary", B), n = A;
|
|
302
|
+
L.onclick = () => {
|
|
303
|
+
E || n !== r.identityEpoch || e.run_id !== r.state.run?.id || (L.disabled = !0, j(() => r.approve({
|
|
302
304
|
approval_id: e.approval_id,
|
|
303
305
|
tool_call_id: e.id,
|
|
304
306
|
arguments_digest: e.arguments_digest,
|
|
305
307
|
decision: u
|
|
306
308
|
})).finally(() => {
|
|
307
|
-
|
|
309
|
+
L.disabled = !1;
|
|
308
310
|
}));
|
|
309
|
-
}, h.append(
|
|
311
|
+
}, h.append(L);
|
|
310
312
|
}
|
|
311
313
|
a.append(h);
|
|
312
314
|
}
|
|
313
315
|
return a;
|
|
314
316
|
}
|
|
315
317
|
function T(e) {
|
|
316
|
-
|
|
318
|
+
A !== r.identityEpoch && (A = r.identityEpoch, b.value = "", D = "", z = [], H = !1, U = !1, R = !1, V = !1, te.clear(), P.clear(), Q = "", J(), S.disabled = !1);
|
|
317
319
|
const a = e.run, d = !!a && !ne.has(a.status);
|
|
318
|
-
if (re.textContent = e.connection === "reconnecting" ? "正在重连" : a?.status === "running" && a.messages.some((n) => n.status === "streaming" && n.content) ? "正在回答" : a ? ge[a.status] || a.status : "就绪", oe.classList.toggle("busy", d), M.disabled = e.sending || R, S.disabled = e.sending || R || V, M.textContent = e.sending ? "发送中…" : d ? "补充 ↑" : "发送 ↑", q.textContent =
|
|
320
|
+
if (re.textContent = e.connection === "reconnecting" ? "正在重连" : a?.status === "running" && a.messages.some((n) => n.status === "streaming" && n.content) ? "正在回答" : a ? ge[a.status] || a.status : "就绪", oe.classList.toggle("busy", d), M.disabled = e.sending || R, S.disabled = e.sending || R || V, M.textContent = e.sending ? "发送中…" : d ? "补充 ↑" : "发送 ↑", q.textContent = D || (e.error ? K({ code: e.errorCode }) : "") || (a?.error ? K(a.error) : ""), Y.classList.toggle("hidden", !a), I.classList.toggle("hidden", !d), I.disabled = U, I.textContent = U ? "正在停止…" : "停止 Agent", a) {
|
|
319
321
|
const n = Number(a.usage.input_tokens || 0) + Number(a.usage.output_tokens || 0);
|
|
320
322
|
se.textContent = `${a.steps} 个步骤 · ${n.toLocaleString()} tokens${a.queued_inputs ? ` · ${a.queued_inputs} 条输入待处理` : ""}`;
|
|
321
323
|
}
|
|
@@ -360,11 +362,11 @@ async function Ee(s, o) {
|
|
|
360
362
|
const x = t("div", "core-label", "AGENT CORE / READY");
|
|
361
363
|
x.setAttribute("aria-hidden", "true"), n.append(l, x, t("h3", "", "从一个想法开始"), t("p", "", "告诉我你的目标。Agent 会结合当前上下文,调用应用提供的工具,与你一起完成。")), u.push(n);
|
|
362
364
|
}
|
|
363
|
-
const
|
|
364
|
-
for (const n of Array.from(y.children))
|
|
365
|
+
const L = new Set(u);
|
|
366
|
+
for (const n of Array.from(y.children)) L.has(n) || n.remove();
|
|
365
367
|
for (let n = 0; n < u.length; n++)
|
|
366
368
|
y.children[n] !== u[n] && y.insertBefore(u[n], y.children[n] || null);
|
|
367
|
-
for (const [n, l] of P)
|
|
369
|
+
for (const [n, l] of P) L.has(l.node) || P.delete(n);
|
|
368
370
|
y.scrollTop = p ? y.scrollHeight : h;
|
|
369
371
|
}
|
|
370
372
|
const le = r.subscribe(T);
|
|
@@ -380,11 +382,11 @@ async function Ee(s, o) {
|
|
|
380
382
|
e.preventDefault();
|
|
381
383
|
const a = b.value.trim();
|
|
382
384
|
if (!a || r.state.sending || R) return;
|
|
383
|
-
const d =
|
|
385
|
+
const d = A, c = ee, p = z.slice();
|
|
384
386
|
R = !0, T(r.state), j(async () => {
|
|
385
387
|
if (r.state.run && !ne.has(r.state.run.status)) {
|
|
386
388
|
if (z.length) {
|
|
387
|
-
|
|
389
|
+
D = "正在运行时只能补充文本,资源引用请在下一轮提交。";
|
|
388
390
|
return;
|
|
389
391
|
}
|
|
390
392
|
await r.appendInput(a);
|
|
@@ -395,7 +397,7 @@ async function Ee(s, o) {
|
|
|
395
397
|
}
|
|
396
398
|
E || d !== r.identityEpoch || (c === ee && (b.value = ""), z = z.filter((h) => !p.includes(h)), J(), (N.activeElement === b || N.activeElement === M) && b.focus());
|
|
397
399
|
}).finally(() => {
|
|
398
|
-
!E && d ===
|
|
400
|
+
!E && d === A && (R = !1, T(r.state));
|
|
399
401
|
});
|
|
400
402
|
}, o.sessionId)
|
|
401
403
|
try {
|
|
@@ -407,8 +409,8 @@ async function Ee(s, o) {
|
|
|
407
409
|
E = !0, le(), w.remove(), i && r.destroy();
|
|
408
410
|
} };
|
|
409
411
|
}
|
|
410
|
-
const
|
|
412
|
+
const Ae = { mount: Ee };
|
|
411
413
|
export {
|
|
412
|
-
|
|
414
|
+
Ae as AgentChat,
|
|
413
415
|
Ee as mount
|
|
414
416
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hostanagent/chat-widget",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Framework-free embeddable agent chat, with isolated styles",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"LICENSE"
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@hostanagent/client-core": "0.2.
|
|
23
|
+
"@hostanagent/client-core": "0.2.5"
|
|
24
24
|
},
|
|
25
25
|
"engines": {
|
|
26
26
|
"node": ">=22.12"
|