Files
yaklang-chrome-extension/docs/AUTO_PROFILE_INFERENCE_ARCHITECTURE.md
T

26 KiB
Raw Blame History

Browser Profile 自动推断与 AI 协作架构

1. 产品决定

自动推断 Profile 不是明文网关的辅助功能,而是浏览器现场的默认完成路径。

用户不应先理解混淆变量、复制密钥、编写包装函数,再手工配置参数路径和输出映射。正常流程必须从一次真实业务操作开始:

用户执行登录 / 查询 / 提交
  -> Recorder 生成有界业务 Trace
  -> 确定性推断器关联明文点、页面调用和请求字段
  -> 已知模式直接生成候选
  -> 未知模式请求 AI 解释业务帧和参数语义
  -> 必要时引导用户再执行一次操作以捕获业务闭包
  -> 编译为文档绑定的 Profile
  -> 使用录制样本做页面内回放校验

手写 JavaScript 保留为高级模式,不再作为主流程或文档中的首选方案。

本设计不包含“发送真实 HTTP 请求验证”。真实请求仍由 Yak / Web Fuzzer 的既有数据面负责。本阶段只负责发现、推断、捕获、编译和页面内样本校验。

2. 用户结果

以一次 CryptoJS 调用为例,默认界面应展示:

已识别请求转换

POST /api/login
JSON 明文 -> CryptoJS.AES.encrypt -> body.encryptedData

输入    argument 0 <- 请求明文 JSON
Key     argument 1 <- 页面内 WordArray · 16 B
IV      options.iv <- 页面内 WordArray · 16 B
模式    CBC / Pkcs7
输出    toString -> URL encode -> encryptedData

证据 4 项 · 高置信度
[生成 Profile]

_0x67b862 一类混淆名称只能出现在折叠的原始证据中。主界面使用 KeyIV明文输入请求字段 等语义角色。

用户应能回答三个问题:

  1. 插件为什么认为这是加密链路;
  2. 哪些结论是确定事实,哪些是推测;
  3. 还需要用户执行什么操作才能完成 Profile。

3. 设计原则

3.1 证据先于 AI

指纹相等、请求字段解析、调用顺序、运行时对象类型和调用栈属于确定性证据。AI 不重复判断这些事实,只消费其结构化结果。

3.2 AI 不能成为执行边界

AI 可以:

  • 给业务 frame 排序;
  • 将参数标注为 payload、key、iv、nonce、timestamp 或 signature
  • 从有限源码片段中解释序列化和包装步骤;
  • 在多个候选之间给出理由;
  • 建议下一次捕获点。

AI 不可以:

  • 直接提交任意 JavaScript 作为生产 Profile
  • 引用不存在的事件、frame、参数或页面函数;
  • 读取或输出 key、Cookie、token、密码等原始值;
  • 绕过 grant、document、origin 或人工接管状态;
  • 将猜测标记为已经验证的事实。

3.3 页面是执行环境,不是密钥导出器

Key、IV、CryptoKey、key promise、WASM 实例和闭包变量继续保留在原页面。Profile 只保存页面内 opaque callable 引用和经过校验的参数映射。

3.4 已知模式不依赖 AI,未知模式不依赖库清单

WebCrypto、CryptoJS、JSEncrypt 以及后续 sm-crypto、node-forge 等已知模式,连同 URLSearchParams、JSON、FormData 和常见编码链,应优先由确定性规则推断。AI 只处理业务语义和未知代码,避免增加延迟、成本和不确定性。

录制协议只暴露统一的 crypto 事件,库差异进入结构化 adapterId / providerKind / family / operation / algorithm / mode / padding / encoding / state / key metadata。推断器、时间线、Deep Capture 和 Agent 不再分别判断 webcryptocryptojs 等事件类型。新增密码库时只扩展 MAIN-world adapter、扩展自带的 manifest 和受限元数据归一化器,不扩展整条产品协议。

已知 adapter 只负责提供更准确的参数角色、算法和状态语义,不是通用性的唯一来源。对于 ESM/Webpack 闭包、Worker、WASM 或完全未知的业务封装,系统必须从请求/消息边界和调用栈恢复上层业务 callable;算法尚未命名不能单独成为 insufficient-evidence。adapter 协议的开放化、Worker/MessagePort 边界、高价值库优先级与反靶场特化验收见 FRONTEND_CRYPTO_GENERALIZATION_ROADMAP.md

