Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ PR #37 是第一阶段 UI Contribution(插件声明的界面入口)基础设

内置模块和插件尽量实现相同领域 Provider 契约。Build/Run/Test/Debug 等采用类型化 Registry(按能力类型登记和查询实现的目录),组合根负责装配通用适配器,不按新增插件名追加分支。先盘点并复用已有语言服务器、构建工具、运行/测试和 DAP(调试适配器协议)链路;上游拥有的项目、任务和依赖事实仍由原工具提供,避免再造一套解释器、工程模型或调试 UI。

Debug 的统一内核、三层能力、插件声明与迁移验收见[统一调试内核与调试适配器 Registry](2026-10-25-unified-debug-engine-and-adapter-registry.md),跟踪当前仓库的 [#1210](https://github.com/1lck/Lithe-IDEA/issues/1210)。寄存器、内存、反汇编等可选能力另见[可选调试扩展](2026-10-25-optional-debug-extensions.md),插件依赖与导出另见[平台级独立提案](2026-10-25-plugin-dependencies-and-exports.md);两者都不作为 #1210 的前置条件。这些提案没有开放新字段,也不改变其他领域 Provider 的设计。

### 通用 Services 与资源所有权

Services 接入容器、Spring Boot、数据库和本地服务。后续统一设计 ServiceProvider/ServiceDescriptor/Instance/Status/Actions/Logs 的语义;这些名称是设计目标,当前没有公布字段全集或承诺已经实现。
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# Agent 笔记:可选调试扩展

状态:提议中

## 先说结论

除断点、线程与调用栈、变量这些必选能力外,调试功能都是**可选扩展**:寄存器、内存、反汇编、反编译源码、条件断点、子会话等。每个扩展都挂在[统一调试内核](2026-10-25-unified-debug-engine-and-adapter-registry.md)上,由三部分组成:内核请求处理器、它允许内核声明的客户端能力、平台界面入口。扩展各自排期、各自验收,都不是 #1210 的关闭条件。

某个功能是否可用,由三个条件同时决定:适配器在 DAP 协商中声明支持、本平台已注册对应扩展、操作已获授权。插件清单不逐项声明这些功能,最多用 `capabilities.disabled` 关掉一项。

## 问题

GDB、LLDB 可以调试 C、C++、Rust 和汇编,但二进制不一定有源码或行号;Java 调试器可以返回反编译源码;js-debug 会发起子会话。这些功能在 DAP 里都是可选的,不同适配器支持的组合各不相同。如果为了显示它们去改必选层的数据结构,或者按适配器名称写分支,每接一个调试器就要改一次宿主。

现在 Rust Core 已经实现 `setVariable`、各类断点(含数据断点)、`stepBack`、`runInTerminal`;尚未实现 `readMemory`、`writeMemory`、`disassemble`、`setInstructionBreakpoints`、指令粒度单步、`modules`、`loadedSources` 和 `startDebugging`。macOS 的 Node 子会话目前在 Swift 会话里实现。

## 提案

### 扩展分组

| 分组 | DAP 请求与能力 | 内核允许声明的客户端能力 | 界面入口 |
| --- | --- | --- | --- |
| 断点增强 | 条件断点、命中次数、日志点(`supportsConditionalBreakpoints`、`supportsHitConditionalBreakpoints`、`supportsLogPoints`);`setFunctionBreakpoints`;`setExceptionBreakpoints` 与过滤项;`dataBreakpointInfo` / `setDataBreakpoints` | — | 断点编辑、异常过滤、监视点 |
| 表达式与变量 | `setVariable`、`setExpression`、`completions`;变量的 `presentationHint`(只读、懒加载) | `supportsVariableType` | 变量编辑、控制台补全 |
| 执行控制增强 | `stepBack` / `reverseContinue`、`restart`、`restartFrame`、`gotoTargets` / `goto`、`stepInTargets` | — | 工具栏与栈帧菜单 |
| 无源码与生成代码 | `source`(`sourceReference`);`loadedSources` | — | 只读源码标签页 |
| 原生调试 | 寄存器(scope 的 `presentationHint: "registers"`);`readMemory` / `writeMemory`;`disassemble`;`setInstructionBreakpoints`;`granularity: "instruction"` 单步 | `supportsMemoryReferences`、`supportsInvalidatedEvent`、`supportsMemoryEvent` | 寄存器、内存、反汇编视图 |
| 进程与模块 | `modules`;`startDebugging` 子会话;`runInTerminal`;`attach` 到进程列表 | `supportsStartDebuggingRequest`、`supportsRunInTerminalRequest`、`supportsProgressReporting` | 模块列表、子会话树、终端 |
| 语言专属 | Java 热替换(`redefineClasses`)等非标准请求 | 由内置 Provider 描述列出 | 由对应扩展提供 |

### 反编译与反汇编是两件事

- **反编译**由适配器完成:例如 Java 调试器从 jar 里的 class 生成源码,通过 `sourceReference` 返回文本。宿主只用只读标签页显示它,不自己反编译。
- **反汇编**是 `disassemble` 请求,返回指令列表,属于原生调试分组。

两者使用不同的界面,也分别验收。

### 无源码位置

- 栈帧可以没有 `source`、`line`、`column`,只有名称和 `instructionPointerReference`。界面显示“无源码”,有反汇编扩展时可以跳到反汇编视图;不伪造文件,也不跳到第 1 行。
- 源码身份三选一:工作区相对路径、会话内 `sourceReference`、已授权的外部路径。会话结束后 `sourceReference` 失效,标签页转为只读快照或关闭。
- 地址和引用(`instructionPointerReference`、`memoryReference`、`address`)都是不透明字符串,宿主不做算术。确实需要偏移时,由内核按字符串解析为 128 位以内的整数,再生成新字符串。

### 寄存器

寄存器直接使用适配器给的 scope:按 `presentationHint: "registers"` 筛选,适配器没给提示时按普通 scope 显示。宿主不内置任何 CPU 的寄存器表,也不按架构名写分支。

### 写操作单独授权

`writeMemory`、`setVariable`、`setExpression`、热替换都会修改被调试程序。读取能力不代表允许写入:写入类扩展各有独立的授权与确认规则,且只有对应的用例通过后才开放。具体权限名在实施时按能力版本规则申请。

### 引用失效

栈帧、变量、内存、反汇编结果都绑定会话 ID 和暂停代次。继续执行、单步、重启后,界面重新获取;收到 `invalidated` 或 `memory` 事件时,只刷新受影响的区域。晚到的旧结果直接丢弃。

### 实现顺序建议

1. 断点增强、表达式与变量:Rust Core 已有实现,迁到新内核后补界面即可。
2. 无源码与生成代码:Java 反编译源码和 Node eval 代码依赖这一组,同时也是原生调试的前提。
3. 进程与模块:`startDebugging` 要先于删除 Swift 会话完成,否则 Node 子会话会回退。
4. 原生调试:寄存器 → 反汇编 → 只读内存 → 指令断点与单步 → 写内存。

第 3 项实际上是 P5 删除旧路径的前置条件:Node 子会话迁到新内核之前,不能删 Swift 会话。

### 正确做法

- 新增反汇编:内核注册 `disassemble` 处理器,两端增加视图,入口只看能力快照里的 `supportsDisassembleRequest`。GDB、LLDB 自动获得,插件不改。
- 断在没有符号的函数里:调用栈显示函数名和地址,编辑器保持原位;打开反汇编视图后定位到当前指令。

### 不要这样做

- 为了让旧解码器能用,给无源码帧填一个假路径和第 1 行。
- 在宿主里内置 x86 / ARM 寄存器表,或者按 `adapterID` 判断是否显示内存视图。
- 已经能读内存,就顺带允许写内存。
- 适配器没有声明支持时仍显示入口,等用户点了再报错。

## 考虑过的备选方案

- **把原生调试放回 #1210:** 会把 Registry 接入和新界面、权限、多架构验证绑在一起,拖慢必选层落地。已拆出。
- **插件用函数实现扩展:** 插件函数不能实现领域操作,而且会让同一个 DAP 请求出现两条执行路径。扩展只做宿主内核模块。
- **自研反汇编器或内存解析:** 重复上游已有的能力,并且受架构限制。只显示适配器返回的结果。

## 验收标准

- 每个分组单独验收,至少用一个声明支持的真实适配器在两个平台各跑一次;不支持的适配器上,界面入口隐藏,或明确显示为不可用。
- 未注册扩展时,initialize 不声明对应的客户端能力,适配器也不会发来相关引用。
- 无源码帧、超过 2^53 的地址、`sourceReference` 会话结束后失效、继续执行后引用失效、未知 `presentationHint`,都有共享 fixtures,三端解码一致;不出现假跳转。
- 写入类操作有独立的授权和确认用例,拒绝时不发出请求。
- 进程与模块分组通过 Node 子会话的现有基线后,才允许删除 Swift 会话。

## 风险

- GDB 是否带 DAP 取决于版本和构建方式;系统调试授权(macOS 的调试权限、Windows 的进程访问权限)和指令集差异会限制原生调试,未验证的组合保持不可用。
- 适配器声明了能力但实现不完整时,要按请求失败显示错误,不能让会话卡住;所有扩展请求都有期限,并且可以取消。

## 验证

本次只修改文档,运行 `./scripts/verify-agent-notes.sh`,不提供运行时证据。实施时加载 `write-stable-tests`,增加 `shared/fixtures/debug/*-v2.json`,运行 `./scripts/verify-shared-contracts.sh`、`./scripts/verify-rust-core.sh` 和对应平台测试;更新功能矩阵,真实调试流程结束后清理本次启动的进程和资源。

## 适用范围

- `rust/lithe-core/src/debug/`
- `shared/fixtures/debug/`
- `shared/proposals/debug-adapters-1210/`
- `macos/Sources/LitheCoreContracts/Debug/`
- `macos/Sources/LitheDebugModule/`
- `windows/tauri/src/features/debugger/`

## 依据

- [统一调试内核与调试适配器 Registry](2026-10-25-unified-debug-engine-and-adapter-registry.md)
- [DAP 规范](https://microsoft.github.io/debug-adapter-protocol/specification)、[GDB DAP](https://sourceware.org/gdb/current/onlinedocs/gdb.html/Debugger-Adapter-Protocol.html)、[LLDB DAP](https://lldb.llvm.org/use/lldbdap.html)
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Agent 笔记:插件依赖、导出与跨包授权的独立设计范围

状态:提议中

## 先说结论

插件之间复用运行时、JSON 声明和计算能力属于整个插件平台,不应由某个 Debug 格式顺带定义。本提案从 [Debug #1210](https://github.com/1lck/Lithe-IDEA/issues/1210) 拆出依赖、导出和授权,挂在[公共扩展契约](2026-10-13-public-extension-contract-and-provider-api.md)下;Debug 的 Registry 与第三方 DAP 接入不依赖它。

方向仍是插件好写、共享逻辑尽量复用,但安装闭包(某个插件及其全部间接依赖)、版本冲突和信任必须先有明确规则。以下只是待单独实施的决策边界,不开放 `pluginDependencies`、`exports`、跨包函数或工作流字段,也不更改现有私有函数契约。

## 问题

上一版 Debug 草案把依赖、导出、跨包函数和顺序工作流放进专用注册格式,却没有回答安装和卸载如何影响其他插件。仅校验引用存在不能处理菱形冲突(两个依赖分支要求同一个插件的不兼容版本),也不能证明社区调用方有权启动官方依赖里的程序。

现有市场已经负责平台、版本、来源、签名和安装确认,插件函数已经规定私有归属。新的复用机制必须扩展这些所有者,而不是增加第二个安装器、进程表或函数路由器。

## 提案

### 1. 先解决安装和失效,再开放导出调用

| 主题 | 拟采用的规则 |
| --- | --- |
| 插件身份与来源 | 依赖绑定插件 ID、允许版本范围及获准来源/发布者;不能仅按 ID 从任意市场寻找同名替代 |
| 安装解析 | 市场先按平台、宿主能力及信任策略过滤版本,再求整个依赖闭包;下载前展示统一计划,验证包内依赖与索引一致 |
| 单版本约束 | 同一插件 ID 只安装一个版本。多个范围取交集;交集为空就拒绝本次安装或更新,并显示两条冲突路径,不自动降级或替换其他已安装插件 |
| 固定结果 | 安装计划固定每个包的版本、来源和内容哈希;执行前重查目录快照。闭包全部准备、校验完成后一起发布,失败恢复旧闭包,不留下半安装状态 |
| 更新 | 显式更新重新计算所有受影响的反向依赖;旧约束不满足时整笔拒绝。用户可以另行批准一组兼容更新,不暗中更新无关插件 |
| 禁用与卸载 | 先算反向依赖闭包,提示受影响的插件。默认拒绝破坏仍启用的依赖方;用户确认后先停依赖方操作和资源,再禁用/卸载目标 |
| 启动时发现缺失 | 保留依赖方安装记录,标记阻塞及具体原因,不启动后端;恢复依赖后重新校验再允许激活 |
| 循环与预算 | 安装依赖和声明导出引用分别检查循环、深度及总量;具体上限、稳定错误码、并发安装锁与崩溃恢复日志是正式契约的前置验收 |

第一版先不加入弱依赖、可选依赖和并存多版本,避免不能证明的解析语义。运行时动态缺失不是静默选择其他依赖版本的理由。

### 2. 信任不沿依赖关系传递

每个包独立验证签名、哈希、来源和 namespace。社区插件依赖官方包不会因此变为官方插件,官方包的签名也不是对调用方的背书。安装确认展示闭包新增包、各自权限、来源以及哪些公开导出将被调用;不能把间接依赖藏在顶层插件的一行名称后面。

若未来导出可以执行程序,审查必须同时展示调用者、资源所有者和实际执行的包/命令。被依赖方拥有可执行资源与进程生命周期,宿主依据导出用途、本次用户动作、工作区信任和双方授权批准执行;依赖方不能通过传路径替资源所有者更换程序。依赖更新后的资源也要重做来源、签名、权限和计划代次检查。

权限不做简单并集,也不能简单取交集后假装已解释语义:每个导出必须定义可接受的调用上下文及所需操作授权。某个导出没有这种定义时,不允许跨包调用,即使两个包都已安装。

### 3. 导出是明确选择的契约,不是公开全部函数

复用对象可以是只读声明、运行时描述或有类型的操作,但不能直接公开依赖包的私有函数表。包版本回答“装哪版”,导出契约版本回答“交换什么输入和结果”;不可兼容的变更需要新导出契约。

若需要跨包资源引用,正式契约使用显式带标签的类型,例如专门的 backend/lease 变体;不靠普通参数恰好叫 `backendRef` 来识别。宿主签发的租约记录资源所有者、调用链、操作与失效代次,跨包只传受限引用;`${pluginDir}` 永远按声明资源的包解析。用户参数里的同名字符串和对象仍然只是数据。

跨包操作只能由宿主在已授权的导出边界调度到资源所属会话,不能将调用方 pluginID 写入目标会话。后端回调宿主重新进入注册表的禁止规则保留;否则等待中的处理器可能等待自己。如何将新授权模型加入函数路由、如何区分私有与公开调用,需要单独版本化,不能删除旧 `wrongPlugin` 或 `recursiveCall` 检查来实现。

### 4. 不在此处偷改已实现决策

[插件函数契约](../../implemented/architecture/2026-10-20-plugin-function-contract.md)仍是当前行为真源:函数私有,领域结果走类型化契约。此提案没有修改它。若以后采用函数传输领域结果或依赖导出,先在实现 PR 中修订对应决策,给出输入/结果、授权、错误优先级与兼容 fixtures,再改 Core 和双端宿主。

新的字段属于统一 `plugin.json` 的能力演进;基础权限或身份语义变化需要新的基础 API。没有 Debug draftVersion,也不要求现有 Build/VCS/UI/语言插件迁移。现有 `backend: 1` 的全局、网页触发行为不变;新的进程共享方式另行版本化。

通用 workflow 尚无证明必要的真实输入,不在这个依赖提案里重新引入 steps 或取值语言。先通过实际导出契约完成复用,再判断是否有需要由宿主解释的公共流程。

### 正确做法与反例

正确做法:两个插件依赖同一个运行时,安装器先求兼容版本并展示共同资源的来源与权限,宿主只对获准导出执行调用。

反例:社区插件依赖官方运行时后,自动继承官方身份和全部权限;或冲突时静默换掉另一个已安装插件的依赖。签名和版本检查都不能支持这种行为。

## 考虑过的备选方案

- 每个领域自己定义依赖和安装:短期好实现,但同一插件的版本、信任和卸载会出现多套答案,不采用。
- 同 ID 多版本并存:可缓解菱形冲突,但影响设置、存储、市场、UI 身份和进程共享,第一版不承担这一范围。
- 依赖即授权、所有函数自动导出:声明短,但让社区包借到依赖方权限,破坏现有私有契约,不采用。
- 只允许声明复用,不允许动态导出:更易验证,可作为第一阶段;是否增加动态操作须由真实插件证明,不为了“完整”预先开放。

## 验收标准

- 独立任务/PR 完成安装解析、版本冲突、来源固定、哈希校验、事务恢复及并发安装规则,之后才公布新能力。
- 有共同兼容版本与不兼容菱形两组标准用例,冲突报告带确定排序的两条依赖路径;失败不修改原安装闭包。
- 禁用/卸载、启动时依赖缺失、升级失效及恢复分别验证反向依赖与资源清理;任何失败不误停不相关插件。
- 社区依赖官方、同名包来自不同来源、签名变化和导出撤回均有拒绝输入;旧私有函数的归属和递归拒绝语义不变。
- 如涉及函数协议,在 `lithe-plugin-docs` 验证 Node/Java SDK;正式契约与 Core 一起落地后再同步副本。
- #1210 的基础 Registry 和第三方 DAP 验收可以完全不安装依赖包而完成。

## 风险

依赖安装事务和信任会扩大影响面,必须单独评审,不能靠一个 Debug schema 推定已解决。单版本第一版会拒绝部分组合,但比静默替换可靠;多版本共存、导出目录及资源授权的完整 wire 形状仍待后续设计,不属于本次交付。

## 验证

本次只运行 `./scripts/verify-agent-notes.sh` 和文档检查,没有依赖解析器或跨包调用实现,不能声称安装或授权用例通过。实施时使用 `write-stable-tests`、`./scripts/verify-shared-contracts.sh`、`./scripts/verify-rust-core.sh`,并分别验证两个宿主和函数 SDK;新增资源时遵守工作树复用与安装目录只读要求。

## 适用范围

- `rust/lithe-core/src/plugins/`
- `rust/lithe-core/src/lsp/interface/plugin_function.rs`
- `shared/contracts/plugins/v1/`
- `macos/Sources/Lithe/Platform/MacOS/`
- `windows/tauri/src-tauri/src/plugin_host/`
- [插件契约与 SDK 仓库](https://github.com/Lithe-IDEA/lithe-plugin-docs)
Loading
Loading