feat(protocol): negotiate Trace v3 with v2 compatibility

Define Trace v3 wire types and generated schemas while keeping protocol
1.1 peers compatible with 1.0. Default record start still produces Trace
v2, and only an explicit trace_version=3 request enables the new shape.
Refs #90

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
“haonan”andCursor authored and GitHub committed 2026-08-22 17:01:15 +08:00
1 parent 2719e76438
commit 954078380c
32 files changed
+2573 -671

No files matched your search

+191 -131
View File
@@ -5,45 +5,65 @@
"type": "object",
"required": [
"entry",
"pages",
"recorded_at",
"steps"
"recorder",
"states",
"steps",
"stopped_by",
"version"
],
"properties": {
"entry": {
"$ref": "#/definitions/TraceEntry"
},
"pages": {
"type": "array",
"items": {
"$ref": "#/definitions/PageRef"
}
},
"purpose": {
"description": "Optional user-provided goal from `--purpose` (metadata only).",
"type": [
"string",
"null"
]
},
"recorded_at": {
"description": "RFC 3339 timestamp when recording stopped.",
"type": "string"
},
"recorder": {
"$ref": "#/definitions/RecorderInfo"
},
"started_at": {
"type": [
"string",
"null"
]
},
"states": {
"type": "array",
"items": {
"$ref": "#/definitions/TraceState"
}
},
"steps": {
"type": "array",
"items": {
"$ref": "#/definitions/Step"
}
},
"stopped_by": {
"$ref": "#/definitions/StopReason"
},
"version": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
},
"definitions": {
"FillCommit": {
"type": "string",
"enum": [
"enter",
"suggestion",
"blur"
]
},
"KeyModifier": {
"description": "Keyboard modifier flags. Multiple flags may be combined; the extension folds them into CDP's bitfield (`alt=1, ctrl=2, meta=4, shift=8`).",
"type": "string",
@@ -54,25 +74,32 @@
"shift"
]
},
"PageRef": {
"description": "Page context dictionary entry — referenced by steps via `page` id.",
"NavigationCause": {
"type": "string",
"enum": [
"user_typed",
"link",
"form_submit",
"reload",
"history",
"script",
"browser"
]
},
"RecorderInfo": {
"type": "object",
"required": [
"id",
"url"
"bsk",
"vom"
],
"properties": {
"id": {
"bsk": {
"type": "string"
},
"title": {
"type": [
"string",
"null"
]
},
"url": {
"type": "string"
"vom": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
}
}
},
@@ -101,21 +128,16 @@
"description": "Fields shared by every step variant (flattened in JSON).",
"type": "object",
"required": [
"cause",
"id",
"op",
"page",
"result",
"state",
"to"
],
"properties": {
"effect": {
"anyOf": [
{
"$ref": "#/definitions/StepEffect"
},
{
"type": "null"
}
]
"cause": {
"$ref": "#/definitions/NavigationCause"
},
"id": {
"type": "integer",
@@ -128,8 +150,11 @@
"navigate"
]
},
"page": {
"description": "Reference into `pages[]`.",
"result": {
"$ref": "#/definitions/StepResult"
},
"state": {
"description": "Observation id immediately before this action.",
"type": "string"
},
"to": {
@@ -143,20 +168,11 @@
"required": [
"id",
"op",
"page",
"result",
"state",
"target"
],
"properties": {
"effect": {
"anyOf": [
{
"$ref": "#/definitions/StepEffect"
},
{
"type": "null"
}
]
},
"id": {
"type": "integer",
"format": "uint32",
@@ -168,8 +184,11 @@
"click"
]
},
"page": {
"description": "Reference into `pages[]`.",
"result": {
"$ref": "#/definitions/StepResult"
},
"state": {
"description": "Observation id immediately before this action.",
"type": "string"
},
"target": {
@@ -183,20 +202,11 @@
"required": [
"id",
"op",
"page",
"result",
"state",
"target"
],
"properties": {
"effect": {
"anyOf": [
{
"$ref": "#/definitions/StepEffect"
},
{
"type": "null"
}
]
},
"id": {
"type": "integer",
"format": "uint32",
@@ -208,8 +218,11 @@
"hover"
]
},
"page": {
"description": "Reference into `pages[]`.",
"result": {
"$ref": "#/definitions/StepResult"
},
"state": {
"description": "Observation id immediately before this action.",
"type": "string"
},
"target": {
@@ -221,22 +234,17 @@
"description": "Fields shared by every step variant (flattened in JSON).",
"type": "object",
"required": [
"commit",
"id",
"op",
"page",
"result",
"state",
"target",
"value"
],
"properties": {
"effect": {
"anyOf": [
{
"$ref": "#/definitions/StepEffect"
},
{
"type": "null"
}
]
"commit": {
"$ref": "#/definitions/FillCommit"
},
"id": {
"type": "integer",
@@ -249,15 +257,15 @@
"fill"
]
},
"page": {
"description": "Reference into `pages[]`.",
"type": "string"
},
"redacted": {
"type": [
"boolean",
"null"
]
"type": "boolean"
},
"result": {
"$ref": "#/definitions/StepResult"
},
"state": {
"description": "Observation id immediately before this action.",
"type": "string"
},
"target": {
"$ref": "#/definitions/TargetDescriptor"
@@ -273,21 +281,11 @@
"required": [
"id",
"op",
"page",
"selection",
"result",
"state",
"target"
],
"properties": {
"effect": {
"anyOf": [
{
"$ref": "#/definitions/StepEffect"
},
{
"type": "null"
}
]
},
"id": {
"type": "integer",
"format": "uint32",
@@ -299,9 +297,8 @@
"select"
]
},
"page": {
"description": "Reference into `pages[]`.",
"type": "string"
"result": {
"$ref": "#/definitions/StepResult"
},
"selection": {
"type": "array",
@@ -309,6 +306,10 @@
"$ref": "#/definitions/SelectedOption"
}
},
"state": {
"description": "Observation id immediately before this action.",
"type": "string"
},
"target": {
"$ref": "#/definitions/TargetDescriptor"
}
@@ -321,19 +322,10 @@
"id",
"key",
"op",
"page"
"result",
"state"
],
"properties": {
"effect": {
"anyOf": [
{
"$ref": "#/definitions/StepEffect"
},
{
"type": "null"
}
]
},
"id": {
"type": "integer",
"format": "uint32",
@@ -357,8 +349,11 @@
"press"
]
},
"page": {
"description": "Reference into `pages[]`.",
"result": {
"$ref": "#/definitions/StepResult"
},
"state": {
"description": "Observation id immediately before this action.",
"type": "string"
},
"target": {
@@ -372,48 +367,74 @@
]
}
}
},
{
"description": "Fields shared by every step variant (flattened in JSON).",
"type": "object",
"required": [
"id",
"op",
"result",
"state"
],
"properties": {
"id": {
"type": "integer",
"format": "uint32",
"minimum": 0.0
},
"op": {
"type": "string",
"enum": [
"scroll"
]
},
"result": {
"$ref": "#/definitions/StepResult"
},
"state": {
"description": "Observation id immediately before this action.",
"type": "string"
}
}
}
]
},
"StepEffect": {
"description": "Observed navigation after a step (objective fact only).",
"StepResult": {
"type": "object",
"required": [
"navigated_to"
"state"
],
"properties": {
"navigated_to": {
"description": "Reference into `pages[]` for the destination page.",
"state": {
"type": "string"
}
}
},
"StopReason": {
"type": "string",
"enum": [
"user_finish",
"cli_stop"
]
},
"TargetDescriptor": {
"description": "Stable semantic handle for an interacted element.\n\n`name` and `nearby_label` are **untrusted page text**.",
"description": "Stable semantic handle for an interacted element within a page observation.",
"type": "object",
"required": [
"tag"
],
"properties": {
"ctx": {
"type": [
"string",
"null"
]
},
"name": {
"type": [
"string",
"null"
]
},
"name_attr": {
"type": [
"string",
"null"
]
},
"nearby_label": {
"type": [
"string",
"null"
]
},
"placeholder": {
"ref": {
"type": [
"string",
"null"
@@ -425,8 +446,8 @@
"null"
]
},
"tag": {
"type": "string"
"unmatched": {
"type": "boolean"
}
}
},
@@ -441,6 +462,45 @@
"type": "string"
}
}
},
"TraceState": {
"description": "Page observation dictionary entry — referenced by steps via `state` / `result.state`.",
"type": "object",
"required": [
"id",
"url"
],
"properties": {
"body": {
"description": "Wire-only: full page observation (front matter + VOM body + annotations).",
"type": [
"string",
"null"
]
},
"id": {
"type": "string"
},
"page": {
"description": "Disk-only: filename under the bundle `pages/` directory.",
"type": [
"string",
"null"
]
},
"title": {
"type": [
"string",
"null"
]
},
"truncated": {
"type": "boolean"
},
"url": {
"type": "string"
}
}
}
}
}