3.5 无兼容负担

插件尚未正式投入使用。页面配方、运行时适配器和 Transform Profile 可以直接收敛到新模型,不保留旧数据迁移或双写逻辑。

4. 总体架构

MAIN-world Recorder
  | bounded events + opaque handles + semantic argument metadata
  v
Evidence Normalizer
  | request fields / call slots / encodings / exact & normalized links
  v
Evidence Graph
  | proven edges + supported edges + hypotheses
  +-----------------------+
  |                       |
  v                       v
Deterministic Inference   AI Analysis
  | known patterns          | frame ranking / semantic labels / unknown code
  +-----------+-------------+
              v
       Candidate Merger
              | schema validation + evidence reference validation
              v
       Pipeline Compiler v2
              | page callable graph, no arbitrary generated code
              v
       Local Sample Replay
              | deterministic compare or structural assertions
              v
       Document-bound Profile

推断计算在扩展后台完成。Yakit、Options 和 AI Agent 读取同一候选结构,不各自实现一套启发式规则。

5. Evidence Graph

5.1 节点

type EvidenceNode =
  | RecordingEventNode
  | RecordedValueNode
  | RequestFieldNode
  | CallableNode
  | CallArgumentNode
  | StackFrameNode
  | SourceExcerptNode

节点只使用录制会话内稳定 ID。原始敏感值不是图节点属性。

5.2 边

type EvidenceStrength = "proven" | "supported" | "hypothesis"

type EvidenceEdgeKind =
  | "exact-value"
  | "normalized-value"
  | "parent-call"
  | "same-trace"
  | "stack-frame"
  | "argument-role"
  | "request-destination"
  • exact-value:同一录制盐下的指纹完全相同;
  • normalized-value:经过有界白名单转换后相同,例如 URL decode、JSON field extraction 或 Base64 表示;
  • parent-callRecorder 的同步父调用关系;
  • same-trace:弱证据,只证明时间和用户操作相关;
  • hypothesis:只能由 AI 或启发式产生,必须列出依据。

5.3 请求边界归一化

网络事件在边界处解析,不全局 Hook JSON.stringifyencodeURIComponent

  • JSON:递归提取最多 64 层、100,000 节点;
  • application/x-www-form-urlencoded:字段级 URL decode
  • FormData:字段名、字符串值和文件元数据;
  • Headers:规范化名称但保留原始大小写用于展示;
  • Query:字段级解析;
  • 原始 body:保留整体指纹和类型。

归一化候选只允许白名单操作并设置总预算。不得对每个值进行无界编码组合爆炸。

5.4 参数语义

Recorder 对已知库记录参数角色而不是变量名:

interface CallArgumentEvidence {
  index: number
  role: "data" | "key" | "iv" | "algorithm" | "options" | "signature" |
    "salt" | "nonce" | "aad" | "unknown"
  dataType: string
  byteLength?: number
  replaceable: boolean
  retained: boolean
  summary?: string
}

例如 CryptoJS AES

  • argument 0data,可替换;
  • argument 1key,不导出,页面内保留;
  • argument 2options,提取 mode、padding 和 IV 长度,不提取 IV 值。

例如 JSEncrypt RSA

  • argument 0:UTF-8 data,可替换;对象输入按稳定 JSON 序列化后再交给原函数;
  • receiver:保留实际 JSEncrypt 实例,不重建、不导出;
  • key:只记录 public/private、模数位数和本次录制随机加盐的指纹;
  • padding:记录 PKCS1-v1_5 等可解释元数据;
  • output:记录 Base64 形态并与 JSON/Form/Header/Query 请求字段做 exact link
  • 公私钥 PEM、模数、指数和页面实例永不进入候选或 AI 上下文。

如果一次 RSA 输出精确进入一个请求字段,且原函数、receiver 和参数模板仍在当前 document 中,候选可以直接进入 ready,不要求用户填写函数表达式或先进入 Deep Capture。

6. 统一 Page Callable

当前页面配方和深度捕获适配器表达的是同一概念:在当前文档中可重复调用的页面函数。两套注册表应合并为 BrowserPageCallable

interface BrowserPageCallable {
  id: string
  kind: "recorded-call" | "business-closure" | "global-function"
  name: string
  target: BrowserTarget
  lifecycle: "document"
  inputSlots: CallableInputSlot[]
  output: CallableOutputShape
  provenance: {
    recordingId?: string
    traceId?: string
    eventId?: string
    frameId?: string
    sourceUrl?: string
    lineNumber?: number
  }
}

