# 0.2.0 架构与设计决策 实现以独立 bundle 接入 DeepSeek Harness,包含可信 Host、原生 Client、严格共享协议和正式 Remote contribution。不修改 Agent Loop、Session V4 或上游核心源码。接口依据见 [SOURCE_AUDIT](SOURCE_AUDIT.md),测试事实见 [VALIDATION](VALIDATION.md)。 ## 1. 模块关系 | 模块 | 主要文件 | 与 Harness 的关系 | |---|---|---| | 注册与配置 | `host/index.ts`、`action-config.ts` | Cordis Standard Schema;核心只依赖 tools/systemPrompt,P1/P2 按配置延迟注入额外服务 | | UI Planning | `host/ui-planning.ts` | 正常 systemPrompt.section,模型自行选择 text/structured_ui/interactive_ui,无额外规划模型 | | 完整 UI | `host/ui-generator.ts` | defineTool/ToolRuntime;canonical value 和 presentationMeta 分工 | | 动作权限与执行 | `host/action-service.ts`、`capabilities.ts` | 正式 Typert Remote、SessionController、tools.execute、真实 approval | | 文档来源 | `host/document-source.ts` | 读取当前 Session 的真实工具结果,核对工具名、call、UI 和版本 | | 操作账本 | `host/action-ledger.ts` | 正式 storage-domain,执行前保留请求和规范化参数,逐次显式验证写入 | | 组件流 | `host/ui-stream.ts` | 四个正常工具;Host 发放游标,保存 checkpoint,Session 记录每一步 | | 共享数据 | `shared/schema.ts`、`types.ts`、`safe-json.ts` | DSL、预检、Schema 和跨节点语义检查,不依赖 Host/浏览器 | | 共享计算 | `shared/expression.ts`、`state.ts` | 有界 AST、派生关系、控件值域和原子本地更新 | | Wire 协议 | `shared/actions.ts`、`stream.ts` | 有界请求、回执、capability、checkpoint、顺序与重放验证 | | Remote 编解码 | `remote/index.ts`、`host.ts` | 官方纯 createTypertRemoteInfo;严格 create/decode;Host 由 typert-loader 注册 | | 原生展示 | `client/index.ts`、`conversation-node.ts`、`tool-view.tsx` | ConversationNodeDefinition、keyed ToolView、chat node;复用 Harness React/locale/theme | | 交互执行 | `client/action-dispatcher.ts`、`remote-client.ts` | Session scope、显式点击、稳定 request ID、取消与可信回执 | | Renderer | `client/renderer.tsx`、`component-registry.tsx` | 21种白名单组件;稳定 ID;状态与文档 revision 分离;局部错误隔离 | | 发行 | `scripts/build.mjs`、`cordis.patch.yml` | Host ESM、Client lazy factory、类型声明、Schema、独立安装和撤销 | ## 2. 文字和 UI 生成 `text` 是 Agent 正常回答,不需要伪造空 UI 文档。规划说明提供选择原则和完整语法,但不拦截每条用户消息、不按关键词套整页模板。工具被 Agent scope 隐藏时,相应规划章节不输出。完整替换 system prompt 的部署仍可选择不采用本章节。 选择 UI 后,Agent 调用 `intelligent_ui({document})`。Host 做 descriptor 预检、严格 Schema、跨节点引用和预算验证,然后返回 canonical envelope。`output.render` 是给模型的短回执;`output.presentationMeta` 将文档放入正常工具结果,供持久化和 Client 使用。canonical value 本身并不自动持久化。 Harness defineTool 参数 DSL 不支持完整递归 `$ref`,所以入口声明结构化 `json`,由共享解析器强制验证内部结构。独立 JSON Schema 用于编辑器和第三方工具,并以 AJV Draft 2020-12 strict 模式测试;它不能取代引用环、唯一 ID 和控件域等语义检查。 外层完整文档协议继续为 `dsh.intelligent-ui` / `1.0`;内层 DSL 可为 `1.0` 或 `1.1`。旧版本禁止 actions 字段,新版本显式要求 actions 字典,不把旧字符串突然解释为执行能力。 ## 3. 原生对话集成 完整 UI 位于 `conversation.chat.node`,`groupTree: 'turn'` 使它不因工具过程折叠而隐藏。各 UI 工具的 `tool.call.toolview` 只显示进度、错误和回执,避免挂载两份独立可操作表单。 ConversationNodeDefinition 从成功结果 metadata 建立节点;使用非排他的 `role: start`,即使历史分页中缺少原始 call,也能从完整结果恢复。普通调用识别 `tool/result`;PTC 子调用识别 `tool/ptc-dispatch` 与真实 subCallId。PTC 事件适配有测试,完整真实模型 PTC SDK 执行未验收。 React 复用 Harness 运行时,固定 CSS 读取 `--dsw-*` 主题变量。模型不能选择模块名、组件函数、DOM 标签、事件函数、CSS 或 URL。Client 的正式 Remote contribution 必须先 `$mount(TYPERT_REMOTE)`,再在子作用域等待 `remote.intelligentUi`,避免等待自身注册服务造成死锁。详见 [CLIENT_INTEGRATION](CLIENT_INTEGRATION.md)。 ## 4. P1:动作的完整路径 ```mermaid sequenceDiagram participant U as 用户与 Client participant H as Host Remote participant L as 持久化账本 participant A as Harness Agent participant T as 工具与审批 U->>H: 点击提交 documentRef、actionId、values、requestId H->>H: 验证实际 principal、Session、持久化文档和 capability H->>L: 先保留请求、参数摘要和 queued 状态 H->>A: 正常 SessionController.prompt A->>H: intelligent_ui_action(operationId) H->>L: 校验所属 turn,原子认领 H->>T: 重新授权,必要时原生 approval T-->>H: 同意或拒绝 H->>T: tools.execute,继承 token 和权限链 T-->>H: canonical result H->>L: 保存权威状态、过滤结果和审计码 U->>H: 查询已保留操作 H-->>U: 严格公开回执 ``` ### 为什么使用一个 Agent bridge 已核查的 ApprovalService 需要真实打开的 Agent turn。直接在空闲 UI RPC 中调用工具,即使使用 tools.execute,也不能凭空获得正确审批上下文;tools.execute 本身也不负责全部 Agent Loop 日志。插件因此先保留动作,再通过正常 SessionController.prompt 进入 Agent turn,只让模型调用保留的 operationId。桥接 tool 结束该 turn,不要求再请求一条模型总结。 模型不能替换已经冻结的 capability 和参数。action turn 中的 guard 限制直接调用其他目标工具;实际目标通过 bridge token 派生的 parent/rootCallId 调用工具流水线,保留原有 allow/deny、monotonic guard、pre/post policy、审批、取消与 compound tool 子调用限制。没有从 registry 取出定义后直接调用 execute。 Remote 的资源声明和调用者检查都使用 Gateway 建立的 Context。浏览器不提交 principal、confirm 或权限令牌。Host 读取当前 Session 中实际持久化的文档并重算参数,目录仅是可用能力说明,不是授权凭证。确认后再次检查能力、Schema 和权限,配置变化不能被旧页面绕过。 ### 幂等和未知结果 操作 ID 绑定 Session、真实 requester 和 requestId;请求摘要绑定 documentRef、actionId 和提交值。相同请求读取既有状态;同 ID 不同内容拒绝。账本在排队前落盘,执行前原子认领,重启不重新调度。公开回执只包含配置开放的标量字段;原始工具错误和完整上下文不回传。 `queued`、`awaiting_confirmation`、`running` 是进行状态。`succeeded`、`failed`、`cancelled`、`unknown`、`accepted` 含义不同:callback 的 accepted 仅表示 Agent 已接收入队,不表示推理或后续工具完成;未知外部效果不能被自动重试。取消发送成功也不代表已发生的写入被撤销。 临时网络错误使 Client 保留同一请求身份,不通过自动生成新 requestId 猜测重试。历史列表和在途 cancel 的响应不能把更新的终态回退为旧状态。新结果不能覆盖提交后用户继续编辑的草稿。 ## 5. P2:组件级流和历史恢复 工具分别为 open、patch、complete、cancel。open 使用真实 callId 作为 sourceCallId,并由 Host 发放 generation;返回给模型的文本明确包含 sourceCallId、generation、revision、seq,模型无需从不可见 metadata 猜游标。 每个结果携带 `UIStreamCheckpoint`:protocol/version/sourceCallId/uiId/generation/seq/baseRevision/revision/type/snapshot。snapshot 始终是完整合法 UI,而非可执行代码或未闭合 JSON。正常工具结果自然进入 Session 的 page/follow,不增加私有 SSE endpoint 或自定义 Session 事件格式。 patch 只允许 add(parentId,index,node)、update(nodeId,完整props)、remove(nodeId)。候选树通过完整验证后才提交。初始 state、derived、actions、Select 域、root identity 和已有组件类型在同一 generation 内固定;改变这些定义需要新 generation。动作按钮在流结束前不可提交,本地控件可以立即使用。 三个游标分别表示不同事实:generation 是一次生成;seq 是 checkpoint 顺序;revision 是文档结构版本。完成/取消增加 seq,但不虚构结构变化。重复相同帧可忽略,同序号冲突拒绝;缺口可从新的完整合法 checkpoint 恢复;终态拒绝迟到更新。Host 保存有界 call 身份,防止非相邻重复工具调用绕过版本检查。 聊天节点使用稳定 `(sourceCallId, uiId, generation)` 身份;官方 assembler 对相同身份只初始化一次,后续结果更新同一节点。React 子组件使用稳定 node.id。同代更新保留有效 state、NumberInput 草稿、DOM 实例和焦点。当前仍会重新验证完整有界文档,不承诺只计算受影响分支。 主机重启后旧运行 epoch 不接受继续 patch,Agent 可从已验证完整快照开启新 generation。已保存的完成或取消界面直接恢复;中断界面保留最后有效快照,不自动生成新请求。Token 级解析、断词编译和分布式续流不在0.2.0内。 ## 6. 状态和数据分层 | 层 | 权威与生命周期 | |---|---| | 文档结构和初始定义 | Host验证后进入Session metadata;Client再次验证 | | local state / derived | 当前渲染实例拥有;本地 reducer 顺序更新;无LLM调用 | | stream revision / checkpoint | Host发放并持久化;Client验证并聚合 | | action请求与执行事实 | Host账本;显式提交后才保存请求参数 | | action结果映射 | 仅本次成功手势且未被新编辑覆盖时应用;恢复仅显示回执 | 不使用 localStorage 充当操作事实。刷新恢复文档初始值,未提交草稿不跨设备同步。账本含明确提交的数据,按 Harness 本地数据的访问与保留策略保护。不能清空账本后还声称保留旧幂等保证。 ## 7. 资源与发行边界 共享限制包括文档256KiB、200组件、UI深度16、state/derived各64、表达式2048节点/深度24/每轮8192步。Action请求32KiB、参数16KiB、公开结果32KiB;每代最多256帧、累计16MiB。Host内部记录开销与不可信文档分层验证,不借内部存储需要扩大模型预算。完整清单见 [SECURITY](SECURITY.md)。 Host ESM 保留官方服务为外部依赖。Client 输出官方模块加载器要求的 lazy CJS factory,并共享 React;仅允许官方纯 wire helper 被打入浏览器,不将 Host 服务或Node builtin打入。生成Host/Client/Remote类型与JSON Schema后通过bundle独立安装。所有注册、订阅和在途任务绑定Cordis生命周期。 当前未实现跨设备草稿、任意嵌套工具参数、任意结果对象、逐Token compiler、分布式账本或自动未知写入恢复。它们的边界与后续验收见 [ROADMAP](ROADMAP.md)。