A pixel desktop pet that watches Claude Code, Codex, Cursor & other AI coding agents — so you don't have to.
# AGENTS.md
This file is the entry point for coding agents working in this repository. Keep it short and operational. Durable architecture lives in `docs/project/`; `docs/plans/` and `docs/investigations/` are historical evidence unless this file explicitly links them.
## Project Overview
Clawd 是一个 Electron 桌宠:通过 hook、日志轮询、plugin 和 extension 感知 AI coding agent 的工作状态,并播放像素风动画。当前支持 Claude Code、Codex CLI、Copilot CLI、Gemini CLI、Antigravity CLI (agy)、Cursor Agent、CodeBuddy、WorkBuddy、Grok Build、Kiro CLI、KiroCrew、Kimi Code CLI (Kimi-CLI)、Qwen Code、ZCode、CodeWhale、opencode、MiMo Code、Pi、OMP (oh-my-pi)、OpenClaw、Hermes Agent、Qoder、QoderWork、QwenWork (千问办公)、Reasonix、DeepSeek Harness、TraeCode (Trae CN)、MiniMax Code;内置 Clawd / Calico / Cloudling 三套主题,支持用户主题,并可经 Settings 从独立 `rullerzhou-afk/clawd-themes` 仓库下载可选官方主题(Hash Sage、Whale-chan,external theme 权限、可卸载);平台覆盖 Windows、macOS、Linux,UI 支持 en / zh / zh-TW / ko / ja / pt-BR / es。
## Common Commands
```bash
npm start
npm run build # default Windows package
npm run build:win:x64
npm run build:win:arm64
npm run build:win:all
npm run build:mac
npm run build:linux
npm run build:all
npm install
npm test
npm run verify:electron
npm run verify:release
npm run audit:assets
npm run audit:pr-history-assets
npm run audit:native-package -- --app-root <extracted-app-root> --target <target-id>
npm run create-theme
npm run install:claude-hooks
npm run uninstall:claude-hooks
npm run install:cursor-hooks
npm run install:gemini-hooks
npm run install:antigravity-hooks
npm run install:kiro-hooks
npm run install:kirocrew-hooks
npm run uninstall:kirocrew-hooks
npm run install:kimi-hooks
npm run install:qwen-hooks
npm run install:zcode-hooks
npm run uninstall:zcode-hooks
npm run install:codewhale-hooks
npm run uninstall:codewhale-hooks
npm run install:pi-extension
npm run uninstall:pi-extension
npm run install:omp-extension
npm run uninstall:omp-extension
npm run install:openclaw-plugin
npm run uninstall:openclaw-plugin
npm run install:hermes-plugin
npm run uninstall:hermes-plugin
npm run install:qoder-hooks
npm run uninstall:qoder-hooks
npm run install:qoderwork-hooks
npm run uninstall:qoderwork-hooks
npm run install:qwenwork-hooks
npm run uninstall:qwenwork-hooks
npm run install:reasonix-hooks
npm run uninstall:reasonix-hooks
npm run install:workbuddy-hooks
npm run uninstall:workbuddy-hooks
npm run install:grok-hooks
npm run uninstall:grok-hooks
npm run install:dsh
npm run uninstall:dsh
npm run install:codex-hooks
npm run uninstall:codex-hooks
npm run install:codex-debug-hooks
npm run uninstall:codex-debug-hooks
npm run install:mimocode-plugin
npm run uninstall:mimocode-plugin
node hooks/codebuddy-install.js
node hooks/opencode-install.js
bash test-demo.sh [seconds]
bash test-mini.sh [seconds]
bash test-macos.sh
bash test-oneshot-gate.sh [state] [seconds]
```
新安装默认只把 Claude Code 和 Codex 标记为已安装并启用;其他 agent 默认未安装、未启用。正常启动时,Clawd 只会为 `integrationInstalled=true` 且 `enabled=true` 的 agent 自动同步 Claude / Codex / Copilot / Gemini / Antigravity / Cursor / CodeBuddy / WorkBuddy / Grok Build / Kiro / KiroCrew / Kimi / Qwen / ZCode / CodeWhale / Qoder / QoderWork / QwenWork / Reasonix / TraeCode / MiniMax Code hooks、opencode / MiMo Code / OpenClaw / Hermes plugins 和 Pi / OMP extensions。Settings Agent 页的 Install 会安装并启用该集成;Uninstall 会卸载 Clawd 管理的 hook/plugin/extension,并同时把该 agent 设为未安装、未启用。单独关闭 enabled 只会跳过启动同步并屏蔽事件/权限入口,不卸载用户已有 hooks / plugins / extensions;重新启用未安装 agent 只打开事件入口,不会写本机集成文件。手动安装命令主要用于调试、重装或远程部署。
Settings 注册的自定义 HTTP Agent 是独立模型:`customApplications` 是注册真相,对应 `agents[customId]` 必须显式保持 `integrationInstalled=false`。注册只分配 ID 和状态入口,不安装 hook、不观察进程;v1 仅允许已注册且启用的 ID 向 `/state` 上报,`/permission` 永远不提供决定。删除或伪造的 `custom-` ID 必须直接拒绝,不能降级成 Claude Code subagent。
Copilot CLI 同步走 `<COPILOT_HOME 或 ~/.copilot>/hooks/hooks.json`,marker-based 增量合并只接管含 `copilot-hook.js` 标记的条目,用户其他 entry / 其他 `hooks/*.json` 文件原样保留;hooks.json 或 `settings.json` 顶层 `disableAllHooks: true` 时 doctor 报 warning(不挂 Fix 按钮)。详见 `docs/guides/copilot-setup.md`。
## Read These Docs
- `docs/project/agent-runtime-architecture.md`:运行时架构、模块边界、启动与数据流、多 agent、permission bubble、终端聚焦和自动同步
- `docs/project/theme-state-ui.md`:状态机、主题系统、settings、mini mode、素材规则、平台限制、待落地 UI 决策
- `docs/project/release-process.md`:发版 checklist、release note 核对、tag 触发 GitHub 打包和资产确认
- `docs/guides/copilot-setup.md`:Copilot CLI 自动同步说明、`COPILOT_HOME` 兼容性、手动配置备选模板
- `docs/guides/dsh-setup.md`:DeepSeek Harness web profile 与桌面版的实验性 plugin 集成状态与普通审批集成
- `docs/guides/state-mapping.md`:状态 → 动画权威表
- `docs/guides/guide-theme-creation.md`:主题作者指南
- `docs/guides/setup-guide.md`:安装、远程 SSH、各 agent 接入
- `docs/guides/custom-agent-http.md`:自定义 HTTP Agent 的 state-only 接入合约和动态端口发现
- `docs/guides/known-limitations.md`:用户向已知限制
- `docs/guides/recap.md`:本地小结的指标口径、coverage、时区/DST、隐私存储与产品红线
- `docs/guides/codex-wsl-clarification.md`:Codex / WSL 路径与 Node 说明
- `docs/guides/guide-remote-ssh.md`:Remote SSH 用户流程、Codespaces 单会话 transport 与共享主机边界
- `docs/guides/telegram-approval.md` / `docs/guides/feishu-lark-remote-approval.md`:远程审批设置与失败回退语义
- `docs/guides/roam-fence.md`:自由漫游与可选围栏
## Runtime Summary
- HTTP hook / plugin 主路径:`src/server.js` → `src/server-route-state.js` → `src/agent-runtime-main.js` → `src/state.js` → IPC;本地 JSONL monitor 直接进入 `agent-runtime-main`,不经过 HTTP server
- 权限主路径:`src/server-route-permission.js` → `src/permission.js`;本地 bubble、显式 permission automation 与可选 Telegram / 飞书 Lark 远程审批都可能产生真实决定
- 桌宠的渲染/输入双窗口由 `src/pet-window-runtime.js` 统一创建和定位;浮层排序与 topmost 行为在 `src/floating-window-runtime.js` / `src/topmost-runtime.js`
- `src/state.js` 生成的 session snapshot 是 Dashboard、HUD/Orbit 与可选通知/presence/mobile consumer 的共享合约,改字段必须检查所有消费者
- `src/integration-sync.js` 为已安装且启用的 agent 异步同步 hooks / plugins / extensions;Codex official hooks 为 primary,JSONL 轮询保留为 fallback
- Claude hook 恢复由 `src/claude-settings-watcher.js` 的目录 watcher + 低频只读巡检共同负责;repair 统一经过 `src/claude-hook-operations.js` 队列并复验,连续失败转 `manual-fix-required`。完整 gate、阈值与 source-missing 语义见 `docs/project/agent-runtime-architecture.md`
- `src/agent-gate.js` 控制各 agent 的安装意图、启用状态、权限气泡开关和 wait-for-input notification 子开关
- 设置系统主链路是 `src/prefs.js` → `src/settings-controller.js`(唯一写入者)→ `src/settings-store.js`;`settings-actions*` 负责校验和 pre-commit gates,`settings-effect-router.js` 负责 post-commit runtime effects 与广播
- 启动时还会尝试自动安装 VS Code / Cursor terminal-focus extension,并初始化 updater
- Remote SSH 通过 effective transport inspection 区分 ordinary parallel SSH 与 serialized transport;后者由 target-scoped coordinator 编排。远程部署只走 Settings,`scripts/remote-deploy.sh` 已 fail-fast 停用
## Core Files
更细的背景见 `docs/project/agent-runtime-architecture.md` 和 `docs/project/theme-state-ui.md`。
| File | Responsibility |
|------|------|
| `src/main.js` | Electron composition root:生命周期、上下文组装与各 runtime / IPC 注册器接线 |
| `src/server.js` + `src/server-route-state.js` + `src/server-route-permission.js` | HTTP 服务、端口发现、`/state` / `/permission` 路由 |
| `src/agent-runtime-main.js` + `src/integration-sync.js` | local monitor / official hook 仲裁、Codex turn fence、按 gate 的集成同步 |
| `src/state.js` | 状态机、多会话合并、优先级、自动回退、睡眠/DND |
| `src/renderer.js` | 动画切换、SVG 预加载、眼球追踪渲染 |
| `src/permission.js` + `src/permission-automation-policy.js` + `src/session-automation-coordinator.js` | 权限气泡、自动化策略、per-session 授权与决策回包;远程 client 由 `main.js` 注入 |
| `src/pet-window-runtime.js` + `src/floating-window-runtime.js` + `src/topmost-runtime.js` | 双窗口 owner、浮层重排、z-order / fullscreen / focusability |
| `src/update-bubble.js` | 更新气泡创建、测高、跟随桌宠定位,避让 HUD / permission stack |
| `src/dashboard.js` + `src/dashboard-renderer.js` | Sessions Dashboard 唯一 owner:会话列表、别名编辑、终端跳转、几何持久化,以及同一页面的临时键盘模式 |
| `src/dashboard-host.js` | Dashboard 普通宿主的平台封装:darwin/win32 用 `BaseWindow + WebContentsView`(无 `ready-to-show`),Linux 保留 `BrowserWindow` |
| `src/dashboard-quick-mode.js` | 完整 Dashboard 的 1–9 键盘模式(**macOS/Windows only**):quick 宿主、opacity/input parking、轮次栅栏与冻结数字映射;Windows 显式取消与页面失效的来源恢复在 `src/quick-select-origin-focus.js`,quick 宿主的退出清理挂在 `before-quit` |
| `src/session-hud.js` + `src/session-hud-renderer.js` | 桌宠旁轻量会话 HUD、折叠行、点击跳转 |
| `src/session-alias.js` | session alias key 规范化、TTL pruning、Kiro cwd scope |
| `src/theme-loader.js` + `src/theme-runtime.js` | stateless 主题加载/消毒与唯一 active-theme owner;`waitForThemeReloadSettled` 完成信号 |
| `src/official-theme-catalog.js` / `-download.js` / `-installer.js` / `-main.js` | 官方可下载主题:严格 catalog/cache、Electron `net.request` 流式下载、受限流式 ZIP 解压与 marker-before-rename、main owner/IPC/共享 `theme` lock |
| `src/prefs.js` | 偏好 schema、load/save/migrate/validate,设置持久化入口 |
| `src/settings-actions*.js` + `src/settings-effect-router.js` | 设置 validators / commands / pre-commit gates 与 post-commit runtime effects |
| `src/settings-controller.js` | 设置系统唯一写入者 |
| `src/settings-store.js` | 不可变 snapshot store |
| `src/settings-ui-core.js` + `src/settings-renderer.js` + `src/settings-tab-*.js` | Settings shared UI state、侧栏/tab shell 与各页逻辑 |
| `src/menu.js` | 托盘 / 右键菜单,串起设置、Dashboard、mini mode、更新入口 |
| `src/mini.js` | 极简模式入场、滑动、peek、状态映射 |
| `src/tick.js` | 主循环、鼠标轮询、眼球和 idle/sleep 逻辑 |
| `src/drag-position.js` | 拖拽落点规范化与跨显示器钳制 |
| `src/visible-margins.js` | 可视角色边距与 edge pinning 规则 |
| `src/updater.js` | Git 模式 / `electron-updater` 双路径更新逻辑 |
| `src/focus.js` | 终端聚焦 |
| `src/hit-renderer.js` + `src/hit-geometry.js` | 输入窗口命中、拖拽、连击反应 |
| `src/remote-ssh-runtime.js` | Remote SSH tunnel/ingress 状态机、ordinary health probe、serialized readiness 与重连 |
| `src/remote-ssh-transport.js` + `src/remote-ssh-transport-coordinator.js` | effective transport inspection、pre-spawn admission、target ownership、drain barrier 与 quarantine |
| `src/remote-ssh-deploy.js` | Deploy / Repair、身份事务、lease/fencing、wrapper、cleanup 与远端 Codex monitor mutation |
| `src/remote-ssh-identity.js` + `src/remote-ssh-layout.js` + `src/remote-ssh-ingress.js` | 身份/布局真相与 profile 专属、nonce 校验的本地 ingress |
| `src/remote-ssh-node.js` + `src/remote-ssh-shell-detect.js` + `src/remote-ssh-local-config.js` | 远端 Node/ shell 探测与本机 SSH config 入口 |
| `src/remote-ssh-profile.js` | Remote SSH profile schema、校验、默认值和持久化规范化 |
| `src/remote-ssh-ipc.js` | Remote SSH runtime / deploy / monitor orchestration、intent policy、IPC 状态与进度广播 |
| `src/remote-ssh-quote.js` | Remote SSH 终端命令与跨平台 shell quoting helper |
| `agents/registry.js` | agent 注册表 |
| `agents/codex-log-monitor.js` | Codex JSONL fallback 轮询 |
| `agents/gemini-log-monitor.js` | legacy Gemini session JSON 轮询器;当前 Gemini hook-only 路径不启动 |
| `hooks/dsh-install.js` | DeepSeek Harness immutable managed bridge generation、ownership verify、web 与 desktop 两个 profile 的安装 / 修复 / 卸载编排 |
| `hooks/kirocrew-install.js` + `hooks/kirocrew-store.js` | KiroCrew state-only gateway hooks、upstream-compatible mutation locks and atomic private store writes |
| `hooks/kirocrew-hook.js` + `hooks/kirocrew-command.js` | KiroCrew state bridge and exact command-shape ownership classifier |
| `hooks/dsh-notices.js` | DeepSeek Harness 常驻提示的存储与规则(按 profile 的 `notices-<profile>.json`) |
| `hooks/clawd-hook.js` + `hooks/copilot-hook.js` | Claude Code / Copilot CLI 状态上报脚本 |
| `hooks/install.js` | Claude hook 注册 / 卸载 |
| `hooks/auto-start.js` | Claude `SessionStart` 自动拉起 Clawd 的 hook |
| `hooks/codex-hook.js` / `hooks/codex-install.js` | Codex official hooks 状态与权限审批、安装 / 卸载 |
| `hooks/cursor-install.js` / `gemini-install.js` / `antigravity-install.js` / `kiro-install.js` / `kimi-install.js` / `qwen-code-install.js` / `codewhale-install.js` / `codebuddy-install.js` / `workbuddy-install.js` / `opencode-install.js` / `pi-install.js` / `openclaw-install.js` / `hermes-install.js` / `qoder-install.js` / `qoderwork-install.js` / `reasonix-install.js` | 各 agent 集成安装逻辑 |
| `hooks/opencode-family-managed-generation.js` | OpenCode 用户可写托管 generation(canonicalizer、bundle/manifest、stage/promote、target owner record、target-scoped lock);纯 Node 内置依赖,不进入 Remote SSH `HOOK_FILES` |
| `hooks/opencode-family-entry-ownership.js` | OpenCode-family plugin entry 的 string/tuple 解析、跨平台路径等价、所有权分类与重复/options 冲突规划(纯逻辑、可注入 fs) |
| `hooks/opencode-family-install.js` / `hooks/opencode-family-jsonc.js` | 家族注册器:`managedMaterialization:true` 走托管 generation 事务 + 共享 entry planner;`false` 保持 legacy broad-basename register/unregister |
| `src/doctor-detectors/opencode-managed-inspector.js` | managed OpenCode Doctor inspector(ownership/manifest/生成结构、Fix policy、masked supplementary warning);接受注入 fs |
| `hooks/workbuddy-hook.js` | WorkBuddy state + Notification command hook;无 session_id 时返回合法 stdout 后丢弃事件 |
| `hooks/grok-hook.js` / `hooks/grok-install.js` | Grok Build state + Notification command hook;写入 `<GROK_HOME 或 ~/.grok>/hooks/clawd-on-desk.json`;`env` marker `CLAWD_GROK_HOOK=v1` 所有权;无 session id 时丢弃;不注册 `/permission` |
| `hooks/zcode-hook.js` / `hooks/zcode-install.js` | ZCode 状态 + 阻塞式 PermissionRequest hooks(`hookSpecificOutput` 决定 / `{}` 无决定)、`hooks.events.*` 按事件超时增量注册与 Claude-imported Clawd hook 清理 |
| `hooks/qoder-hook.js` | Qoder state-only 状态上报脚本(Phase 1,stdout 恒为 `{}`) |
| `hooks/minimax-hook.js` / `hooks/minimax-install.js` | MiniMax Code state-only 本地插件:整目录 install/uninstall(`<MINIMAX_DATA_DIR 或 MAVIS_DATA_DIR 或 ~/.minimax>/plugins/clawd-state/`),CLAUDE 格式 hooks + exec-form args,10 事件无 PermissionRequest/Notification,`.clawd-managed.json` 结构化所有权凭据、fail closed |
| `hooks/codex-remote-monitor.js` | 远程 Codex JSONL 轮询并通过 SSH 隧道回传(含 token_count 订阅配额的 metadata_only 上报) |
| `hooks/claude-statusline.js` / `hooks/antigravity-statusline.js` | 各 CLI statusline 适配器:渲染状态行 + 把订阅配额(rate_limits)以 metadata_only POST 上报;Claude 版支持远程部署(`CLAWD_REMOTE=1` 前缀)与 `--chain` 串联模式(原 statusLine 对象保存在 `~/.claude/hooks/clawd-statusline-chain.json`,卸载时恢复) |
| `extensions/vscode/extension.js` | VS Code / Cursor 终端 tab 聚焦辅助扩展 |
## Constraints
- `agents/registry.js` 的 capabilities 是权限、subagent、session-end 等路由/gate 的权威来源;不得另写名单代替 capability 判定。但 permission automation eligibility 由更窄的 `isKnownPermissionAgent()` 显式判定(`KNOWN_PERMISSION_AGENTS` + explicit opencode-family membership),不得从 `permissionApproval` 自动派生或合并回 registry capabilities
- 小结的指标能力例外地只认 `src/recap-metrics.js` 的显式逐 Agent 口径,不得从 registry capabilities 或标准化 event 名推导。只统计已通过 gate、Codex source/replay fence、subagent 与完成仲裁的 accepted activity;DND 继续记录且不切 coverage,suspend/退出/关闭记录才切。`null` 是不支持,绝不能渲染或汇总成 `0`
- 小结持久层固定为 `~/.clawd/recap-v1/` 下 14 个本地日的最小 HMAC 小票与 400 个本地日的 daily/coverage;不得落 prompt/回复/命令/工具内容、路径/项目、原始 event/session/profile/turn/tool ID,不得增加网络、导出或分享。首版不做 Token、金额、模型、推理强度、Skill、人的工作/编码时长、streak、峰值/最长项、排行榜或生产力评分
- Claude Code / CodeBuddy 的阻塞式权限审批走 `POST /permission` HTTP hook;普通状态事件走 command hook(CodeBuddy command hook 对所有事件输出 `{}`,不做工具或权限决定)
- permission automation(off / auto-tools / unattended)和 per-session grant 会在 bubble 渲染前产生真实 allow/answer。agent/family eligibility 是显式白名单;工具分类则因 mode/adapter 而异:auto-tools 对 Claude/Qwen 的未知 built-in fail closed,但其他已知 adapter 不都使用逐工具白名单,unattended 还会有意自动放行可作 Allow/Deny 的未知请求。新增 agent、工具或交互类型必须审查 policy + tests,不能从 `permissionApproval` 推导资格或笼统假设“未知请求都会 defer”
- Telegram / 飞书 Lark 与本地 bubble 是并行决策通道。远程通道超时、断连、未配置或发送失败不得产生远程决定,更不得转成 deny;有本地 bubble 时请求继续 pending,只有 remote-only 且所有可用 client 都无决定时,整体请求才 no-decision 并回到 agent 原生流程
- WorkBuddy 通过当前 `~/.workbuddy-ai/settings.json`(旧版兼容 `~/.workbuddy/settings.json`)的 Claude Code 兼容 command hooks 做 **state + Notification only** 集成:不注册 `/permission`,审批始终留在 WorkBuddy 原生沙箱与 GUI;无 `session_id` 的事件返回合法 stdout 后直接丢弃。当前只支持 macOS/Windows 桌面应用,没有已验证的 Linux/WSL CLI;不要把裸 `Electron` 当 WorkBuddy 进程。
- Grok Build(agent id `grok-build`)通过 `<GROK_HOME 或 ~/.grok>/hooks/clawd-on-desk.json` 的 Claude Code 兼容 command hooks 做 **local / main-session / state + Notification only** 集成(Phase 1):stdin 为 camelCase(`sessionId` / `toolName`),`hookEventName` 是 camelCase key + snake_case value,`hook_event_name` 是 snake_case key + PascalCase value。**逐 handler 的 `env` 结构化 marker `CLAWD_GROK_HOOK=v1` 是唯一所有权凭据**;文件名/substring(含 `grok-hook.js.backup`)只用于诊断告警,永不授权改写或删除。安装/卸载只增删含该 marker 的 handler,保留 unknown 顶层键、其他 event、foreign matcher group 与 sibling handler;没有 marker 的既有文件一律原样拒绝。`SessionEnd` 不写 `timeout`(继承 Grok 1.5s teardown),其余事件 5s。Windows 用 `windowsWrapper:"portable"` 形态,不得用 `&` / `-EncodedCommand` / 自造 `shell` 字段。Grok 没有 `PermissionRequest`,不注册 `/permission`,adapter 始终输出 `{}`。子代理事件(`subagentType`)直接丢弃;不注册 `SubagentStart` / `SubagentStop`。Grok 默认会扫 `~/.claude/settings.json`,所以 `clawd-hook.js` / `cursor-hook.js` / `auto-start.js` 只在非空官方 `GROK_HOOK_EVENT` 下直接退出,`GROK_HOME` / `GROK_SESSION_ID` / 无关 `GROK_*` 不得触发。`Stop` 只有精确 `reason === "end_turn"` 且无 background task / session cron / `stopHookActive` 才算完成,其余由 adapter 本地判定;`channel_closed` / `shutdown` 等 session-end 形态的 Stop 在 fence 之前丢弃。turn 顺序由 `src/grok-turn-fence.js` 的 bounded in-memory fence 仲裁(晚到旧 turn、重复 terminal、continuation Stop、Stop→StopCancelled 纠正)。不进入 subagent / WSL / Remote SSH / startup recovery / terminal focus / quota / context / recap turn 指标。`~/.grok/hooks/clawd.json` 是未发布的 PR preview 路径,只做 warning,不得自动接管或删除。
- Codex 的阻塞式权限审批走 official `PermissionRequest` command hook:hook 脚本长连接 `POST /permission`,只允许 stdout 返回 sanitized `behavior/message`,`updatedInput` / `updatedPermissions` / `interrupt` 必须 omit。权限模式默认 `auto`(旧 `native` 规范化为 `auto`),仅准确本机 session/turn 的人工 reviewer 证据可进 Clawd;未知、自动 reviewer、MCP/app、远程/WSL 均 no-decision;唯一例外是远程/WSL(路由 reason 为 `nonlocal`)的 Bash/apply_patch 在全局自动放行会立即 allow 时(`wouldAutoAllowPermission`:非 reminder hold、非会话覆盖、非交互子代理)由 Clawd 直接放行(#1186),路由后若设置已变则 no-decision,绝不弹气泡。`intercept` 是显式接管;升级时旧 `intercept` / `native` 统一迁到 Auto 一次,之后重新选择的 Intercept 会保留。不得用全局 config、其他会话或旧回合推断当前请求的审批方;证据不足时返回 no-decision,交还 Codex 原生流程。
- hook 脚本只允许依赖 Node 内置模块,以及同目录 `hooks/` 下、且登记在 `src/remote-ssh-deploy.js` 的 `HOOK_FILES` 部署清单中的纯 Node helper(如 `server-config.js` / `shared-process.js` / `json-utils.js` / `codex-originator.js` / `codex-subagent-fields.js` / `context-usage.js` / `state-payload-size.js` / `quota-bucket.js` / `claude-rate-limits.js` / `codex-rate-limits.js` / `antigravity-context-usage.js`);manifest-consistency 测试强制检查依赖闭包,新增 helper 必须登记
- CJS hook 脚本需要稳定终端 PID 时,必须复用 `hooks/shared-process.js` 的 `createPidResolver()` 及其 lifecycle context;不要复制进程树 walk 或用 `process.ppid` 简化。`getStablePid()` 只是 opencode-family plugin 的内部 resolver
- `agentCmdlineCheck` 只对进程名在 `agentCmdlineNames` 里的祖先进程运行,默认是 `DEFAULT_AGENT_CMDLINE_NAMES`:`node` / `node.exe`,加上 Linux 上没设过 `process.title` 的 Node 在 `ps -o comm=` 里的名字——Node 23.8–25.4(含全部 24.x)把主线程命名为 `MainThread`、≥ 25.5 为 `node-MainThread`,Linux 报的进程名就是主线程名。传入 `agentCmdlineNames` 会整体替换默认值:只想加名字的 adapter 必须在选项里直接写成 `agentCmdlineNames: new Set([..., ...DEFAULT_AGENT_CMDLINE_NAMES])`(如 ZCode;可以跨行,方括号里只能有普通字符串字面量和 `...DEFAULT_AGENT_CMDLINE_NAMES` 本身,不能有注释或其他表达式;简写属性、先存进变量,或任何不是这个形状的提及(包括注释里的)都会被判不合格,因为文本检查无法核对),只有刻意只探测非 Node 宿主的(WorkBuddy 的 `electron`)例外,`test/shared-process.test.js` 会扫描 `hooks/*.js` 检查
- opencode v1 权限不走 `permission.ask` hook,而是 event hook + reverse bridge;v2 走 `ctx.permission.hook("evaluate")` 的阻塞 HTTP 响应
- OpenCode / MiMo v1 原生问题走独立本地 `/question` 长轮询,不复用审批回包;遵循全局 permission bubble 开关与 family 单 agent 开关。只接受已核实的 v1 来源、版本和问题 schema,提交前用原 SDK client 复核精确 request/tool/session,答案保持有序 `string[][]`;无决定返回 204 交还原生。OpenCode 由原生 reply RPC 的 `true` / 404 判定提交结果,MiMo 仍需精确 `question.replied` 答案匹配。子会话问题、OpenCode v2、Remote SSH 及未审核 agent 保持原生;不要改 Claude/Hermes wire 或托管 generation 依赖集合。详见 `docs/guides/agent-questions.md`。
- opencode-family v1 插件经 `ctx.client` 连回宿主时,主机为通配监听地址(`0.0.0.0` / `[::]`)就按次改用 `127.0.0.1` / `[::1]`(不改宿主共享的客户端配置;非通配地址保持原调用形状)。默认 TUI 走进程内 fetch;`opencode serve` / `web` 下这是对监听地址的真实 HTTP 请求,受宿主进程里的 `HTTP_PROXY` / `NO_PROXY` 影响,而通配地址不会自动绕过代理。桥接回复失败返回 `{ok:false,status,error}`(`status` 为上游状态码,缺省 0)。
- OpenCode 2.x(npm `@opencode/cli`,issue #1039)由 `hooks/opencode-plugin-v2/` + core.mjs 内 `createOpencodeFamilyPluginV2` 承接:installer 在同一 generation 内额外物化 `opencode-plugin-v2/index.mjs`(v1 四文件、v2 五文件;目录 specifier 解析 index.mjs、无需 package.json,2.0.15 实测),并把它写进 **`plugins` 键**,同时清理 Clawd 自有的旧 `plugin` 条目;用户条目、tuple/options 和 v1 行为保持不变。未被 Clawd 纳管的未知 `plugin` 条目仍只报 loader WARN;v1 对未知 `plugins` 键的静默丢弃只在 **1.18.16+** 成立——1.18.15 及更早会以 `Unrecognized key: plugins` 拒绝整个 config(anomalyco/opencode#41312,2026-08-08 起才忽略未知顶层键),因此 `plugins` 写入由宿主探测决定(`hooks/opencode-host-detect.js` 三态:探测到 v2 才写;探测到 v1 不写并清扫 owned 残留;探测失败不碰该键;`CLAWD_OPENCODE_HOST` 环境变量可钉死结论,Doctor 在无 v2 宿主时不要求 v2 条目)。v2 插件是零 import 纯对象 `{id, setup}`(v2 loader 拒绝函数 default export,`@opencode/...` 裸包名无插件本地 node_modules 不可解析),事件经 `ctx.event.subscribe()`(`session.step/tool/execution.*` 词汇完全换代,未知事件一律忽略),cwd 取事件信封 `location.directory`,标题走 `session.renamed`,context usage 走 `session.step.ended` 的 step tokens(`session.usage.updated` 是 session 累计计费值,不能当占用率)。权限走 **`ctx.permission.hook("evaluate")` 阻塞长连接 POST `/permission`**,决定就是 HTTP 响应体 `{decision: allow|always|deny}`(服务端按 `hook_source === "opencode-plugin-v2"` 路由到 v2 阻塞子分支),只有 `200` + 身份头 + 合法 JSON 才采纳决定,204/超时/断连/非 200/任何错误都不改 effect 回原生 ask UI;配置 allow 也进 hook 但绝不降级;单条 shell 命令以 `tool_input.resource` 传输,服务端 v2 分支会为其别名出 `command` 以接入破坏性操作提醒(`mapOpencodeV2ShellResource`,非 shell 工具不映射;全字符串的多 resource 以换行合并作扫描别名);resources 原样整组转发(对象转 JSON 字符串),不做条数/长度截断,唯一边界是整 body 超 512 KiB 时跳过转发。"Always allow" 无宿主持久化 API,用插件内 per-session 内存规则(随常驻 service 生命周期,气泡对 v2 使用单独文案说明关闭/重开终端不会撤销)。插件跑在常驻共享 service(daemon):`source_pid`/进程树不可信故**整组省略**(终端跳转对 v2 降级),安装/更新后需 `opencode service restart`。`ctx.permission.hook()` 按上游 v2.0.15 类型返回 `Promise<Registration>`:本实例 dispose(setup 返回的 cleanup,宿主也会在插件 scope 关闭时调用)会调用 `registration.dispose()`,注册 Promise reject 只记日志不影响事件订阅。事件订阅**不等待** hook 注册(注册挂起/reject 时状态上报照常),reload **不等待**上一代注销完成;本实例清理先同步完成本地清理(标记 disposed、abort 订阅、身份恢复)再去注销 registration,注册 dispose 有约 2s 上限、超时只记日志,注册未完成就被取代则在注册完成那一刻立即注销,不残留活 hook。上游 v2.0.15 的 trigger 对同名 hook 依次调用每个仍注册的回调、共用同一个可变 event,而我们的注销是异步的,所以 reload 窗口内旧回调可能仍被调用:已退役实例的回调入口检查 `instance.disposed` 后直接返回,不 POST、不改 event。每个 ask 各自转发(`source.id` 是工具调用级、不是 ask 级,同一工具调用会发出多个不同 ask,如 external_directory + edit),不做内容去重。config 顶层重复 `plugins` 键按歧义处理:Doctor 报 `config-corrupt` 不抛异常,register/卸载在首次写入前中止(v1 宿主 sweep 只告警并跳过),v1 `plugin` 键行为不变。v1 1.18.31 tuple 契约、generation 布局、owner/lock 语义不变;`inspectGeneration` 把历史 4 文件集接受为合法 owned-stale 形态以便迁移。真机证据与降级项见 `docs/investigations/opencode-v2-e1-evidence.md`
- OpenCode 的 plugin 注册不再直接指向打包/源码目录:`agents/opencode-family.js` 的 `managedMaterialization:true`(OpenCode true / MiMo 本次 false)gate materialize、ownership classifier、managed Doctor 与 generation cleanup。开启后 register 必须在目标 home 下维护 `<home>/.clawd/integrations/opencode-family/opencode/homes/<configDirHash>/generations/<bundleHash>/` 的内容寻址 generation(OpenCode v2 当前五文件,历史 v1 四文件),同一 generation 可同时物化 v1/v2 入口文件,但不因此在配置中注册两个键;探测到 v2 宿主时只把 `opencode-plugin-v2` 入口写入 `plugins`,并将 `plugin` 键仅用于清理 Clawd 自有旧条目;v1 宿主和探测不到版本时保持原有 v1 注册路径;bundle allowlist 固定为 `<agent>-plugin/index.mjs` / `<agent>-plugin/package.json` / `opencode-family-plugin/core.mjs` / `opencode-family-plugin/session-ids.mjs` / `opencode-plugin-v2/index.mjs`(v2)。owner record(`owner.json`,literal `clawd-on-desk.opencode-family.target`)与 target-scoped `mutation.lock/`(独立 literal `clawd-on-desk.opencode-family.target.lock`)是所有权与 app-present 真相;lock 被持有时不并发写:interactive Install/Repair/CLI/Uninstall/About cleanup 只做一次短而有界、可注入 delay 的重试,startup automatic 不等待直接 skipped;合法且超时 2 倍以上、PID 明确 ESRCH 才允许 atomic quarantine 接管,corrupt/foreign/live/EPERM 一律不接管;release 只删自己 token 的 `lock.json` 与随后为空的精确目录,禁止 recursive cleanup。owner 的 `activeSourceMarker` 必须是 `<activeSourceRoot>/<pluginDirName>/index.mjs`(共享 canonical 身份、Windows 折叠大小写)且为 regular file,目录/dangling/错 plugin 目录/root 不匹配都不算 live。同一 target 的 source owner 冲突(marker 仍存在)一律 `owner-conflict`,不得来回改 config;旧 owner 的源码 marker 已不存在时,新 source 必须在锁内重读并原子接管 owner(config 已 current 也不例外),否则残留死 owner 会让托管 plugin 永久 inert。锁内若 config 与当前 source owner 都已被并发操作收敛,则本次零 mutation 返回 skipped。lock release 失败不得掩盖结果:成功保留 status 并附 warning + 精确 lock path 的 residualPaths,已有 primary error 时保留原错误。basename 本身永不构成所有权;copy 到 managed layout 外的完整 generation 因缺少对应 owner record 而 inert,只有 verified-current-copy 可 Repair。tuple 契约固定为 OpenCode 1.18.31 实测的 `[specifier, optionsObject]`(隔离 `opencode debug config` 证据:第二项必须是普通对象,数组/字符串第二项会被宿主拒绝);重写只替换 `plugin[index][0]`,options 值级保留,绝不写回嵌套 tuple,malformed array 原样保留。`options.pluginDir` 仍是 test-only expected-canonical override(跳过 generation/manifest/boundary,但不跳过 ownership/唯一 entry/read-back),`resolvePluginDir()` 仍返回 source。新建文件按当前用户普通写权限落盘:不得加 readonly/deny-write ACL,不得用 `W_OK`/`r+`/INIT 日志/session 活跃做健康门禁。Uninstall 以“活动注册是否移除”为提交依据(`registrationRemoved` false/null 阻止 Settings 提交,undefined 沿用 legacy status),残留 generation 只产生 warning;清理有效 generation 时必须先在同一 `generations/` 内原子改名为非 canonical `.cleanup-*` 再递归删除,删除失败不得占住原 hash 槽。兼容旧版本留下的半残 canonical 目录时,只允许在 owner 已 released、bounded history 精确证明该 canonical entry、且配置无活动 Clawd entry 的条件下,把它原子隔离为 `.recovery-*` 后重新物化;active/foreign/unproven/canonical-in-use 一律继续 fail closed,绝不原地覆盖或修补可疑 bytes。托管 core 的 orphan inert gate(`evaluateManagedLayoutGate`)必须在 `resetDebugLog()` / PID walk / bridge / handler 注册之前,owner marker 缺失、损坏、agentId 不匹配或 `activeSourceMarker` 不存在时返回空 handler;source-direct 模式保持原行为。MiMo 保持 broad-basename register/unregister/Doctor 契约不变。
- Pi 通过 `~/.pi/agent/extensions/clawd-on-desk` 的 global extension 推送状态;Clawd 对 Pi 是 **state-only**,不接管权限、不弹权限气泡,也不把 Pi 的默认 YOLO 流程改成手动确认
- OMP (oh-my-pi) 通过 `~/.omp/agent/extensions/clawd-on-desk` 的 global extension 推送状态,agent id `omp`,对 Clawd 同样是 **state-only**。它是 Pi 的 fork(包名 `@oh-my-pi/pi-coding-agent`),所以 `hooks/omp-extension.ts` 刻意与 `hooks/pi-extension.ts` 保持逐行对齐(只差 package import、core import 与函数名),进程树遍历与终端识别不得各自漂移;OMP 独有的差异全部收在 `hooks/omp-extension-core.js`。**完成必须走 `session_stop` 候选 + 终态 `agent_end` 提交的两阶段协议** —— `session_stop` 先保留主会话边界,OMP 再汇总所有 extension 的续跑请求;只有后续 `agent_end.willContinue !== true` 才向 Clawd 发完成。不得直接把任一事件单独映射为完成:仅用 `agent_end` 会把 subagent/调度边界误报为完成,仅用 `session_stop` 则会早于 extension 续跑判定。`session_switch` / `session_branch` 要为被切走的旧 session 补发一条合成 `SessionEnd`,否则 Clawd 会留下一条再也不会有人上报的活动行。session id 命名空间是 `omp:<raw>`,且必须始终带 `session_title`(多个交互式会话合法地共用同一个 cwd,缺标题会让列表和跳转目标无法区分)。安装器遇到社区桥接 `~/.omp/agent/extensions/clawd-on-desk-omp.ts` 时必须 fail closed 跳过:两者上报同一批事件,同时存在会双发。当前 `assets/source/agent-icons/omp.png` 是 `fallback: true` 的占位 π 字形,不是 OMP 官方品牌素材,拿到授权素材后应替换
- DeepSeek Harness 首发是 **web profile / Windows-first experimental / plugin-only**;rc.6 已于 2026-08-29 完成 macOS / Windows x64 源码运行的真实 API-backed session/approval smoke(#962);rc.2 Windows packaged-app 证据仅覆盖安装、web boot 与直接 bridge client 的 state/Allow/Deny 往返(#938),不得合并宣称 rc.2 安装包的真实 API 会话已验证。Clawd-managed in-process plugin 监听公开 `session/created`、`session/event`、`session/disposed`、可选 `sessionProjections.onChanged` 与 blocking `approval/request`;标题和上下文仅作为会话元数据同步,不占用生命周期 seq。状态按 session FIFO 上报并携带上游 seq;Clawd 不读取 `$DSH_HOME/storages` projection。普通审批进入独立 DSH `/permission` adapter,只支持人工 Allow Once / Deny;204 / 断连 / DND / disabled 都由 plugin `next()` 交还 DSH 原生 web answerer。`ask_user_question` 始终留在 DSH 原生 provider;DSH 不进入 `KNOWN_PERMISSION_AGENTS`,auto-tools / unattended / per-session grant 全部 DEFER。安装器通过官方 `dsh plugin --profile web add/remove` 管理 immutable marker-owned generation,foreign 同名包 fail closed;版本放行改成按 **小版本族(major.minor)**。维护两张表:一张是唯一写出具体版本/npm artifact/integrity 的已验证 artifact 清单(优先 `@deepseek-ai/[email protected]`(integrity `sha512-EAJ3gPNcVt/uv8X19PMm9NkVhWgT7xXNMk0UKCVm+IQ5rpSQOcsMUa0HWlnYYVybKMsccjcRB21vVVsaXQ6IdA==`,2026-10-04 已做静态接口核对,并在 macOS 源码运行版 Clawd + 真实 `dsh web` 与真实模型上验证了用 Clawd 安装器装出的 generation 的启动同步换代、会话状态、HUD 标题/上下文、Allow/Deny 与消息上的失败标记;其 `tool/result` 把失败标记放在消息 `message.isError` 而不是内容项上,bridge 新旧两种位置都认),并保留 `@deepseek-ai/[email protected]`(integrity `sha512-c0W6Xqc4ChjFcCJkbzPeIxZQdnbKqe+QAcJzWGtogg0ZzsnZRcw3vopMyZ5oZU6E2fmyqGcyDR1sBeiCH4yHcg==`,2026-09-27 已在 macOS 源码运行版 Clawd + 真实 `dsh web` 与真实模型上验证启动同步安装、会话状态、HUD 标题/上下文与 Allow/Deny)、`@deepseek-ai/[email protected]`(integrity `sha512-rmNmzQCg3oIc1z8xH7izRSOuy1TNzq+/NILyfM+7e8DKOyV+yBtg47WEsqR2SiIe1ATec3L/rUa1YhIcfQ2XEg==`)、`@deepseek-ai/[email protected]`(integrity `sha512-UP1UIh6q3Gme/yXRn/QL2P8IsVlv8Shpg22TRJIZPsCRWLm4CBiA1MUvXmJAfsOEETBMLAl+xWPtFw6ICsN3wg==`)与 `@deepseek-ai/[email protected]`(integrity `sha512-brpZfED7ieRa2PQ5tUxMhHrM1pb2CmKFVM/f6yMULBDMicahk+Z2OsHgTwTDnoiZm23Ftu9rQz0NN4pflaoJcg==`)),一张是族表(`0.2` 下限 `0.2.0-rc.2`、`0.1` 下限 `0.1.0-rc.6`)。host 版本经严格解析(拒绝前导零、空预发布标识与 build 元数据)通过、major.minor 命中族、且不低于该族下限即放行;同一族内不同 host 版本共用一个 generation,出现新的小版本必须重新验证并新增一个族。旧的精确 `=<version>` marker 仍按原 range 核对哈希,新 generation 一律写族 range;Install/Repair 按 host 版本所在的族选择目标契约,Uninstall / 手动 npx 命令在 marker 版本已列入清单时钉它自己的 artifact,否则钉同族版本最高的已验证 artifact(无 CLI 且无 marker 时用首选族最新的并记为 assumed);加锁前后比较的是实际检测到的 host 版本,不看族。DSH 的 `metadata_only` 标题/上下文旁路必须核对目标会话的 `agentId` 确为 `deepseek-harness`,撞名的其他 agent 会话直接丢弃。`47f9438` 仅是源码调查基线,不代表任何 tag/commit 映射。
- DSH 每个 canonical `DSH_HOME` 的 mutation lock 只有在 owner/schema/token/PID/timestamp/owner-recorded operation timeout 合法、超过该 owner timeout 两倍、且 OS PID probe 明确为 `ESRCH` 时才允许 atomic-rename takeover;live、`EPERM`、unknown、corrupt/foreign owner 一律拒绝并返回精确 lock path。canonical lock 的 owner write/release 禁止 recursive cleanup。无全局 CLI 时生成的手动 npx generation 必须先在同一锁内写 owned reference,手动 add/remove 命令必须显式 pin shell-quoted canonical `DSH_HOME`;malformed/foreign/concurrently replaced reference 一律 fail closed 并保留 generation
- DSH 桌面版支持(#1119)与 web 共用一个 agent id `deepseek-harness`:web profile 和 DeepSeek Harness 桌面版是两个承载方、各自一个 profile(桌面版是 `$DSH_HOME/profiles/desktop`),但在 Settings 和 Doctor 里仍是**一行**,显示名 `DeepSeek Harness (experimental)`,一次排队操作先 web 后 desktop、一边失败不影响另一边;安装只要有一边核实健康即成功(另一边失败变 warning + 常驻提示),卸载要所有 Clawd 注册都没了才成功。桌面版查找:macOS 用 `/Applications` 或 `~/Applications/DeepSeek Harness.app` 里自带的 `Contents/Resources/runtime/cli/bin/dsh`;Windows 按用户安装,从 `HKCU` 卸载项(显示名以 `DeepSeek Harness ` 开头)与 `%LOCALAPPDATA%\Programs\DeepSeek Harness` 找 `resources\runtime\cli\bin\dsh.cmd`,注册表用 PowerShell 读 UTF-8 JSON、不用 `reg.exe`;Linux 不支持桌面版。静态版本来自 `Info.plist` / 卸载项 `DisplayVersion`,真正动手前跑一次启动器 `--version` 核实。桌面版**永不用 npx**,手动 npx 命令与 manual reference 只属 web。每边角色是 `mutable` / `diagnose` / `not-applicable`:桌面版没装、或 profile 从未初始化(桌面版没打开过)→ 不适用并提示打开一次;foreign 同名包、文件读不了、清理残留、注册查不清、版本不支持、装了不止一个桌面版(`multiple-desktop-installs`)、桌面版找不到但注册还在(`carrier-unavailable`)、桌面版查不清(`desktop-unverifiable`)→ 只报告不动手。启动同步在集成已安装且启用时会把插件加进已初始化的桌面版 profile;桌面版开着也会加载新加的插件,只有插件更新(换了 generation)后要重启。常驻提示在 `hooks/dsh-notices.js`,按 profile 存 `notices-web.json`/`notices-desktop.json`,四种 kind(`restart-required`/`first-install`/`manual-command`/`failed-target`),写不进去只算 warning;提示显示在 Settings → AI Coding Tools 里这一行下面。Doctor 一行详情 `web: …; desktop: …`,状态取适用边里最差,只要有一边能尝试修复且不健康就给修复按钮(`src/doctor-detectors/agent-integrations.js`)。插件在 DSH 里被关掉(依赖在、bundle 行没了)时 `plugin add` 不会重开:显式修复先 remove 再 add(add 前先清掉上游 remove 留下的自有链接,Mac 是符号链接、Windows 是 junction),按 profile 记 `repair-operation-<profile>.json`,中断后下次显式修复续做,启动同步只报 `plugin-disabled-in-dsh`。`generations/` 两边共享,任一边有未处理状态(检查记录、清理残留、坏修复记录或 manual reference、读不了的 profile、符号链接 profile 目录)时清理暂停但不挡安装,卸载残留进 warnings。检查记录按 profile:web `inspection-required.json`、桌面版 `inspection-required-desktop.json`。`plugin add/remove` 写命令两边都超时 300 秒(web 原 120 秒),比上游写锁等待长。上游 `dsh plugin remove` 只清 `link:` 清单和锁、`node_modules` 链接会留下,Clawd 卸载自有插件时确认归属后清掉。Windows 静态查找是同步代码、不起进程,只读异步快照:启动时(不管 DSH 是否启用)、打开 Doctor 前、Agents 页手动扫描时刷新,没快照时桌面版显示"查不清"。安装检测把"找到桌面版"也算装了 DSH(`src/agent-installation-detector.js`)。桥接插件在 apply 时读 `ctx.profileContext?.name`,只有 desktop profile 才在 state / 审批载荷里带 `dsh_carrier: "desktop"`(metadata 不带;web 或未知 profile 一律不带);Clawd 只接受 DSH + `dsh-plugin` + 本机 + 非 WSL 的载体,按生命周期覆盖或清除、不粘滞,权限 fallback entry 与快照(含签名)都带上它。点击桌面版会话时 `getSessionFocusTarget` 在 darwin/win32 返回 `dsh-desktop`(`dsh://open`),分派先 `shell.openExternal("dsh://open")`,被拒绝再按 `discoverDshDesktopSync({ requireUniqueApp: true })` 的兜底要求唯一安装启动界面程序(macOS `open <appRoot>`、Windows 启动 `<appRoot>\DeepSeek Harness.exe`,去掉 `ELECTRON_*`/`NODE_OPTIONS`,不用 CLI 启动方式;两份真实路径不同的有效安装视为有歧义,只要还有核实不了的候选也不自动启动,同一份安装的符号链接只算一份);只能打开应用窗口、不会切到具体对话,web 会话仍不可跳;桌面版启动时会重新打开上次的对话,Clawd 只在这个对话有了动作(提问、工具调用、审批)之后才把它放进 Session HUD(`awaitingActivity` 并入快照 `hiddenFromHud`,但不关跳转;与 Kimi Code 共用同一个标记),Dashboard 照常列出、按钮照常可跳;2026-10-05 macOS 26.6.2 与 Windows 11 x64 源码运行真机验证(桌面版 `0.2.0-rc.2`):别的窗口在前、最小化、关窗后,点击都经 `dsh://open` 把窗口带到最前;协议打不开时的启动兜底只有单元测试。真机证据:2026-10-04 macOS 源码运行(Clawd `ac64d726`,桌面版 `0.2.0-rc.2` + 全局 `@deepseek-ai/[email protected]`)验证了启动同步把插件装进桌面版(与 web 共用 generation)并出现"已装进桌面版"提示、Doctor 一行两边正常,桌面版真实会话的 Allow 建成文件、Deny 后文件内容和 mtime 不变且 Clawd 显示 `PostToolUseFailure`,"知道了"后提示消失,改 bridge 源码(同 Clawd 版本)后启动同步两边报 `generation-conflict` 并提示去 Doctor 修复、修复把两边换到新 generation/删旧/出现"重启桌面版后生效"、重启后事件来自新宿主进程,设置页卸载后两边依赖与 bundle 行都没了、上游残留链接被清、generation 删除、两边提示清空,再在设置页安装两边都重新装上、桌面版重新出现"已装进桌面版"("经设置页首次安装"因此已覆盖),开着 DND 时桌面版的工作区外写文件不弹 Clawd 气泡、返回 no-decision、桌面版弹自己的原生审批框、批准后文件建成且事件照常到达。2026-10-04 Windows 11 x64 经 SSH 用 Node 直跑安装器代码(管理员会话,桌面版在 `%LOCALAPPDATA%` 按用户安装 `0.2.0-rc.2`、全局 dsh `0.1.0-rc.6`,web 在 0.1 族、桌面版在 0.2 族):PowerShell 读注册表找到桌面版、静态模式无快照时报"查不清"且不起进程、自带 `dsh.cmd`(带空格路径)`--version` 返回 `0.2.0-rc.2`、`dsh.cmd plugin --profile desktop add` 经 pnpm junction 装入并核实、记录"已装进桌面版"、两族各用一份 generation 并存、Doctor 一行两边有修复按钮、卸载桌面版清依赖与 bundle 行并清掉 `dsh plugin remove` 残留 junction、只删桌面版那份 generation 而保留 web 在用的;2026-10-04 另在 Windows 上以真 Clawd(Electron,源码运行、用户会话、普通权限)验证:先抓到一个只在 Electron 里出现的 bug(Electron 的 fs 把 `app.asar` 当目录、Windows 安装目录核实要求它是文件,于是在真 Clawd 里桌面版被判成没装,已在 `d8190692` 修成"存在即可");修好后桌面版一直开着(宿主进程未重启),启动同步把插件装进桌面版并记"已装进桌面版",在桌面版新开对话的事件来自那个未重启的宿主进程,**Windows 上桌面版开着也会加载新加的插件**。**未覆盖**:Windows 上桌面版的审批、Windows 上的 Electron 界面(设置页提示、Doctor 窗口、启动预热)、打包后的 app、Linux、HTTP 204 交还与取消、装了多个桌面版。**管理员 junction 观察(推断)**:提升过的管理员进程在 Windows 11 上穿不过非管理员进程建的 junction(PowerShell "untrusted mount point"、Node `UNKNOWN`),该会话里 web 的旧 junction 读不到、web add 失败,桌面版同会话新建的 junction 正常;普通权限运行不受影响;由此推断以管理员身份运行 Clawd 可能把普通权限装好的插件读成缺失,未用以管理员身份运行的 Clawd 直接复现
- OpenClaw 通过 `~/.openclaw/openclaw.json` plugin 路径做 state-only 集成;Phase 1 不做 permission bubble / terminal focus,主要支持本地 `openclaw tui --local`
- Antigravity CLI (agy) 通过 `~/.gemini/config/hooks.json` 做 **state-only** hook 集成(PreInvocation / PostToolUse / PostInvocation / Stop),**不注册 PreToolUse**。agy LLM 会主动调内置 `ask_permission` 工具,触发 agy 自己的 5 选项 native menu(含 "Persist to settings.json" 持久白名单),Clawd 不插手权限决策也不双层确认。`agents/antigravity-cli.js` `capabilities.permissionApproval` / `interactiveBubble` 均为 false。
- Qwen Code 通过 `~/.qwen/settings.json` 做 hook-only 集成(SessionStart / SessionEnd / UserPromptSubmit / PreToolUse / PostToolUse / Stop / Notification / PermissionRequest),支持状态与阻塞式 `PermissionRequest` 权限气泡;`disableAllHooks: true` 时注册条目不会触发。
- QwenWork (千问办公) 通过 `~/.QwenWorkCN/settings.json` 做 **hook-only / state-only** 集成(agent id `qwenwork`;SessionStart / UserPromptSubmit / PreToolUse / PostToolUse / PostToolUseFailure / Stop / Notification / PermissionRequest / PermissionDenied / SessionEnd)。`PermissionRequest` / `PermissionDenied` 只作为观察映射成 `working`(每任务 40+ 次,映射成 notification 会刷屏),hook stdout 恒为 `{}`:Clawd 不产生 allow/deny,不注册 `/permission`,也不进入 permission automation eligibility,唯一决策者是 QwenWork 原生权限流程;`agents/qwenwork.js` 的 `capabilities.permissionApproval` / `interactiveBubble` 均为 false。Windows command 必须保持 `windowsWrapper:"portable"`(QwenWork 经 POSIX shell 执行 command hook),PowerShell `-EncodedCommand` **只**用于识别并原地迁移旧条目,不是当前写入形态。marker 只认 `qwenwork-hook.js`,不得仅凭 `name:"clawd"` 删除用户 hook;混合 entry 中第三方 hook 原样保留。session id 命名空间是 `qwenwork:<raw>`;`~/.QwenWorkCN/workspace/<id>` 这类内部工作区 cwd 不做 basename 回退,避免 HUD/Dashboard 显示内部 ID。当前真实支持平台只有 macOS / Windows 桌面端(官方下载页 https://qwenwork.cn/download 只提供 macOS 14+ / Windows 10+ / HarmonyOS 6.1+,没有 Linux 客户端),因此 `processNames.linux`、resolver linux agent name 均为空,也不进 `src/wsl-deploy.js` 的 WSL Pair 映射。桌面主进程长驻、不代表 active turn,`startupRecoveryProcessNames` 全空、无 startup recovery。安装/卸载走 Settings → AI Coding Tools,或 `npm run install:qwenwork-hooks` / `npm run uninstall:qwenwork-hooks`;卸载必须同时在 `hooks/cleanup-integrations.js` 的 `MANAGED_AGENT_IDS` / `AGENT_CLEANERS` / `byAgent` 里有条目,否则 About cleanup 只会改 prefs、hook 留在磁盘上
- ZCode 通过 `~/.zcode/cli/config.json` 的 `hooks.events.*` 集成全部 7 个支持事件(SessionStart / UserPromptSubmit / PreToolUse / PostToolUse / PostToolUseFailure / Stop / PermissionRequest,无 SessionEnd / Notification)。PermissionRequest 为阻塞式权限审批:hook 长连接 `POST /permission`(HTTP 等待 590s,installer 注册 `timeoutMs: 600000`),有决定时 stdout 返回最小合法 `{"hookSpecificOutput":{"hookEventName":"PermissionRequest","decision":{"behavior":"allow"}}}`(deny 可带 `message`),无决定 / 超时 / 断连 / 任何错误输出 `{}` 并 exit 0,ZCode 回退原生权限流程;`interrupt` / `updatedInput` / `updatedPermissions` / `permissionUpdates` 必须 omit(最小输出 union 按 ZCode 3.5.x 严格 schema 约束;macOS ZCode 3.8.1 已完成真实 hook / 气泡 Allow/Deny 往返,3.5.x PermissionRequest 真机往返未覆盖)。超时单位陷阱:ZCode `timeout` 是秒、`timeoutMs` 才是毫秒,且超时按事件区分(PermissionRequest 600000、state 事件 8000)。Phase 2 只允许人工审批:ZCode 不进入 `KNOWN_PERMISSION_AGENTS`,global / per-session permission automation 在工具面与会话身份审计完成前全部 defer。prefs v14→v15 迁移把 Phase 1 持久化的 `zcode.permissionsEnabled:false` 翻 true(当时开关从未渲染、无用户意图)。配置必须有 `hooks.enabled=true` 才会执行;安装器只在该字段缺失时补 true,显式 `hooks.enabled=false` 或 Clawd 单项 hook 的 `enabled=false` 必须保留,Doctor warning 且不提供会覆盖用户选择的 Fix。ZCode 导入 Claude 配置后,只清理 `.zcode` 中明确引用 `clawd-hook.js` 的 Clawd-owned 条目,绝不改 `~/.claude/settings.json`。安全边界(复核后新增):`/state` 生命周期 sweep 对 zcode(及 qwen/DSH 等 native-fallback adapter)只允许 no-decision,绝不伪 deny;hook 侧 `tool_input` 预算内原样发送、任何截断(长 Bash 尾部 `rm -rf` 场景)或序列化超 512KiB 一律 fail-closed 输出 `{}` 交还原生 UI;无 allow/answer/plan capability 的 interaction(如 ZCode 真实的 ExitPlanMode / AskUserQuestion)本地气泡与远程卡片都不提供决定入口、直接 204;全局气泡关闭只关本地窗口,Telegram/飞书 remote-only 审批仍可用(per-agent gate 关闭才是全退出);installer 遇到 foreign `PermissionRequest` hook 时不注册自己的 blocking hook,若 foreign hook 后出现则移除自己的 managed hook(ZCode 同事件 hook 串行执行、后写覆盖,Clawd allow 会盖掉用户 hook 的 deny),Doctor 报 `permission-conflict` 且不给 Fix;六个 Phase 1 managed hook 全部显式 disabled 时新 PermissionRequest 继承 `enabled:false`;服务端 pre-parse 超限一律断连回退,不伪造 deny
- Qoder / Qoder CN 共用 `qoder` agent,通过 `~/.qoder/settings.json` / `~/.qoder-cn/settings.json` 做 **state-only** hook 集成;各自尊重 `QODER_CONFIG_DIR` / `QODERCN_CONFIG_DIR`,同步所有已初始化目录,不创建缺失版本目录,卸载/cleanup 覆盖两版且只移除 Clawd entries,Doctor 不得让健康的一版掩盖另一版故障。Clawd 只把 `PermissionRequest` / `PermissionDenied` 当 notification 观察,**不替 Qoder 做权限决策**,hook stdout 恒为 `{}`;Windows command 必须保持 `windowsWrapper:"portable"`。session id 命名空间是 `qoder:<raw>`;启动恢复只认 CLI `qodercli` / `qoder-cli` / `qoderclicn`,不认闲置 IDE / CN 桌面进程;本机原生会话标题由 `src/qoder-session-title.js` 低频异步增量读取,通过 `updateSessionMetadata` 更新;SessionEnd / 禁用 / 退出取消旧读取,远端与 WSL 路径不在本机读取。2026-10-18 已核对 Windows CN runtime 1.1.69 的真实 SessionStart / UserPromptSubmit / Stop hooks,用户确认开启 Clawd Qoder 接入后任务状态恢复;macOS/Linux CN 未实测
- TraeCode (Trae CN) 通过 `~/.trae-cn/hooks.json` 做 **hook-only / state-only** 集成。Windows command 必须保持 `windowsWrapper:"encoded"`:Trae 的 Windows Sandbox 会把 hook 命令作为 `trae-sandbox.exe --command-line` 的单个原生参数再次转发,普通引号或 `portable` 形态会在含空格路径处拆分;外层必须保持无引号的 PowerShell `-EncodedCommand`,POSIX 则继续使用普通 quoted command。该约束不适用于经 POSIX shell 执行 Windows hook 的 agent(如 Qoder / QwenWork)。
- MiniMax Code 通过 `<MINIMAX_DATA_DIR 或 MAVIS_DATA_DIR 或 ~/.minimax>/plugins/clawd-state/` 的 **Clawd 独占本地插件**做 **state-only** 集成(manifest 用 `.claude-plugin/plugin.json`,hooks 文档按 MiniMax 的 CLAUDE sourceFormat 解析;不用 `.minimax-plugin/`,那会走 MINIMAX 格式:matcher 必须非空且不支持 exec-form args)。handler 固定 exec-form `command=node 路径 + args=[minimax-hook.js]`(spawn 直执行、无 shell、免引号),`timeout` 单位是秒且只接受 1–10 的整数(越界的 handler 被丢弃而不是钳制;SessionEnd 事件总预算仅 3s),因此 **PermissionRequest 物理上不可阻塞审批、完全不注册**,也没有 Notification 事件;hook stdout 恒为 `{}`(permissionDecision 缺省 abstain)。目录级所有权只认结构化凭据 `.clawd-managed.json`(`{app:"clawd-on-desk",integration:"minimax",managed:true,version:1}`,同 Pi 约定;MiniMax 只读 manifest 声明的文件,会忽略它),且凭据与所有受管理路径(`.claude-plugin/`、`hooks/` 及其文件)都不得是符号链接;目录名、manifest name、文档里的 basename/子串乃至整份文档指纹都永不授权改写或递归删除,缺凭据的目录(包括补丁前预发布构建写出的)一律 fail closed,只能手动删除后重装。staging / removal 目录固定落在数据目录(`plugins/` 之外,MiniMax 会把 `plugins/` 下每个目录项都当插件候选,含点号名):首装在 staging 写全三份文件后一次 rename 发布(只覆盖空目标,非空目录/文件/链接拒绝;空 `clawd-state/` 视为未占用),失败时尽力删除 staging;已拥有的目录原地逐文件更新,中断后仍可证明归属、由下次安装补完。只有 ENOENT/ENOTDIR 算不存在,其他 lstat 错误按无法确认处理,绝不据此宣告卸载。卸载先把已验证目录 rename 出 `plugins/`、复验后再删;移走失败报 error 并保留安装意图,移走成功但删除失败时按已移除提交并在 warnings 里给出残留 `.clawd-minimax-removing-*` 路径,复验失败且移回失败时两个位置都报 `registrationRemoved:null` 并带 `residualPaths`;删不掉且目录仍运行 Clawd hook 时返回 `registrationRemoved:false`(读不清为 `null`),Settings / About 据此保留安装意图。删除后返回前还会复查一次原位置:此时若已有另一个 Clawd 实例重新发布的插件,报“仍在注册”(`registrationRemoved:false`)、不宣称卸载;这次复查是快照,复查之后、Settings 提交之前的安装不在本次结果里(多实例强一致需要覆盖 prefs 提交的跨进程锁,未做)。Settings 界面目前不显示成功结果里的 `warnings` / `residualPaths`:Agents 页安装/卸载只弹“已卸载/已安装”,About 清理只显示计数,只有命令行 cleanup 脚本会打印 warning。进程中途崩溃,或 staging 清理本身失败,可能在数据目录留下 `.clawd-minimax-staging-*` / `.clawd-minimax-removing-*`(MiniMax 不会加载,只能手动删除);`plugins/` 与数据目录跨文件系统(EXDEV)时不支持。判断外来目录是否仍跑 Clawd hook 按 manifest 声明的 hooks 文档和 MiniMax 默认规则检查(含裸文档外壳、反斜杠路径,以及 shell 形式的 command / commandWindows handler);Clawd 不建模的 manifest(根 `plugin.json` / `.minimax-plugin/` / `.codex-plugin/`)或读不清时按无法确认处理。node 探测失败时只保留全部 Clawd handler 一致、basename 为 node、存在且可执行、并通过 `node --version` 有界探测的已记录路径(exec-form 无 shell,Dock 启动的桌面 App 的 PATH 里没有 node),安装器与 Doctor 共用 `resolveDesiredNodeBin`。PostCompact 按 #406 上报 thinking(`trigger:"manual"` 时 idle),不是 attention。hook 的 `agentNames` 必须全小写(resolver 比较的是小写 basename),且不含桌面版的 “MiniMax Code Helper”:拉起 hook 的 NodeService helper 可能在 App 运行中重启,把它当 agent 会误删活会话,识别应落到长驻的主程序;mcode CLI 启动时执行 `process.title = "minimax-code"`,macOS/Linux 上 `ps` 报告的进程名就是 `minimax-code`(按名字匹配),Windows 上仍是 node.exe,靠 `agentCmdlineCheck` 只看 argv 里的脚本位置识别(命令行检查的进程名沿用共享默认 `DEFAULT_AGENT_CMDLINE_NAMES`,已含 Linux 上新版 Node 的主线程名 `mainthread` / `node-mainthread`,MiniMax 不再自带名单)(`node` 后的脚本 basename 是 `mcode`/`minimax-code` 启动器,或脚本路径含 `/@minimax-ai/code/` / `/.minimax-code/`),参数里的目录片段不算——MiniMax 退出时不发 SessionEnd,没有 agent pid 会话就无法按 agent-exit 清理。hook 不触发时的启用步骤在 MiniMax 应用内(`mcode plugin enable clawd-state@local` 或插件面板),磁盘上读不到启用状态,Doctor/Settings 只能提示。session id 命名空间 `minimax:<raw>`,标题走首行 prompt 派生 + 服务端 first-wins(同 traecode)。
- Kimi Code(agent id `kimi-cli`)的 agent pid 靠进程名识别:CLI 启动即 `process.title = "kimi-code"`,libuv 把标题写回原 argv 所占的空间、放不下就截断,所以 macOS/Linux 上 `ps` 与 `pgrep -x` 看到的名字随启动命令长短而变(0.42.0 原生版实测:`kimi` 仍是 `kimi`,`kimi -c` 是 `kimi-co`,`kimi --yolo` 或全路径启动是 `kimi-code`;只有整条启动命令比 `kimi` 还短时(例如不带参数的软链接 `k`)才认不出,`k -c` 显示的已经是 `kimi`)。`hooks/kimi-process-names.js` 必须列全从 `kimi` 到 `kimi-code` 的每一种截断,resolver 与 startup recovery 共用这一组,只写 `kimi-code` 会漏掉 `kimi -c`。拉起 hook 的就是这个改名后的主进程(TUI 在进程内跑 agent 引擎,从 SessionStart 到 SessionEnd 同一个 PID);其他同名进程(后台更新下载器、经 kimi 二进制跑的插件进程等)都是它的子进程,不跑 hook。resolver 比较小写 basename,macOS 桌面版 `Kimi Code.app` 写成 `kimi code`(它的 agent server 跑在主进程里,hook 由主进程直接拉起);桌面版关窗后常驻托盘、不代表在干活,同其他桌面 App 一样不进 startup recovery。Windows 上标题只改控制台标题:原生版仍是 `kimi.exe`,npm 版仍是 `node.exe`,命令行检查只认包目录 `/@moonshot-ai/kimi-code/`,不能退回“含 kimi 就算”(Kimi 本身没被认出时,任何命令行提到 kimi 的 node 祖先都会被当成 agent)。Kimi Code 正常 `/exit` 会发 SessionEnd,但关终端、`kill`(0.42.0 实测:SIGTERM 与 SIGKILL 都不发;源码里 SIGTERM 注册了 `stop(143)`,实测为什么没发 SessionEnd 还没查清)、崩溃都不发;没有 agent pid 的会话只要终端 App 还开着就一直留在列表里。桌面端启动时会恢复上次查看的对话、在侧栏点开旧对话也一样,这类只发一条 SessionStart、还没有任何动作的对话不进 Session HUD,Dashboard 照常列出、按钮照常可跳,出现提问/工具/审批/Stop 等动作后自动进 HUD(与 DSH 共用同一个 `awaitingActivity` 标记)。
- HTTP 服务端口范围固定为 `127.0.0.1:23333-23337`;运行时端口写入 `~/.clawd/runtime.json`
- Mobile Preview 是独立的 LAN listener,候选范围为 `0.0.0.0:23334-23338`;它不写 `runtime.json`,不得把该范围误写成 hook / custom-agent HTTP ingress 范围
- 自定义 HTTP Agent 的 sender 必须读取 `~/.clawd/runtime.json`,不能把 23333 写死;注册不等于已连接。custom v1 只支持 `/state`,不支持 `/permission`
- CodeBuddy PermissionRequest hook 的所有权只认本机 managed URL 或 marker `clawd-on-desk.permission.v1`;纯 `name:"clawd"` 不能触发改写/删除。裸 CLI 和 WSL 默认 preserve,Settings/startup/repair 必须显式传 local/custom permission target
- Remote SSH 的远端 Node 探测要求 Node >= 14;Node discovery/version validation 只在 `src/remote-ssh-node.js`,ordinary tunnel health 与 serialized readiness 在 `src/remote-ssh-runtime.js`,不得互相复制或从已停用脚本另起实现
- 注册 Claude Code hook 必须 marker-scoped merge:只可更新/删除含 `clawd-hook.js` / `auto-start.js` marker 的 Clawd-owned entry,不得整体覆盖数组或改动无 marker 的用户 entry
- 注册 Claude Code statusLine 默认只接管空槽或自己的槽(marker `claude-statusline.js`);本机 Settings 显式开启采集遇到第三方槽时,必须经共存确认并复核原槽指纹,使用 `--local-chain` 和独立的 `clawd-statusline-local-chain.json` 保存/恢复完整原对象。启动/自动修复不得自行取得第三方槽;恢复记录缺失、损坏或不匹配时 fail closed 并保留现场。远程部署仍用 profile 的 `chainStatusline` opt-in 和既有 `--chain` sidecar,不与本机记录混用。订阅配额通过 `metadata_only` POST 进入 session-independent `updateAccountQuota` per-source store;不要把 quota 塞进 `updateSession` opts,也不要以 session 存活作为摄入前提
- Copilot CLI hooks 走按需自动同步:`hooks/copilot-install.js` 在本地启动仅当 Copilot CLI 已安装且已启用时调用;远端由 Settings Remote SSH deploy controller 调用。路径解析尊重 `COPILOT_HOME` env(trimmed 非空才生效,否则 fallback 到 `~/.copilot`);`hooks/copilot-hook.js` 的 session-state resolver 同样走 env
- Remote SSH 的 effective transport 由 `ssh -G` 只读检查决定:ordinary SSH 在没有 retained serialized occupancy 时保持 `context:null` 的 parallel 路径;serialized transport 以有效 target key(不是 profile id)互斥。所有 serialized managed SSH/SCP child 必须持 coordinator 发出的有效 connection/operation context,并通过其 pre-spawn gateway 启动;用户交互终端只有命中 serialized/retained occupancy 时才要求 coordinator 判定 target 完全 idle
- serialized persistent tunnel 使用同一条 SSH 内嵌 readiness;暂停通过 stdin EOF 请求自然退出并等待 `close`。强杀或带 signal 的 outer `ssh.exe` close 不能证明 nested ProxyCommand 已 drain;timeout/未验证 drain 必须 quarantine,期间禁止新 child、mutation、resume 或 interactive terminal
- mutation 在 deploy-lock acquire-attempted / lock-owned 后遇到 exit 255、EOF/reset、signal 或其他 unknown result 时不得自动 replay、retry、release lock 或恢复连接;必须保留 primary error 并传播 recovery state,必要时要求 `manual_lock_inspection_required`
- Remote SSH secure hook 必须同时携带 `CLAWD_REMOTE=1` 与 `CLAWD_SSH_REMOTE=1`,只读 layout identity 并 pin 精确端口;identity 缺失/损坏/不可读必须 fail closed,禁止回退端口扫描
- Remote SSH deploy-lock acquisition 必须原子化;成功 acquire 后的每个 live mutation 都要在同一远端命令内 fencing 校验,cleanup 还必须锁内重读 installId/profileId/runtimeKey/layoutVersion 所有权。无身份、冲突、ownerless/corrupt lock 均不得自动接管
- 默认 `account-default` 只支持不同 Unix 账号;同 Unix 账号冲突必须阻止。`profile-isolated` 在真实 SSH/CLI 矩阵完成前由 `CLAWD_ENABLE_EXPERIMENTAL_REMOTE_ISOLATION=1` 发布门隐藏;它只隔离用户级 CLI config/session/runtime 与 Clawd 路由,不代表完整 HOME 或同 UID 安全隔离,project 配置/部分 cache/macOS Claude Keychain auth 仍可能共享
- Hermes Agent 的 Remote SSH 部署是 `secureDeploy` 内的 `hermes-files` → `install-hermes` 两个阶段,复用同一条 serialized transport、deploy lease、fencing 与所有权检查,没有独立的 Hermes SSH deployer:secure 模式由每个托管 `plugins/clawd-on-desk/` 内的 plugin-local marker `clawd-ssh-secure-v1` latch,latch 后 identity 必须在每次 state/probe/permission 请求时重新读取(禁止 `(mtime,size)` 缓存),且不得发送任何远端 PID / 进程树 metadata;plugin 资产走独立 staging 目录,清理只 `rm -f` 精确文件再 `rmdir`,禁止 `rm -rf`;`REMOTE_IDENTITY_STEP_NAMES` 保持冻结的 8 步不变,不得加入 `installHermes`;Clawd 绝不自动重启 Hermes gateway,托管模块被替换时只报告需要手动重启
- 禁用 agent 不应卸载 hooks / plugins / extensions:只停止对应 monitor、清理 session / bubble、让 HTTP hook 入口快速 fallback;重新启用未安装 agent 不触发本机 integration sync。卸载集成必须走 Settings Agent 页的 Uninstall / 对应 uninstall 命令,并同时清掉 `integrationInstalled`
- Kiro 的 `sessionId="default"` 会复用;session alias key 必须按 cwd scope 区分,同时保留旧 `local|kiro-cli|default` 只读 fallback
- Windows NSIS release 必须产出明确架构的 x64 / ARM64 安装包:`win.artifactName` 保留 `${arch}`,`nsis.buildUniversalInstaller` 保持 `false`
- 每个 release target 只能保留一个匹配目标架构的 Koffi `koffi.node`;`afterPack` 只允许剪裁 `app.asar.unpacked` 的物理文件,禁止重写 `app.asar`,并必须通过完整 native inventory audit(唯一常设例外是 electron-builder 管理的 Windows ia32 `resources/elevate.exe`)
- 资源路径统一用 `path.join(__dirname, ...)`
- 需要编辑发布素材时,先复制到 `assets/source/` 再改,不要直接改工作素材来源不明的文件
- `assets/source/cloudling-pointer-bridge/` 是 Cloudling 指针桥素材的保留源文件目录;运行时逻辑已内联进主题 SVG,不要把这个 source 目录当临时文件清理
- 主题状态、sleep/DND、mini mode、状态映射的细节在 `docs/project/theme-state-ui.md`
- 官方可下载主题(`official-theme-*`)是受管分发层,不是 built-in、也不提升信任:catalog 是固定远端小目录(renderer 只能传 themeId),下载走 Electron main `net.request` 的 manual redirect + 精确 CDN host allowlist + 固定 bytes/SHA-256(未支持 host 返回稳定 `DOWNLOAD_HOST_UNSUPPORTED`,不允许 catalog 扩展 allowlist),ZIP 只在 manager 专属 staging 内流式解压并以 marker-before-rename 同卷提交,最终以 `isBuiltin=false` 加载(`trustedRuntime` 无效)。`setThemeSelection` / `removeTheme` / `officialTheme.commitInstall` / `officialTheme.uninstall` / `theme` update 共用 `lockKey="theme"`;卸载 active 主题必须等 `waitForThemeReloadSettled()`。主仓库附带一份目录快照 `src/official-theme-catalog-snapshot.json`:网络拉取失败时列表先用上一次成功加载的目录(内存 LKG 或磁盘缓存),都没有才用快照;第一次拉取有结果之前(`uninitialized`)不用快照;快照**不作安装依据**,安装仍只认经校验的网络/磁盘目录。快照只作列表回落(仍显示 `offline` 和重试),不含素材与预览图,不写磁盘缓存,且参与版本不能回退的比较;发布前用 `npm run update:official-theme-snapshot` 刷新。不要把 Hash Sage / Whale-chan 的动画素材(APNG / WebP),或把它们写成内置主题的文案带回主仓库
- Settings 体系里,store 是唯一真相,controller 是唯一写入者;不要绕开 `settings-controller.js`
- Dashboard 数字快选是**完整 Dashboard 同一页面的临时键盘模式,仅 macOS/Windows**;Linux 明确 NOT SUPPORTED(不是待验证)。平台 gate 的唯一真相是 `shortcut-actions.js` 的 `supportedPlatforms` + `isShortcutActionSupported()`,Settings 展示、globalShortcut 注册/录制、设置命令和冲突占用都必须服从它;遗留的不支持绑定只忽略执行,不删用户 prefs、不占其他快捷键。darwin/win32 的 Dashboard 页面活在 `WebContentsView` 里,`BrowserWindow.fromWebContents()` 对它返回 null、BaseWindow 不触发 `ready-to-show`;页面 WC 一律从 owner 取。禁止用 `hide()`+`showInactive()` 归还已显示的普通宿主(实测会遮挡来源窗口),只能 opacity/input parking 并幂等恢复捕获值。详见 `docs/project/theme-state-ui.md`
## Testing
- 自动化测试使用 Node 内置 test runner:`npm test`
- `test/*.test.js` 覆盖核心 unit/contract、installer、route、UI/runtime 与 Remote SSH transport/coordinator/identity;部分 fixture 会启动 Node、shell 或 Git Bash 子进程,缺少依赖时可能 skip,不能把全套结果概括成纯逻辑或等价真机验证
- Remote SSH 真机矩阵与记录在 `scripts/manual/README.md`;Windows OpenSSH + Codespaces stdio 边界及 ordinary-host V15 release gate 不能从 unit tests 互相推断,无法执行时必须明确记录 pending / residual risk
- Windows Terminal 真机、GUI smoke 与清理必须假定多个 tab/window 共享进程:不得按 PID/进程名 `taskkill`、`Stop-Process` 或宽泛终止 WindowsTerminal、Codex、`cmd.exe`、`ssh.exe`、OpenConsole、conhost。测试终端只能在明确 test-owned session 内 `exit`;无法证明 ownership 就保留现场并请用户手动关闭
- 当前开发环境是 Windows-first;macOS 特定路径无法在这里手动 QA,改到 mac 逻辑时要用 code-review