recorded-call 保存原函数、receiver、固定参数模板和可替换槽位;business-closure 保存 CDP 暂停时捕获的业务函数与闭包;两者使用同一执行、授权、生命周期和审计接口。

Profile 不再引用 recipeIdadapterId,只引用 callableId

7. Pipeline v2

手工 JavaScript 中常见的 JSON 序列化、编码、调用和封装应变成可审计的类型化节点:

type PipelineNode =
  | { kind: "context.read"; path: string }
  | { kind: "builtin"; operation: BuiltinOperation; inputs: NodeRef[]; options?: object }
  | { kind: "page.call"; callableId: string; arguments: NodeRef[] }
  | { kind: "output.write"; destination: string; source: NodeRef; encoding: ValueEncoding }

首批 BuiltinOperation

value.literal
json.stringify
json.parse
text.toString
url.encode
url.decode
base64.encode
base64.decode
hex.encode
hex.decode
object.pick
object.compose
form.compose

value.literal 只允许字符串、数字、布尔值或 null,用于编译器生成固定的协议元数据,例如表单 Content-Type。它不接受输入,也不能持有函数、对象或页面秘密。

每个节点有明确输入输出类型和大小预算。未知操作不能通过 AI 临时创造;用户确实需要自定义代码时,进入独立的高级节点,并沿用程序 Eval 的高风险授权。

8. 推断候选

候选不是立即生效的 Profile

interface BrowserProfileInferenceCandidate {
  id: string
  recordingId: string
  traceId: string
  target: BrowserTarget
  request: { eventId: string; method: string; url: string }
  direction: "request" | "response"
  status: "ready" | "capture-required" | "mapping-required" | "insufficient-evidence"
  confidence: { score: number; level: "high" | "medium" | "low" }
  summary: string
  pipeline: PipelineNodeDraft[]
  evidence: InferenceEvidenceRef[]
  missing: InferenceMissingStep[]
  aiContext: BrowserInferenceAIContext
}

置信度不是 AI 的主观百分比。分数由固定规则产生,并在 UI 中解释:

  • 请求字段与加密输出 exact link:强加分;
  • 可重复 callable 已保留:强加分;
  • 同一用户 Trace 且顺序正确:中等加分;
  • 仅时间接近:弱加分;
  • 多个同分候选:降分;
  • 缺少输入映射或输出封装:状态不能为 ready。

9. 自动业务函数捕获

低层 CryptoJS.AES.encryptcrypto.subtle.encrypt 往往不足以构造完整线上报文。推断器应把它作为断点入口,然后寻找上层业务函数。

候选指出需要业务 callable
  -> 用户点击“自动捕获完整加密流程”
  -> 插件在已知低层调用处 arm 一次性断点
  -> 用户重复相同操作
  -> 页面暂停并立即显示控制面
  -> 后台排除 Hook/依赖帧,并使用多来源共同祖先提示排序页面帧
  -> 纯函数用 selected-frame;负责 DOM 取值/组包/发送的函数用 request-transaction
  -> 页面立即恢复
  -> 新明文映射到参数或页面控件,仅返回被拦截的线上 envelope

录制器只从每个来源事件的有界同步栈提取页面帧提示,并对 functionName + script URL 求交集;支持来源更多、平均深度更浅的共同祖先优先。捕获入口选择最早已确认的密码来源,而不是已经离开上层异步函数后的 Fetch 边界。后台再结合真实 CDP scriptId、函数位置、来源分类和副作用检查做最终选择,因此前端不能通过提交 URL、行号或函数名把任意对象伪装成推荐帧。

当最高候选唯一、可解析且未发现副作用时,默认路径使用 selected-frame。如果多个密码来源的最近共同页面祖先本身包含网络、DOM 或条件导航,系统不会跳过它去选更外层的事件 handler,而是建立 request-transaction:保留真实函数、receiver 和固定参数,在页面内替换明文控件,拦截唯一的目标 Fetch/XHR/Beacon/Form,校验所有预期输出字段后回滚 DOM。

存储副作用、多个或未授权请求、无法唯一绑定函数、或共同祖先证据并列时,系统保持页面暂停并解释原因。函数引用表达式只存在于高级模式。页面暂停不等待远程 AI;AI 只能在页面恢复后基于同一份有界证据做解释和候选补丁。

