@lensagent/core
v0.1.3
Published
Lens Harness kernel and HTTP server.
Readme
lens-core
lens-core 是 LensAgent 的后端 Core / Harness 仓库。当前实际行为以源码、Schema、配置、脚本、测试和可复核运行结果为准。
仓库与运行
仓库根目录本身就是可安装、构建和运行的 Node.js 项目。文件和终端能力由独立仓库 lens-remote-executor 提供;本仓库通过 gRPC 与其通信。两侧各自维护一份 proto/executor.proto,修改 RPC 契约时需要同步。
要求 Node.js 24 或更高版本。
业务正文与 JSON 载荷使用新的 zstd BLOB 存储格式,旧数据目录不能直接打开。格式边界、新目录切换、日志解码与状态重建见 zstd 存储说明。
pnpm install
pnpm run typecheck
pnpm run lint
pnpm run build
pnpm run dev默认配置模板位于 config/lens.example.jsonc。本地运行时复制为 config/lens.jsonc。配置与 config/pair.json 的相对路径均以进程 cwd 为根。
客户端首次连接会登记未批准身份。本机管理员使用 pnpm run pair:approve -- --client <client-id> --strong-trust 批准并签发凭据;已安装包可执行 node dist/entry/pair.js --client <client-id> --strong-trust。省略 --strong-trust 签发只读客户端凭据。命令只返回一次凭据,管理员应通过受保护渠道交给客户端;再次签发会撤销旧凭据。
在 Facet 侧栏输入凭据。凭据只保存在当前页面内存中,HTTP、Artifact 和 SSE 请求统一携带认证。Pair 记录的 strongTrust 默认关闭;单独设置 isVerified 或复制设备身份字段不能获得访问权限。强信任客户端可在 General 中编辑受管理配置文件原文,包括 Pair 信任记录;普通客户端不允许配置原文读取或修改,也不能启动 Agent/工具等可绕过配置权限的操作。配置原文不进入请求日志。
配置文件接口列出 Core 主配置、Pair 文件,以及主配置所在目录中现有的非模板 JSON/JSONC 配置文件。其他配置保存后由其 owner 在重启时读取;API 不接受任意路径。远端组件的独立配置由该组件的本机管理员管理,不通过 Core 猜测远端文件路径。LAN 上传输凭据应使用 HTTPS。
已安装包的入口与构建产物入口:
lens-core
node dist/entry/node.jsExecutor 需要独立启动;pnpm run dev 不会自动启动它。
图片输入
view_image({ "path": "截图.png" }) 通过 Executor 读取工作区图片,支持 PNG、JPEG、GIF、WebP,单张不超过 5 MiB;路径权限与 read_file 一致。新 Agent 默认加载该工具,已有 Agent 可通过 Capability 分配和加载启用。需要同时更新 Core 和 Executor(core-executor-rpc: 8),并选择支持图片输入的模型。
工具和 MCP 返回的图片保存为 Artifact,在下一次模型请求中作为图片内容发送;PTC 子调用的图片也会随根结果传递。图片只以 Artifact 文件持久化;Full Context 仅保存 Artifact ID,Working Context 的图片项仅保存 projectId/artifactId,MIME 和 SHA-256 留在 Artifact 元数据中。Working Context 的 Snip 可以移除较早的图片,手动 Include Result 会重新附带图片。请求快照也只保存 Artifact ID 引用;发送模型请求前才在内存中加载、校验并编码图片字节,不把 base64 或 data URL 回写到 Context、数据库或请求诊断。图片预算目前按每张约 4096 token 估算,实际消耗由模型决定。
本阶段提供工具图片输入,不包含聊天附件上传入口、图片生成、音频或视频。
文件工具
read_file 可读取 UTF-8 文件或列出目录。目录包含隐藏项,名称按序排列,子目录带 / 后缀;响应的 kind 为 file 或 directory。start_line 从 1 开始选择行或目录项,max_lines 限定返回数量;offset 和 next_offset 始终表示 UTF-8 字节位置,配合 eof 分页。目录在分页期间发生变化时应重新读取。
edit_file 合并创建、整文件重写和批量字符串替换:
create:提供content,目标必须不存在;创建父目录并原子发布,拒绝覆盖已有目标。rewrite:提供content,目标必须为现有 UTF-8 常规文件。edit:提供非空edits数组,省略content。每项包含old_string、new_string和可选replace_all,按顺序在内存中替换;默认要求唯一精确匹配,全部成功后提交一次。
patch_file 接受单个 path 与 patch 更新块正文。块头为 @@ 或 @@ 精确行锚点,正文分别用空格、+、- 标记上下文、新增、删除行。所有块基于同一份原文件,必须唯一命中且区间不重叠;*** End of File 限定匹配到文件末尾。没有旧内容的纯新增块仅用于空文件,且只能有一个块。未修改区段保持原字节;新增行采用附近的行尾风格。
edit 最多 128 个替换项,patch 最多 128 个块;输入文本总量、原文件和最终文件分别不超过 8 MiB。成功结果的 structured_content 为 {path, changed, units_applied, bytes_written},未发生内容变化时 bytes_written 为 0。两个工具共用文件历史、调用重放、原子提交和 revert_call。原 write_file 与旧的单替换 edit_file 输入已移除;调用方需使用协议 8 的新合同,在途调用的原始参数不会自动改写。
历史读取
history_search 默认检索当前 Project 内各 Session、Task 和 Agent 的可见 Full Context,包括子代理和 Compact 之前的记录。可用 agent_id、session_id、task_id 筛选。结果携带来源身份与 context_item_id;分页使用返回的 next_after_context_item_id 作为下一次的 after_context_item_id,保持原筛选条件,避免不同代理重复的 ordinal 导致漏读。
history_read 根据 context_item_id 读取上述范围内的完整条目,用 UTF-8 字节 offset/limit 分页,并用 digest 检查连续读取期间的变化。用户隐藏的记录和其他 Project 的记录不开放;历史消息和工具调用仅作为证据,不自动成为当前指令或待执行调用。
工具加载
capability_search 按相关性返回并加载当前 Agent 已分配的匹配能力;新加载的能力从下一次模型请求开始允许执行。原生工具清单与加载状态分开:分配了 programmatic_tool_run 时,默认原生披露 eager 工具与 PTC 入口,延迟工具经搜索结果披露后通过 PTC 调用;localPtc.nativeCapabilities 可指定固定的原生披露名单,空数组表示仅保留 PTC 入口。没有分配 PTC 时,从首轮披露全部已分配工具。名单中的工具即使尚未加载也可见,但调用仍受加载状态、策略和审批约束,搜索加载不会改写原生工具清单。
Skill 工具
skill({}) 列出分配给当前 Agent 的 Skill 名称、描述和加载状态;skill({"name":"名称"}) 返回加载确认、资源基目录和最多 10 个资源文件样本。完整指令按加载顺序在完整工具结果批次之后追加为持久消息,不再插入旧历史前部;工具结果不重复返回完整指令。目录中的符号链接不作为资源遍历。
已加载指令保留内容快照,重复加载、目录刷新和重启不会改写或重复追加;显式卸载后重新加载才采用当前文件内容。后置 system 消息保持历史中的实际位置,包括恢复记录。用户主动编辑上下文、提示词或配置时按其修改生效,不为缓存保留旧内容。
工作区 Skill 可位于 skills/、.agents/skills/ 或 .codex/skills/ 下的子目录。SKILL.md 可用 YAML 前置元数据声明 name 和 description;没有元数据时沿用目录名称和正文标题。文件资源仅在实际加载该 Skill 时枚举,目录刷新不重复扫描所有资源。工作区仍须通过共享文件系统验证。
SDK 输入与缓存
OpenAI Responses 和 OpenAI-compatible Chat 保留消息原有顺序。Anthropic 保留开头连续的 system 提示,并统一把后置控制指令原位转换为带 Lens instruction update: 来源标记的 user 内容块,适配连续追加、重试和历史回放;API 层权限为 user。转换只发生在 SDK 请求编译阶段,持久化上下文仍保留原始角色和内容,后续追加不会改写已转换的前缀。该行为固定启用,无需 Provider 或 Model 配置。
原生工具名使用三种协议共同支持的 ASCII 字符集和 64 字符上限。不符合要求的内部能力 ID(例如 MCP ID)采用稳定摘要别名发送,描述中保留能力 ID 以便识别;流式调用、持久化历史和执行权限仍使用原内部 ID,加载顺序不会改变别名。
OpenAI 显式缓存只在可发送的输入内容块上放断点,包括用户图片和工具结果,不把 assistant 输出当作可写断点。promptCache 的 openai-explicit 模式支持 ttl: "30m" 或 "12h";当前 SDK 的请求级寿命仍为 30m,12h 通过逐断点覆盖发送。诊断记录对应实际断点寿命,不支持的手工寿命会明确拒绝。是否支持显式缓存由配置声明,不根据模型名推断。
Anthropic 缓存按其回溯位置计数:连续工具调用、连续工具结果各算一个位置,在新增位置达到 20 时保留匹配的旧前缀锚点。缓存统计沿用 Provider 返回的读写计数,未返回的值保持未知;本地前缀一致不能代替在线命中证据。
网页工具
web_search 接受 query,并支持 num_results(1–50)、livecrawl(fallback / preferred)、type(auto / fast / deep)和 context_max_characters(1–1048576)。搜索使用公共搜索服务,返回带来源的检索文本;总超时 25 秒。
web_fetch 接受 HTTP(S) url、format(默认 markdown,也可为 text / html)和秒数 timeout(默认 30,最大 120)。超时覆盖域名解析、最多 5 次重定向、响应头与响应体;流式读取和解压后的响应均限制为 5 MiB。Markdown 中的相对链接按最终 URL 解析,二进制响应作为 Artifact 保存。
默认只允许公共网络地址;持有 full_access 的调用可访问私有地址。URL 不接受内嵌凭据,每次重定向重新校验目标并固定连接到已验证的解析地址。网页和搜索结果作为外部证据处理,工具返回内容不授予新的指令权限。
搜索与 full_access 网页获取遵循 Node 的标准代理设置,可通过 NODE_USE_ENV_PROXY=1 或 --use-env-proxy 启用。仅公共地址的网页获取使用直接连接,保证连接地址与本地校验结果一致。
时间工具
get_system_time({}) 返回 unix_ms、utc 和 local,三个字段表示同一时刻。UTC 使用 YYYY-MM-DDTHH:mm:ssZ;本地时间使用 YYYY-MM-DDTHH:mm:ss±HH:mm,仅包含偏移,不包含地区或时区名称。
sleep_milliseconds({"milliseconds":1000}) 在当前工具调用中等待固定毫秒数,然后返回 started_at_unix_ms 和 ended_at_unix_ms。支持取消;0 立即返回,最大值为 2147483647。实际结束时刻可能因事件循环调度延后。
文档
2026-09-19 之前的架构设计、Context、计划、研究、审查、日期快照及相关生成物已归档到 waste/2026-09-19/,不再作为当前规范。
当前保留:
- AGENTS.md — 仓库级工程与 Agent 工作规则。
- CLAUDE.md — Claude Code 的最小入口。
- CONTEXT.md — 当前术语、事实边界与工程共享语境。
- docs/2026-09-19-01/current-system-architecture.md — 基于
5195e0a源码与 CodeGraph 重建的系统架构快照。 - docs/2026-09-21-01/code-quality-review.md — 基于
e7325fd的模型、上下文、工具 schema、外部接入及 HTTP 质量审查、根因和修复建议;建议尚未实施。 - docs/document-lifecycle.md — 文档命名、治理与归档规则。
- waste/manifest.md — 历史归档清单。
不要根据 waste/ 中的历史资料推断当前行为;需要确认时直接检查当前代码和可执行证据。
许可证
本项目使用 MIT License。
本地 LSP 和工作区 Skills 要求在 executor 中显式设置 sharedFilesystem: true。Core 使用短暂的随机文件探针验证 Executor 能读取同一工作区,探针随后删除;未通过验证的路径不用于本地 Skills,LSP 请求明确失败。默认按远端文件系统处理。文件读、写、任意命令和 revert 在单 Core 调度器内使用保守的执行域锁,写操作串行、读操作可并发,不根据 Core 的 OS 猜测远端路径。该锁不约束其他 Core 或外部进程;未引入跨进程分布式锁或新的 RPC。
