207、【Agent】【OpenCode】TUI 内部:18 层 Provider 的职责地图 【声明】本博客所有内容均为个人业余时间创作所述技术案例均来自公开开源项目如GithubApache基金会不涉及任何企业机密或未公开技术如有侵权请联系删除标题207、【Agent】【OpenCode】TUI 内部18 层 Provider 的职责地图背景上篇 blog【Agent】【OpenCode】TUI 内部装配层与 context 工厂讲了 TUI 的分工骨架helper.tsx的createSimpleContext是 context 工厂产出 provider 组件 use 钩子context/下 12 个 context 都靠它sdk.tsx是工厂的一个实例只管通信useSDK供 18 组件取 clientapp.tsx是组合根——不造模块只把18 层 Provider按依赖顺序嵌套成树向上接住 thread.ts 的 transport。206 只讲了叠成树没逐个介绍这些 Provider 干什么——本篇给出一份职责地图4 组、18 个、各司其职OpenCode先回顾 app.tsx 里那棵洋葱206 篇见过此处只留轮廓ArgsProvider → 命令行参数 ExitProvider → 退出 KVProvider → 键值持久化 ToastProvider → 通知气泡 RouteProvider → 路由 TuiConfigProvider → TUI 配置 SDKProvider → SDK client 事件流 SyncProvider → 中央数据仓 ThemeProvider → 主题 ...Keybind/Local/Command/Dialog/Prompt 系列 App /这 18 层可归为 4 组数据源 / UI 结构 / 输入 prompt / 生命周期。组 1数据/服务源——TUI 的内容从哪来Provider源文件提供什么ArgsProvidercontext/args.tsx命令行参数model/agent/prompt/continue/sessionID/forkTuiConfigProvidercontext/tui-config.tsxTUI 配置thread.ts 读好的 TuiConfigSDKProvidercontext/sdk.tsxSDK client 事件流——数据进 TUI 的闸门204 篇SyncProvidercontext/sync.tsx中央数据仓session/message/part/todo/provider/permission…KVProvidercontext/kv.tsx键值持久化state/kv.jsonLocalProvidercontext/local.tsx本地 UI 状态列表、最近模型、颜色其中SyncProvider是体型最大的一个——它的 store 几乎镜像了整个后端状态// context/sync.tsx 的 store 形状节选const[store,setStore]createStore({provider:[],// 模型提供方permission:{},// 权限请求按 sessionquestion:{},// 提问请求按 sessionsession:[],// 会话列表message:{},// 消息按 sessionpart:{},// 消息片段按 messagetodo:{},mcp:{},vcs:...,// todo/mcp/版本控制…})它由 SDKProvider 的事件流持续填充——后端一有事件新消息、权限请求Sync 的 store 就更新所有订阅它的组件自动刷新。组 2渲染/UI 结构——界面怎么组织Provider源文件提供什么RouteProvidercontext/route.tsx路由{type:home}或{type:session, sessionID}切换首页/会话页ThemeProvidercontext/theme.tsx主题dark/light 自定义用 KV 持久化ToastProviderui/toast.tsx通知气泡顶部提示DialogProviderui/dialog.tsx对话框系统弹窗渲染、鼠标交互KeybindProvidercontext/keybind.tsx快捷键注册与分发读 config 的 keymap组 3输入 prompt——打字区的配套Provider源文件提供什么PromptRefProvidercontext/prompt.tsxprompt 输入框引用外部可写入/聚焦PromptStashProvidercomponent/prompt/stash.tsxprompt 暂存草稿存prompt-stash.jsonl稍后恢复PromptHistoryProvidercomponent/prompt/history.tsxprompt 历史↑ 回溯存prompt-history.jsonlFrecencyProvidercomponent/prompt/frecency.tsx频率排序最近/最常用优先CommandProvidercomponent/dialog-command.tsx“/” 斜杠命令面板trigger/slashes/keybinds组 4生命周期/容错Provider源文件提供什么ExitProvidercontext/exit.tsx退出清理渲染器、FlushInputBuffer、写提示、触发 onExitErrorBoundarysolid-js 内置错误边界子树渲染崩溃时兜底显示错误组件而非白屏嵌套顺序的为什么洋葱顺序不是随意的而是依赖从下往上外层 Provider 是被依赖的内层可以use外层的值。看几处关键依赖SDKProvider 必须在外层 ← Sync/Local/Keybind 都要 useSDK() 拿 client TuiConfigProvider 在外 ← Theme/Keybind 都读 config KVProvider 在外 ← Theme 用 KV 持久化主题 RouteProvider 居中 ← App 按 route 渲染 Home 或 Session 页规则很简单凡是一个 Provider 的 init 里要use另一个 Provider前者就必须嵌在后者内部。App最内层因此能拿到整棵树的全部能力。一句话理解组回答的问题数据源Args/config/SDK/Sync/KV/Local数据从哪来UI 结构Route/Theme/Toast/Dialog/Keybind界面长啥样、怎么交互输入 promptRef/Stash/History/Frecency/Command打字区怎么用生命周期Exit/ErrorBoundary怎么退出、崩了咋办一句话记忆18 层 Provider 洋葱树外层是被依赖的数据/配置源SDK 居中、Sync 靠它喂数据、KV 管持久化内层是具体功能模块prompt 的暂存/历史/排序/斜杠命令、弹窗/通知/快捷键。规则一条谁 init 里use了谁谁就得嵌在谁内部App在最内层拿全部能力。OK本篇先到这里如有疑问欢迎评论区留言讨论祝各位功力大涨技术更上一层楼更多内容见下篇 blog【Agent】【OpenCode】TUI 内部终端背景色的获取