函数捕获后,后台从 Function.prototype.toString 恢复包括默认参数在内的有序参数名。单参数业务函数默认读取整个逻辑 Body;多参数且名称可靠时,引导配置生成 body.<parameter> 读取节点;arg0 这类占位名不会被冒充为已确认字段。Options 同时从已授权暂停帧的 local/block/closure scope 取同名原始值,构造一次性的本地回放 Body。完整暂停作用域始终只存在于当前会话;只有用户明确生成并保存明文网关后,选中的短时样本才会复制到独立的本机回放草稿。该草稿按 profileId + request/response 隔离,不写入 Profile、Bridge、审计、Yak/AI、诊断或导出,并可由用户单独清空。

9.1 多密码调用按请求建图

一个请求可能同时包含 AES ciphertext、RSA-encrypted session key、HMAC signature、nonce 和 timestamp。即使每个低层输出都与请求字段精确匹配,也不能把这些调用分别保存后独立回放:它们可能共享同一随机 key、IV、nonce 或闭包状态。

推断器因此按请求边界合并多个来源,生成一个 request-level candidate

plaintext -----------------> AES.encrypt ----------> body.data
dynamic AES key -----------> JSEncrypt.encrypt ----> body.encryptedKey
canonical request fields --> HMAC.sign ------------> header.X-Sign
                               |
                               +-- 同一上层业务 callable 保证动态值一致

界面展示每个密码调用及其线上目标,但状态固定为 capture-required。用户点击“自动捕获完整加密流程”时,Deep Capture 优先在仍保留上层业务调用栈的密码来源处武装断点,并捕获一次上层业务封装;系统不会把多个看似 ready 的低层调用拆成多个可执行 Profile,也不会误导用户反复缩短已经足够短的录制操作。

9.2 请求事务的输入与输出契约

request-transaction 对明文只暴露一个 body 输入槽,对 Pipeline 返回被页面业务代码生成的整个请求 Body。因此 AES + RSA 之类多输出流程会直接编译为:

context.read(body)
  -> page.call(sendDataAesRsa 请求事务)
  -> output.write(body)

事务保留暂停现场的 URL/event/receiver 等固定参数。逻辑 Body 是对象时,先按 input name/id 向页面控件做同名映射;参数名明确是 payload/data/body/request/params/input 时才直接替换参数。已混淆的单参数如果其保留值解析后等于目标 URL,必须继续保留,不得被明文对象覆盖。

10. AI Agent 集成

10.1 绑定资源

Yakit 从浏览器集成页启动 AI 分析时,附加一个类型化资源:

AttachedResourceInfo.type = browser_session
AttachedResourceInfo.key  = context

Value 只在 Yak 进程内解析,包含 device、grant、document、selected trace 和 candidate ID。渲染给模型的内容只包含安全摘要,不暴露 device token、grant secret 或录制值。

资源必须绑定:

timeline session
AI task
deviceId
grantId
tabId + frameId + documentId + origin
expiresAt

10.2 Agent 工具

不要把几十个 Bridge RPC 原样暴露给模型,也不要提供通用 method + params 工具。首批提供三个领域工具:

browser_observe
  page summary / actionable nodes / trace / inference / status / diff

browser_inference
  list candidates / inspect evidence / arm capture / choose callable /
  propose mapping / compile candidate / local replay

browser_act
  stable node action / tab activation / human handoff

工具回调从当前 AI task 的 browser_session 资源解析绑定,AI 参数中不存在 deviceIdgrantId 或任意 Bridge method。

browser_observe 默认只读;browser_inference 的读取和推断无需额外确认,arm debugger、创建 callable 和发布 Profile 使用现有细分 scopebrowser_act 遵循 Agent review policy 和人机接管状态。

10.3 AI 输出 Schema

AI 只能返回候选补丁:

interface AIInferencePatch {
  candidateId: string
  labels: Array<{ evidenceId: string; role: SemanticRole; reason: string }>
  preferredFrameId?: string
  argumentBindings?: Array<{ slotId: string; contextPath: string; reason: string }>
  suggestedBuiltins?: Array<{ operation: BuiltinOperation; evidenceIds: string[] }>
  unresolved: string[]
}

Candidate Merger 必须验证所有 ID 存在、document 未变化、操作在白名单内、映射路径合法。验证失败只产生新的待处理项,不能退化为执行 AI 代码。

11. UI / UX

