Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/convergence/protocol-registry.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ This document is generated from [`protocols/registry.json`](../../protocols/regi
| [exported-evidence-and-handoff](#exported-evidence-and-handoff) | `external-durable` | Evidence, verification, report, and handoff maintainers | 35 | true |
| [extension-package-contracts](#extension-package-contracts) | `external-durable` | Skill, Plugin, Hook, and extension maintainers | 48 | true |
| [github-pr-followup-observation](#github-pr-followup-observation) | `projection` | GitHub review and task follow-up maintainers | 1 | false |
| [http-openapi-contract](#http-openapi-contract) | `external-durable` | HTTP/OpenAPI and generated-client maintainers | 138 | true |
| [http-openapi-contract](#http-openapi-contract) | `external-durable` | HTTP/OpenAPI and generated-client maintainers | 139 | true |
| [in-memory-token-session](#in-memory-token-session) | `ephemeral` | Credential and bootstrap maintainers | 1 | false |
| [lsp-process-session](#lsp-process-session) | `ephemeral` | Code intelligence maintainers | 1 | false |
| [mcp-interchange](#mcp-interchange) | `external-durable` | MCP client/server maintainers | 9 | true |
Expand Down Expand Up @@ -986,7 +986,7 @@ This document is generated from [`protocols/registry.json`](../../protocols/regi
- `pr-followup-http-request-reader` (`v1`, active) at `internal/application/pr_followup.go`
- `pr-followup-http-response-reader` (`v1`, active) at `web/src/api/pr-followup.ts`

<details><summary>138 active identifiers</summary>
<details><summary>139 active identifiers</summary>

- `agent-code-tools.v1`
- `agent_browser_close.v1`
Expand Down Expand Up @@ -1102,6 +1102,7 @@ This document is generated from [`protocols/registry.json`](../../protocols/regi
- `thread_application_services.v1`
- `thread_branch.v1`
- `thread_change_summary.v1`
- `thread_change_undo.v1`
- `thread_command_service_policy.v1`
- `thread_context_compaction.v1`
- `thread_context_compaction_operations.v1`
Expand Down
210 changes: 210 additions & 0 deletions docs/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -28390,6 +28390,117 @@
"x-cyberagent-read-only": true
}
},
"/api/v1/threads/{thread_id}/changes/undo": {
"post": {
"operationId": "undoThreadChanges",
"summary": "Preview or undo recorded changes for one Thread reply",
"description": "With confirm=false, checks current authority and conflicts without writing. With confirm=true, restores only receipt-proven paths to their state before this reply interval. One durable operation is bound to the exact Thread and source event; completed and partial results replay without applying again. Requires an idle physical working directory and current control authorization.",
"tags": [
"Control"
],
"parameters": [
{
"name": "thread_id",
"in": "path",
"description": "Thread identity",
"required": true,
"schema": {
"maxLength": 256,
"minLength": 1,
"pattern": "^[^/\\\\\\x00-\\x1f\\x7f]+$",
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Control request accepted or idempotently replayed",
"content": {
"application/json": {
"schema": {
"additionalProperties": false,
"properties": {
"data": {
"$ref": "#/components/schemas/ThreadChangeUndoResult"
},
"request_id": {
"maxLength": 256,
"minLength": 1,
"type": "string"
},
"version": {
"const": "api.v1",
"type": "string"
}
},
"required": [
"version",
"request_id",
"data"
],
"type": "object"
}
}
}
},
"400": {
"$ref": "#/components/responses/BadRequest"
},
"401": {
"$ref": "#/components/responses/Unauthorized"
},
"403": {
"$ref": "#/components/responses/Forbidden"
},
"404": {
"$ref": "#/components/responses/NotFound"
},
"409": {
"$ref": "#/components/responses/Conflict"
},
"412": {
"$ref": "#/components/responses/FailedPrecondition"
},
"413": {
"$ref": "#/components/responses/RequestEntityTooLarge"
},
"414": {
"$ref": "#/components/responses/RequestTooLarge"
},
"415": {
"$ref": "#/components/responses/UnsupportedMediaType"
},
"429": {
"$ref": "#/components/responses/ResourceExhausted"
},
"500": {
"$ref": "#/components/responses/InternalError"
},
"503": {
"$ref": "#/components/responses/Unavailable"
},
"504": {
"$ref": "#/components/responses/GatewayTimeout"
}
},
"security": [
{
"ControlBearerAuth": []
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ThreadChangeUndoRequest"
}
}
}
},
"x-cyberagent-read-only": false
}
},
"/api/v1/threads/{thread_id}/command-service-policy": {
"get": {
"operationId": "getThreadCommandServicePolicy",
Expand Down Expand Up @@ -68372,6 +68483,9 @@
"minLength": 1,
"type": "string"
},
"undo": {
"$ref": "#/components/schemas/ThreadChangeUndoResult"
},
"version": {
"type": "string"
}
Expand All @@ -68392,6 +68506,102 @@
],
"type": "object"
},
"ThreadChangeUndoConflict": {
"additionalProperties": false,
"properties": {
"path": {
"type": "string"
},
"reason": {
"type": "string"
}
},
"required": [
"reason"
],
"type": "object"
},
"ThreadChangeUndoRequest": {
"additionalProperties": false,
"properties": {
"confirm": {
"type": "boolean"
},
"source_event_id": {
"maxLength": 256,
"minLength": 1,
"type": "string"
},
"version": {
"type": "string"
}
},
"required": [
"version",
"source_event_id",
"confirm"
],
"type": "object"
},
"ThreadChangeUndoResult": {
"additionalProperties": false,
"properties": {
"conflicts": {
"items": {
"$ref": "#/components/schemas/ThreadChangeUndoConflict"
},
"type": "array"
},
"remaining_paths": {
"items": {
"type": "string"
},
"type": "array"
},
"replayed": {
"type": "boolean"
},
"restored_paths": {
"items": {
"type": "string"
},
"type": "array"
},
"run_id": {
"maxLength": 256,
"minLength": 1,
"type": "string"
},
"source_event_id": {
"maxLength": 256,
"minLength": 1,
"type": "string"
},
"state": {
"type": "string"
},
"thread_id": {
"maxLength": 256,
"minLength": 1,
"type": "string"
},
"version": {
"type": "string"
}
},
"required": [
"version",
"thread_id",
"source_event_id",
"run_id",
"state",
"conflicts",
"restored_paths",
"remaining_paths",
"replayed"
],
"type": "object"
},
"ThreadCommandServicePolicy": {
"additionalProperties": false,
"properties": {
Expand Down
5 changes: 5 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ AI 回复下方的“本轮改动”卡片汇总本轮已应用的文件编辑
统计范围为文件编辑工具留下的应用记录;命令直接写入的文件不在此范围。敏感内容或不连续的
记录会标明差异与行数不可用,没有净改动时不显示卡片。

卡片上的“撤销”会先检查当前文件,确认后将本轮记录的改动恢复到本轮开始时的内容。
同一文件的多次编辑会一起撤销;其他路径与 Git 暂存区保持不变。有后续文件修改、任务仍在
执行或编辑记录不完整时会显示原因。成功后显示“已撤销”,刷新页面后仍保留此状态;
请求中断时可“重新检查”结果。若恢复中途失败,会分别列出已恢复和未恢复的路径。

Agent 建议固定任务要求时,问题会显示在输入框上方的独立卡片中。可选择“本次任务始终保留”
或“仅当前讨论”,也可直接撰写回复,再点击“发送”。自定义回复只有勾选“将这段回复固定到本次任务”
才会成为固定要求;“跳过”不固定内容,也不发送答复。回答失败时可重试原答复。
Expand Down
10 changes: 10 additions & 0 deletions internal/application/run_worktree_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,16 @@ func (s *RunWorktreeService) ReconcileWorkspaceCheckpoints(ctx context.Context)
return nil, apperror.New(apperror.CodeConflict,
"checkpoint recovery transaction workspace does not match its owner")
}
if workspacecheckpoint.IsThreadUndo(transaction) {
scoped := *s.checkpoints
scoped.withRunWorkspaceResolver(func(ctx context.Context, runID string) (session.WorkspaceInfo, bool, error) {
if runID != run.ID {
return session.WorkspaceInfo{}, false, apperror.New(apperror.CodeConflict, "reply undo recovery Run changed")
}
return resolved.Workspace, true, nil
})
return &scoped, nil
}
// Older Drydock boundaries used the ordinary cursor table. Preserve that
// recorded identity when it still points at this transaction; never adopt
// a source cursor or a different Drydock boundary into the new file cursor.
Expand Down
Loading
Loading