What's missing
bsk record produces a semantic trace.json "textbook" of what a user did — navigate, click, fill, press — but the trace says nothing about what those actions caused. An agent reading a trace can see "the user clicked Search", but not which backend call that produced, nor whether it returned 200 or 500.
That gap matters in two common cases:
- Replay / verification. An agent re-running a recorded flow has no ground truth for "did this step actually work?" — UI state alone is ambiguous (a spinner that never resolves looks like a slow success).
- Understanding an unfamiliar app. The most useful thing a recording can teach an agent is the mapping between an interaction and the request behind it. Today that half is simply absent from the trace.
bsk network already captures exactly the missing data — but it can't be joined to a trace after the fact, and I don't think that's fixable from outside:
- Steps carry no timestamp. Reading
crates/bsk-protocol/schema/trace.json: the only time fields are trace-level (started_at, recorded_at, the latter documented as "when recording stopped"). StepV2 is effect / id / op / page / target / … and StepV3 is id / op / result / state / target / … — neither has a time field. So there is no shared axis to align a trace against a network buffer.
- Network entries carry a per-tab sequence, not a step id — the two numbering schemes are unrelated.
- The best an external tool can do is page-granular: bucket requests by URL using
StepV2.page.url (or states[].url in V3). That cannot separate five consecutive clicks on the same page.
Inside the extension none of this is a problem: both event streams already pass through the same CDP connection, so the correlation is known at capture time and only has to be written down.
Proposal
An opt-in flag on the existing command, rather than a new one:
bsk record start --capture-network
The resulting trace gains a top-level array, mirroring how states / pages are referenced by id today:
Requests that fire outside any step (initial page load, background polling) would use "step_id": null.
Scope for a first cut: method + url + status only. Those are exactly the fields bsk network already buffers, so this adds no new capture surface — it is purely a correlation and serialization change.
I'd deliberately leave request headers and bodies out of v1. They'd make the feature far more useful for the "understand an unfamiliar app" case, but they also turn trace.json into a file that can contain auth tokens and business data, where today only fill values on password inputs are redacted. If that depth is ever wanted, it should be a separate, clearly-labelled flag with its own redaction story — not folded into this one.
Possible approach
Everything needed already exists, from the bsk network work in #8:
Network is already enabled on attach and requestWillBeSent → responseReceived / loadingFailed are already buffered per tab with a monotonic sequence.
- The recorder already assigns each step a monotonic
id.
So the change is roughly: while a recording is active, tag each buffered network entry with the id of the step currently in flight, and emit the tagged entries into the trace on record stop. Bounding it the same way the rest of the trace is bounded (a cap on entries, plus the existing noise filtering) would keep the file size predictable.
What I checked
I read crates/bsk-protocol/schema/trace.json (both TraceV2 and TraceV3) to confirm the "no per-step timestamp" claim above, and #8 / #2 for how the network buffer is shaped. I have not prototyped this — unlike #2, there's no working branch behind this issue, so please treat the implementation sketch as a reading of the existing code rather than something I've validated.
中文
缺什么
bsk record 产出的 trace.json 记录了用户做了什么(navigate / click / fill / press),但没有记录这些动作引发了什么。agent 读 trace 只能看到"用户点了搜索",看不到这产生了哪个后端调用、返回的是 200 还是 500。
两个常见场景会被这个缺口卡住:
- 重放与验证:agent 重跑一个录好的流程时,没有"这一步到底成没成功"的依据——只看 UI 是有歧义的(一个永远转不完的 loading 看起来和"慢一点的成功"一样)。
- 理解一个陌生应用:录制能教给 agent 的最有价值的东西,就是"这个交互 ↔ 背后这个请求"的映射。而这一半现在完全不在 trace 里。
bsk network 抓的正好就是缺的这部分数据,但它没法在事后与 trace 对齐,而且我认为这在外部是补不了的:
- 步骤没有时间戳。读
crates/bsk-protocol/schema/trace.json:时间字段只有 trace 这一层的 started_at / recorded_at(后者文档写明是"录制停止时")。StepV2 是 effect / id / op / page / target / …,StepV3 是 id / op / result / state / target / …,都没有时间字段。所以两边没有共同的时间轴可对。
- 网络条目带的是按 tab 的序号,不是 step id,两套编号互不相干。
- 外部工具能做到的极限是页面级:用
StepV2.page.url(V3 用 states[].url)把请求按页面分桶。同一个页面上连续点五下,分不开。
而在扩展内部这根本不是问题:两条事件流本来就走同一条 CDP 连接,关联关系在采集那一刻就是已知的,只差把它写下来。
建议
不加新命令,在已有命令上加一个可选开关:
bsk record start --capture-network
trace 增加一个顶层数组,沿用现在 states / pages 按 id 被引用的写法:
不属于任何步骤的请求(初始加载、后台轮询)用 "step_id": null。
第一版范围只要 method + url + status。 这三样正是 bsk network 已经在缓冲的字段,所以不新增任何采集面,纯粹是关联 + 序列化的改动。
我建议把请求头和 body 明确排除在 v1 之外。它们会让"理解陌生应用"这个场景有用得多,但也会让 trace.json 变成一个可能含鉴权 token 和业务数据的文件——而现在只有密码输入框的 fill 值会被脱敏。如果以后确实需要这个深度,应该是一个单独的、标注清楚的开关,配自己的脱敏方案,不该并进这一个。
一种实现思路
需要的东西 #8 的 bsk network 都已经做好了:
- attach 时已经 enable 了
Network,requestWillBeSent → responseReceived / loadingFailed 已经按 tab 带单调序号缓冲;
- recorder 也已经给每个 step 分配了单调递增的
id。
所以改动大致是:录制进行中时,给每条缓冲的网络条目打上"当前进行中的那个 step 的 id",record stop 时把打好标的条目写进 trace。用和 trace 其余部分一样的方式限界(条数上限 + 已有的噪音过滤),文件大小就仍然可控。
我核过什么
我读了 crates/bsk-protocol/schema/trace.json(TraceV2 和 TraceV3 两版)来确认上面"没有逐步骤时间戳"这一点,以及 #8 / #2 里网络缓冲的形状。我没有做过原型——和 #2 不同,这个 issue 背后没有可跑的分支,所以上面的实现思路请当作"对现有代码的阅读",而不是我验证过的结论。
What's missing
bsk recordproduces a semantictrace.json"textbook" of what a user did —navigate,click,fill,press— but the trace says nothing about what those actions caused. An agent reading a trace can see "the user clicked Search", but not which backend call that produced, nor whether it returned 200 or 500.That gap matters in two common cases:
bsk networkalready captures exactly the missing data — but it can't be joined to a trace after the fact, and I don't think that's fixable from outside:crates/bsk-protocol/schema/trace.json: the only time fields are trace-level (started_at,recorded_at, the latter documented as "when recording stopped").StepV2iseffect / id / op / page / target / …andStepV3isid / op / result / state / target / …— neither has a time field. So there is no shared axis to align a trace against a network buffer.StepV2.page.url(orstates[].urlin V3). That cannot separate five consecutive clicks on the same page.Inside the extension none of this is a problem: both event streams already pass through the same CDP connection, so the correlation is known at capture time and only has to be written down.
Proposal
An opt-in flag on the existing command, rather than a new one:
The resulting trace gains a top-level array, mirroring how
states/pagesare referenced by id today:Requests that fire outside any step (initial page load, background polling) would use
"step_id": null.Scope for a first cut:
method+url+statusonly. Those are exactly the fieldsbsk networkalready buffers, so this adds no new capture surface — it is purely a correlation and serialization change.I'd deliberately leave request headers and bodies out of v1. They'd make the feature far more useful for the "understand an unfamiliar app" case, but they also turn
trace.jsoninto a file that can contain auth tokens and business data, where today onlyfillvalues on password inputs are redacted. If that depth is ever wanted, it should be a separate, clearly-labelled flag with its own redaction story — not folded into this one.Possible approach
Everything needed already exists, from the
bsk networkwork in #8:Networkis already enabled on attach andrequestWillBeSent→responseReceived/loadingFailedare already buffered per tab with a monotonic sequence.id.So the change is roughly: while a recording is active, tag each buffered network entry with the id of the step currently in flight, and emit the tagged entries into the trace on
record stop. Bounding it the same way the rest of the trace is bounded (a cap on entries, plus the existing noise filtering) would keep the file size predictable.What I checked
I read
crates/bsk-protocol/schema/trace.json(bothTraceV2andTraceV3) to confirm the "no per-step timestamp" claim above, and #8 / #2 for how the network buffer is shaped. I have not prototyped this — unlike #2, there's no working branch behind this issue, so please treat the implementation sketch as a reading of the existing code rather than something I've validated.中文
缺什么
bsk record产出的trace.json记录了用户做了什么(navigate/click/fill/press),但没有记录这些动作引发了什么。agent 读 trace 只能看到"用户点了搜索",看不到这产生了哪个后端调用、返回的是 200 还是 500。两个常见场景会被这个缺口卡住:
bsk network抓的正好就是缺的这部分数据,但它没法在事后与 trace 对齐,而且我认为这在外部是补不了的:crates/bsk-protocol/schema/trace.json:时间字段只有 trace 这一层的started_at/recorded_at(后者文档写明是"录制停止时")。StepV2是effect / id / op / page / target / …,StepV3是id / op / result / state / target / …,都没有时间字段。所以两边没有共同的时间轴可对。StepV2.page.url(V3 用states[].url)把请求按页面分桶。同一个页面上连续点五下,分不开。而在扩展内部这根本不是问题:两条事件流本来就走同一条 CDP 连接,关联关系在采集那一刻就是已知的,只差把它写下来。
建议
不加新命令,在已有命令上加一个可选开关:
trace 增加一个顶层数组,沿用现在
states/pages按 id 被引用的写法:不属于任何步骤的请求(初始加载、后台轮询)用
"step_id": null。第一版范围只要
method+url+status。 这三样正是bsk network已经在缓冲的字段,所以不新增任何采集面,纯粹是关联 + 序列化的改动。我建议把请求头和 body 明确排除在 v1 之外。它们会让"理解陌生应用"这个场景有用得多,但也会让
trace.json变成一个可能含鉴权 token 和业务数据的文件——而现在只有密码输入框的fill值会被脱敏。如果以后确实需要这个深度,应该是一个单独的、标注清楚的开关,配自己的脱敏方案,不该并进这一个。一种实现思路
需要的东西 #8 的
bsk network都已经做好了:Network,requestWillBeSent→responseReceived/loadingFailed已经按 tab 带单调序号缓冲;id。所以改动大致是:录制进行中时,给每条缓冲的网络条目打上"当前进行中的那个 step 的 id",
record stop时把打好标的条目写进 trace。用和 trace 其余部分一样的方式限界(条数上限 + 已有的噪音过滤),文件大小就仍然可控。我核过什么
我读了
crates/bsk-protocol/schema/trace.json(TraceV2和TraceV3两版)来确认上面"没有逐步骤时间戳"这一点,以及 #8 / #2 里网络缓冲的形状。我没有做过原型——和 #2 不同,这个 issue 背后没有可跑的分支,所以上面的实现思路请当作"对现有代码的阅读",而不是我验证过的结论。