录制是入口,自动推断是录制完成后的主结果。三列工作台保持不变:

Trace 列 | 数据流与候选 | 推断证据 / 下一步

右侧主区域按状态显示:

  • ready:一键生成 Profile
  • capture-required:解释原因并提供“自动捕获完整加密流程”;
  • mapping-required:只让用户选择少量无法确定的明文字段;
  • insufficient-evidence:建议重新录制,并明确缺少哪类证据。

证据采用三种强度:

  • 已证实:实线和明确措辞;
  • 有支持:普通文本并展示依据;
  • 待确认:虚线或次级文本,不使用成功色。

AI 是候选的解释者,不单独占据一个聊天面板。主要入口是“让 AI 深入分析”,结果回填到同一证据区域。需要继续对话时再打开 Yakit AI 会话,并携带相同 browser_session 资源。

手工 Pipeline 编辑器移入“高级编辑”,默认只展示推断出的可读流程和少量可修改字段。

默认 Profile 编辑器不是节点画布,而是三个业务决定:

1. 明文从哪里来
2. 交给哪个页面函数
3. 线上结果写到哪里

当第三步选择“写入表单字段”并填写 encryptedData 时,编译器自动生成 form.compose(keys=["encryptedData"])、固定 Content-Type、Header 输出和 Body 输出。用户不需要看到或填写 keys、节点 ID、输入引用和输出引用。已有非规范 DAG 不会被静默改写,只能继续在高级模式中编辑,或由用户明确替换为引导流程。

12. 性能预算

  • 单次快照最多 500 事件、每事件 48 个 evidence
  • 图构建使用 fingerprint/path 索引,目标复杂度 O(E + V)
  • normalized link 每值最多生成 8 个白名单变体;
  • 候选最多 16 个,发送给 AI 的候选最多 3 个;
  • scope 每次最多 8 个 frame,源码片段按需读取并限制总字节;
  • 推断结果按 recordingId + event revision 缓存,增量追加事件时只处理新增部分;
  • 不在页面主线程执行全量源码搜索、AST 构建或全局 JSON/URL 编码 Hook
  • Pipeline 在目标 document 内一次执行完成,每次请求/响应只跨扩展到页面边界一次,不按节点往返;
  • 页面暂停路径绝不等待网络或 AI。

13. 隐私与授权

  • 默认推断只使用指纹、类型、长度、路径、算法摘要和源码位置;
  • 敏感录制预览即使被用户开启,也不自动进入 AI context;
  • Key、IV、CryptoKey 和闭包值只显示语义、类型与长度;
  • 源码片段可能包含硬编码 secret,发送 AI 前先进行字面量脱敏并由用户授权;
  • 推断读取使用 browser.recording.read
  • scope/source 深入读取使用 browser.debugger.read
  • arm/resume 与 callable 创建使用 browser.debugger.control
  • callable 创建、执行与本地回放使用 browser.callable.execute;从暂停 frame 捕获 callable 还需要 browser.debugger.control
  • Profile 发布使用 browser.transform.manage
  • document、origin 或 grant 变化后候选立即标记 stale,不静默重绑。

14. 生命周期与恢复

Profile 是 document-bound。刷新后不能继续调用旧闭包,但推断定义可以保留为恢复计划:

页面刷新
  -> callable stale
  -> Profile disabled
  -> 插件按原 operation / script / route 重新 arm
  -> 用户正常执行一次业务操作
  -> 重新捕获 callable
  -> 本地样本校验
  -> 用户确认后重新启用

恢复计划不保存 key 或源码计算结果,只保存捕获入口、业务 frame 特征、参数语义和映射结构。

15. 分阶段实现

P0:证据与候选基线

  • [已完成] 使用统一 crypto 事件记录 WebCrypto / CryptoJS / JSEncrypt / sm-crypto / node-forge 的 adapter、provider kind、family、调用、参数角色、类型、长度和 state/retained 状态;
  • [已完成] MAIN-world 密码适配器注册表支持稳定 adapter 与运行时晚加载 adapter
  • [已完成] JSEncrypt RSA encrypt/decrypt/sign/verify 保留真实 receiver,并仅输出公私钥类型、位数和加盐指纹;
  • [已完成] 为 CryptoJS 结果补充安全的字符串表示 evidence;
  • [已完成] 从 exact link、请求字段和调用顺序生成只读候选;
  • [已完成] Options / Yakit 展示置信度、证据和缺失步骤;
  • [已完成] 候选结构可通过 browser.recording.get 提供给 Agent。

