diff --git a/README.md b/README.md index d1797004..3515d701 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ly-pi -`ly-pi` 是一个基于 Java 的本地 coding agent,面向代码库理解、文件修改、命令执行、长任务推进和会话沉淀。参考学习了`pi`,`codex`,`claude-code` 的代码实现 +`ly-pi` 是一个基于 Java 的本地 coding agent,面向代码库理解、文件修改、命令执行、长任务推进和会话沉淀。项目参考了 `pi`、`Codex` 和 `Claude Code` 的代码实现。 项目关注 coding agent 工程化中容易变复杂的部分:会话如何恢复,历史如何审计,工具如何受控,模型差异如何收敛,资源如何渐进披露,记忆如何沉淀,子任务如何隔离。代码采用 Maven 多模块结构,使用 Spring Boot 做装配,核心边界通过接口和契约类型定义,便于替换模型适配、工具实现、资源发现和交互入口。 @@ -9,10 +9,11 @@ - **会话可恢复**:会话记录采用追加式 JSONL,分支、压缩摘要、模型切换、thinking 切换、agent mode 和权限运行态都以 entry 表达。 - **上下文可管理**:从当前 leaf 回放会话路径,合并资源快照,按模型 context window 估算预算,并在超过阈值时规划压缩。 - **工具可审计**:默认工具、子代理工具和 MCP 工具都经过注册表、Schema 校验、权限判断、事件发布和结果预算链路。 -- **权限有边界**:`PermissionRuntimeState` 统一表达审批策略、active permission profile、legacy behavior 和兼容权限模式。 +- **三种权限模式**:`ASK` 交给用户确认,`AUTO` 交给模型独立复核,`BYPASS` 面向明确授权的高信任自动化;运行态统一携带 mode、approval policy 和 permission profile。 - **资源渐进披露**:支持 context file、memory、Skill、Prompt Template 和 MCP 配置发现,Skill 正文按需激活。 -- **模型适配收敛**:OpenAI 兼容 Provider 将 Responses、Chat Completions、SSE、WebSocket、thinking 参数和 fallback 行为收敛到内部事件流。 -- **多入口协同**:TUI、Headless 子代理协议和后续入口共享会话、工具、资源、权限和事件模型。 +- **多协议模型适配**:OpenAI 兼容 Provider 覆盖 Responses、Chat Completions、SSE 和 WebSocket;Anthropic Provider 覆盖 Messages 请求和流式事件,两者统一为内部事件流。 +- **异步子代理**:`spawn_agent` 启动 prompt-only child session,completion 通过 `wait_agent` 或模型边界 exactly-once 投递,不复制父对话。 +- **原生终端交互**:稳定 transcript 写入终端原生 scrollback,动态区域保持有界,并支持 active-turn steering、会话投影切换和 provider fallback 状态。 - **后台记忆沉淀**:主 turn 完成后通过事件触发后台记忆 gate,满足 token 增长和工具调用条件时再运行沉淀流程。 ## 架构概览 @@ -24,13 +25,13 @@ | `lypi-contracts` | 公共契约、会话条目、工具描述、错误类型、事件、权限状态、资源结构、子代理协议和 TUI 视图模型。 | | `lypi-session` | 会话创建、恢复、分支查询、JSONL 存储、fork、child session 和工作树 diff 查询。 | | `lypi-agent-core` | 单轮编排、上下文组装、模型交互、工具回合、压缩规划、分支摘要和中断处理。 | -| `lypi-ai` | 模型注册、OpenAI 兼容 Provider、远端模型发现、流式事件归一化、fallback 和 thinking 参数映射。 | -| `lypi-tool` | 工具注册、Schema 校验、权限门禁、批次执行、结果预算、内建工具、MCP 工具适配和沙盒执行。 | +| `lypi-ai` | 模型注册、OpenAI 兼容与 Anthropic Messages Provider、远端模型发现、流式事件归一化、fallback 和 thinking 参数映射。 | +| `lypi-tool` | 工具注册、Schema 校验、人工或模型权限复核、批次执行、结果预算、内建工具、MCP 工具适配和沙盒执行。 | | `lypi-security` | 权限 profile 编译、Bash 风险分析、前缀规则匹配、路径安全、网络策略和审批策略判断。 | | `lypi-resource` | context file、memory、Skill、Prompt Template、MCP 配置发现,以及系统提示词构建。 | | `lypi-runtime` | 事件总线、AgentCenter、mailbox、子进程管理、运行中 agent 快照和后台记忆沉淀。 | | `lypi-transport-headless` | 子代理 stdin/stdout JSON 协议和单次 Headless Run 执行。 | -| `lypi-transport-tui` | 基于 JLine 的终端界面、输入编辑、快捷键、slash command、权限弹层、diff、mention 和渲染。 | +| `lypi-transport-tui` | 基于 JLine 的输入与事件投影、inline terminal rendering、原生 scrollback、steering、slash command、权限与 diff 弹层。 | | `lypi-boot` | Spring Boot 自动装配、配置绑定、启动入口、默认组件图和示例配置。 | 核心原则是上层依赖抽象契约,下层能力通过端口接入。`lypi-agent-core` 不直接绑定具体终端、具体 Provider 或具体工具实现;`lypi-runtime` 只依赖 `lypi-contracts`;`lypi-boot` 负责把默认实现装配成可运行应用。 @@ -60,20 +61,30 @@ Web 工具默认关闭。配置 `lypi.web.enabled=true` 后,运行时会注册 `web_fetch` 和 `get_search_content`;如果 Exa 启用或至少一个商业 provider API key 可用,还会注册 `web_search`。当前 `web_search` 支持 Exa、Tavily、Brave Search 和 Perplexity Search;`web_fetch` 使用本机 HTTP client 抓取公开网页,不依赖商业 provider。 -子代理运行层可用时只注册 `spawn_agent` 和 `wait_agent`。`spawn_agent` 必填 `task_name`、`message`,可选 `tools`、`provider`、`model`、`thinking_level`;不暴露 cwd、权限和 Agent mode。`tools` 只接受已注册的 canonical 工具名,在固定的 `read`、`grep`、`glob` 基础集合上追加并去重。`wait_agent` 只接受可选的 `timeout_ms`,等待当前 session 任意 subagent completion。MCP 工具通过 adapter 映射到内部 `Tool` 契约,并使用规范化名称避免与内建工具直接冲突。 +子代理运行层可用时只注册 `spawn_agent` 和 `wait_agent`。`spawn_agent` 必填 `task_name`、`message`,可选 `tools`、`provider`、`model`、`thinking_level`;不暴露 cwd、权限和 Agent mode。`tools` 只接受已注册的 canonical 工具名,在固定的 `read`、`grep`、`glob` 基础集合上追加并去重。`wait_agent` 只接受可选的 `timeout_ms`,区分 completion、用户 steering、turn abort 和 timeout;timeout 不会终止 child。MCP 工具通过 adapter 映射到内部 `Tool` 契约,并使用规范化名称避免与内建工具直接冲突。 -权限判定以 `PermissionRuntimeState` 为中心,包含审批策略、active permission profile、完整 profile、legacy behavior 和兼容用的 `legacyPermissionMode`。`PermissionMode` 仍用于旧配置、旧 JSON 和 UI 展示兼容,新运行时判定优先读取 `PermissionRuntimeState`。 +公开权限模式由 `lypi.runtime.permission-mode` 选择,默认是 `ASK`: -`request_permissions` 用于请求本轮或本会话 additional permissions。`bash` 只有在对应请求已批准后,才应使用 `sandboxPermissions=withAdditionalPermissions` 扩大 managed sandbox 权限。路径安全、Bash 风险、网络策略、显式规则和人工审批都在统一管线中处理;当沙盒策略无法满足时,工具结果会返回可审计的 retry 提示,而不是自动提权。 +| 模式 | 非只读工具调用的复核方式 | 默认 profile | +| --- | --- | --- | +| `ASK` | 安全策略和工具检查完成后,由交互式 permission gate 请求用户确认。 | `:workspace` | +| `AUTO` | 使用当前模型和有界上下文执行独立的 allow/deny 复核;输出无效、provider 失败或复核中断时拒绝执行。 | `:workspace` | +| `BYPASS` | 跳过人工和模型复核,直接执行;只应在已明确授权的高信任环境使用。 | `:danger-full-access` | -`web_search` 会把 query 或域名发送给配置的 provider;`web_fetch` 会由本机直接访问目标 URL,必要时回退到 Jina Reader。网络 profile 未启用时,工具级权限检查会进入人工审批而不是静默放行。`web_fetch` 会校验 URL scheme、credential、localhost、loopback、private 和 link-local 地址,避免访问明显的本地或内网地址。Jina fallback 复用同一次 `web_fetch` 权限决策,不作为网络权限绕过。 +跨模块仍以 `PermissionRuntimeState` 为 canonical state,统一携带公开 mode、approval policy、active profile 和完整 permission profile;旧权限枚举字符串只在 JSON 读取时兼容。可通过 `lypi.permissions.default-permissions` 选择 `:read-only`、`:workspace`、`:danger-full-access`、`:external` 或自定义 profile。 + +`request_permissions` 用于请求本轮或本会话 additional permissions。`bash` 只有在对应请求已批准后,才应使用 `sandboxPermissions=withAdditionalPermissions` 扩大 managed sandbox 权限。在 `ASK` 和 `AUTO` 下,路径安全、Bash 风险、网络策略、显式规则以及对应的人工或模型复核都经过统一管线;当沙盒策略无法满足时,工具结果会返回可审计的 retry 提示,而不是自动提权。 + +`web_search` 会把 query 或域名发送给配置的 provider;`web_fetch` 会由本机直接访问目标 URL,必要时回退到 Jina Reader。网络 profile 未启用且模式不是 `BYPASS` 时,工具级权限检查会进入人工或模型复核,而不是静默放行。`web_fetch` 会校验 URL scheme、credential、localhost、loopback、private 和 link-local 地址,避免访问明显的本地或内网地址。Jina fallback 复用同一次 `web_fetch` 权限决策,不作为网络权限绕过。 ### 模型适配 -`lypi-ai` 维护模型描述和 Provider 适配。内建 OpenAI 兼容配置可通过 `application.yml` 覆盖或关闭;新增 Provider 和模型时,也通过配置追加到模型注册表。 +`lypi-ai` 维护模型描述和 Provider 适配。内建 OpenAI 兼容配置可通过 `application.yml` 覆盖或关闭;也可以通过配置注册其他 OpenAI 兼容 Provider 或 Anthropic Messages Provider,并把模型追加到统一目录。 OpenAI 兼容适配支持 Responses、Chat Completions、SSE、WebSocket 和 fallback request style。上层收到的是项目内部的 `AssistantStreamEvent`,不需要直接处理供应商原始事件。模型描述中的 context window、最大输出 token、thinking 支持和图片输入支持会影响请求构建与上下文预算。 +Anthropic 适配负责 Messages 请求、SSE 事件归一化、tool call/result 映射和 usage 合并。当前版本不启用 Anthropic extended thinking:Anthropic 模型的 `supports-thinking` 应保持 `false`,作为默认模型时还需把 `lypi.runtime.thinking-level` 设为 `off`。 + ### 资源与记忆 资源运行时按用户层、项目层、嵌套项目层和显式路径发现上下文材料: @@ -98,7 +109,9 @@ completion 进入父 session 的持久 mailbox 后只会被消费一次。父 tu `lypi-transport-headless` 面向单次 child Run,使用 stdin/stdout JSON 协议。输入贯通 task、Agent、child session、Run、父会话、任务提示、工作目录、工具策略和权限运行态;输出返回相同身份、状态、内容、最终 entry 和错误信息。协议要求 stdout 保持结构化 JSON,避免污染父进程解析。 -`lypi-transport-tui` 通过事件 reducer 把语义事件投影成 `TuiViewModel`,再由渲染层展示。终端层包含输入编辑、历史、快捷键、slash command、文件 mention、Skill mention、权限弹层、diff 展示、Markdown 渲染和宽度计算。 +`lypi-transport-tui` 通过事件 reducer 把语义事件投影成 `TuiViewModel`。真实终端渲染路径把稳定 transcript 每个 block 只提交一次到终端原生 scrollback,只重绘有界的 live content、输入框、弹层和状态栏,不再维护应用侧的固定行数历史窗口。 + +主 turn 执行期间提交的新输入会进入 steering 队列,在下一模型边界合并到当前 turn;如果模型正在 `wait_agent`,steering 会唤醒等待但不消费 pending completion。切换或新建 session 时,TUI 会开启新的 transcript projection epoch 并替换当前动态帧;状态栏同步展示 cwd,provider retry/fallback 通过瞬态事件展示而不写入持久 transcript。 ## 快速开始 @@ -151,6 +164,30 @@ lypi-boot/src/main/resources/application.yml.example 用户级配置默认从 `~/.ly-pi/application.yml` 读取;文件不存在时跳过。运行目录中的 `application.yml`、环境变量、JVM 系统属性和命令行参数按 Spring Boot 标准优先级覆盖用户级配置。 +默认权限配置为 `ASK + :workspace`;显式配置示例: + +```properties +lypi.runtime.permission-mode=ask +lypi.permissions.default-permissions=:workspace +``` + +切换到 Anthropic Messages Provider 的最小配置示例: + +```properties +lypi.runtime.default-provider=anthropic +lypi.runtime.default-model=claude-sonnet-4-5 +lypi.runtime.thinking-level=off +lypi.ai.providers.anthropic.enabled=true +lypi.ai.providers.anthropic.api-style=anthropic +lypi.ai.providers.anthropic.base-url=https://api.anthropic.com/v1 +lypi.ai.providers.anthropic.api-key=${ANTHROPIC_API_KEY:} +lypi.ai.providers.anthropic.anthropic-version=2023-06-01 +lypi.ai.providers.anthropic.models[0].model-id=claude-sonnet-4-5 +lypi.ai.providers.anthropic.models[0].context-window=200000 +lypi.ai.providers.anthropic.models[0].max-output-tokens=64000 +lypi.ai.providers.anthropic.models[0].supports-thinking=false +``` + 启用 Web 工具的最小配置示例: ```properties