P1:统一 Callable 与 Pipeline v2

  • [已完成] 删除 recipe / adapter 双模型,不保留旧方法别名或迁移分支;
  • [已完成] 页面 callable 使用统一注册表、来源信息、生命周期和命名 input slot schema
  • [已完成] Pipeline v2 使用有序 DAG,并加入类型化 context.read / builtin / page.call / output.write 节点;
  • [已完成] builtin 限定为 JSON、文本、URL、Base64、Hex、对象和表单组合白名单;
  • [已完成] 输出支持 body、字段级 body、header 和 query,并由 Yak 二次限制 URL 只能改变 query;
  • [已完成] 单条 exact value link 且保留可执行调用句柄的 stateless/receiver 模式可直接编译候选;stateful/stream 模式必须捕获上层 callable
  • [已完成] 同一请求的多个密码来源合并为 request-level candidate,并强制捕获上层业务 callable 以保持动态值关系;
  • [已完成] JSON 字段、表单字段、Header、Query 和完整 Body 会编译为对应的引导式输出,不要求用户理解 DAG;
  • [已完成] 录制短时样本自动填入 Options/Yakit 明文网关本地回放,并允许编辑后恢复原样本;
  • 为页面内回放生成确定性/结构性断言。

P2:自动业务函数捕获

  • [已完成] 候选一键 arm,并在已有捕获等待或页面暂停时拒绝覆盖;
  • [已完成] 业务 frame 使用来源、边界距离、函数可解析性、副作用、命名和作用域信息做确定性排序;
  • 参数槽位与 request context 自动映射;
  • 文档刷新后的引导式重新捕获。

P3Yak AI Agent

  • browser_session attached resource
  • task-bound 三个 Agent 工具;
  • AIInferencePatch schema 与 Candidate Merger
  • Yakit 从候选直接启动带上下文的 AI 会话;
  • Agent 操作写入现有 session timeline。

P4:复杂应用

  • Axios/interceptor、GraphQL、WebSocket frame、protobuf 与自定义 serializer
  • [已完成] 按通用化路线迁移 adapter host,加入 sm-crypto、node-forge 与 Beacon/Worker/MessagePort 边界,并通过随机 ESM + WASM holdout
  • jsrsasign、jose 与后续现代密码生态按真实样本继续推进;
  • sourcemap 存在时的业务 frame 增强;
  • 多候选对比和跨操作共用 callable 识别。

P4 的实现顺序、协议草案、性能门禁和随机化测试矩阵以 FRONTEND_CRYPTO_GENERALIZATION_ROADMAP.md 为准。

16. 验收夹具

至少覆盖:

  1. 固定 CryptoJS AES,混淆变量名,JSON 字段输出;
  2. 真实 JSEncrypt RSA + form-urlencoded data 字段,独立服务端用私钥解密验收;保留实例 receiver,停止录制后对象明文仍可回放;
  3. RSA 候选和 AI 上下文只包含 key 类型、位数与加盐指纹,不包含 PEM 或模数;
  4. WebCrypto AES-GCM + HMAC,闭包内不可导出 key 和动态 nonce/IV
  5. AES + RSA + HMAC 同请求多来源图,不允许拆分低层调用回放;
  6. 动态 key promise,页面刷新后重新捕获;
  7. Axios interceptor 中的请求签名;
  8. Form URL encode 和 Header signature
  9. 自定义业务 wrapper,低层库调用不足以构造完整报文;
  10. 未知函数与多个同分业务 frame,AI 只能补全候选,不能直接执行代码;
  11. WASM 导出函数,只能观察输入输出和业务 wrapper;
  12. 敏感预览开启时,AI payload、审计和诊断仍不含原始值;
  13. 500 事件 / 24,000 evidence 的性能与内存预算。

17. 目标目录

src/features/browser-recording/
  evidence.ts
  recorder.ts

src/features/browser-inference/
  graph.ts
  normalize.ts
  rules/
  candidates.ts
  compiler.ts
  ai-context.ts

src/features/browser-callable/
  registry.ts
  execute.ts
  lifecycle.ts

src/features/browser-transform/
  pipeline-v2.ts
  profile.ts
  replay.ts

Yak 侧将 browser_session 资源解析和 Agent 工具放在独立包中,依赖一个最小的 Bridge caller interface,避免 common/ai 直接依赖 gRPC Server。