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 | 137 | true |
| [http-openapi-contract](#http-openapi-contract) | `external-durable` | HTTP/OpenAPI and generated-client maintainers | 138 | 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>137 active identifiers</summary>
<details><summary>138 active identifiers</summary>

- `agent-code-tools.v1`
- `agent_browser_close.v1`
Expand Down Expand Up @@ -1101,6 +1101,7 @@ This document is generated from [`protocols/registry.json`](../../protocols/regi
- `thread_activity_summary.v1`
- `thread_application_services.v1`
- `thread_branch.v1`
- `thread_change_summary.v1`
- `thread_command_service_policy.v1`
- `thread_context_compaction.v1`
- `thread_context_compaction_operations.v1`
Expand Down
223 changes: 223 additions & 0 deletions docs/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -28295,6 +28295,101 @@
"x-cyberagent-read-only": false
}
},
"/api/v1/threads/{thread_id}/changes": {
"get": {
"operationId": "getThreadChanges",
"summary": "Read recorded file changes before one Thread reply",
"description": "Read-only net change summary for applied FileEdit records since the previous durable assistant reply in the same Run. The source event must be an assistant Session message in this Thread. Repeated edits are combined, unapplied proposals are excluded, and current workspace files are never read. Redacted or discontinuous histories omit counts and diff bodies; bounded diff omission is explicit.",
"tags": [
"Threads"
],
"parameters": [
{
"name": "thread_id",
"in": "path",
"description": "Thread identity",
"required": true,
"schema": {
"maxLength": 256,
"minLength": 1,
"pattern": "^[^/\\\\\\x00-\\x1f\\x7f]+$",
"type": "string"
}
},
{
"name": "source_event_id",
"in": "query",
"description": "Durable assistant session.message_created event ID from the Thread transcript",
"required": true,
"schema": {
"maxLength": 256,
"minLength": 1,
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Successful read",
"content": {
"application/json": {
"schema": {
"additionalProperties": false,
"properties": {
"data": {
"$ref": "#/components/schemas/ThreadChangeSummary"
},
"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"
},
"414": {
"$ref": "#/components/responses/RequestTooLarge"
},
"429": {
"$ref": "#/components/responses/ResourceExhausted"
},
"500": {
"$ref": "#/components/responses/InternalError"
},
"503": {
"$ref": "#/components/responses/Unavailable"
},
"504": {
"$ref": "#/components/responses/GatewayTimeout"
}
},
"x-cyberagent-read-only": true
}
},
"/api/v1/threads/{thread_id}/command-service-policy": {
"get": {
"operationId": "getThreadCommandServicePolicy",
Expand Down Expand Up @@ -68169,6 +68264,134 @@
],
"type": "object"
},
"ThreadChangeFile": {
"additionalProperties": false,
"properties": {
"additions": {
"format": "int32",
"type": "integer"
},
"counts_available": {
"type": "boolean"
},
"deletions": {
"format": "int32",
"type": "integer"
},
"destination_path": {
"type": "string"
},
"diff": {
"type": "string"
},
"diff_truncated": {
"type": "boolean"
},
"edit_ids": {
"items": {
"type": "string"
},
"type": "array"
},
"operation": {
"type": "string"
},
"path": {
"type": "string"
},
"reason": {
"type": "string"
},
"workspace_id": {
"maxLength": 256,
"minLength": 1,
"type": "string"
}
},
"required": [
"workspace_id",
"path",
"operation",
"additions",
"deletions",
"counts_available",
"diff",
"diff_truncated",
"edit_ids"
],
"type": "object"
},
"ThreadChangeSummary": {
"additionalProperties": false,
"properties": {
"additions": {
"format": "int32",
"type": "integer"
},
"change_scope": {
"type": "string"
},
"counts_available": {
"type": "boolean"
},
"deletions": {
"format": "int32",
"type": "integer"
},
"file_count": {
"format": "int32",
"type": "integer"
},
"files": {
"items": {
"$ref": "#/components/schemas/ThreadChangeFile"
},
"type": "array"
},
"partial": {
"type": "boolean"
},
"reasons": {
"items": {
"type": "string"
},
"type": "array"
},
"run_id": {
"maxLength": 256,
"minLength": 1,
"type": "string"
},
"source_event_id": {
"maxLength": 256,
"minLength": 1,
"type": "string"
},
"thread_id": {
"maxLength": 256,
"minLength": 1,
"type": "string"
},
"version": {
"type": "string"
}
},
"required": [
"version",
"thread_id",
"source_event_id",
"run_id",
"change_scope",
"files",
"file_count",
"additions",
"deletions",
"counts_available",
"partial",
"reasons"
],
"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 @@ -116,6 +116,11 @@ Ctrl+Shift+V(macOS 为 Cmd+Shift+V)按纯文本粘贴,保留 Markdown 标
实时回复的新片段以 150 ms 的轻微模糊淡入显示,不额外等待或延迟输出;历史消息和
代码块直接显示。开启系统的“减少动态效果”后,文字直接出现。

AI 回复下方的“本轮改动”卡片汇总本轮已应用的文件编辑,显示文件数和净增删行数。
默认展示三个文件,可展开全部;点击文件或“查看变更”可查看本轮起始内容与最终内容的差异。
统计范围为文件编辑工具留下的应用记录;命令直接写入的文件不在此范围。敏感内容或不连续的
记录会标明差异与行数不可用,没有净改动时不显示卡片。

Agent 建议固定任务要求时,问题会显示在输入框上方的独立卡片中。可选择“本次任务始终保留”
或“仅当前讨论”,也可直接撰写回复,再点击“发送”。自定义回复只有勾选“将这段回复固定到本次任务”
才会成为固定要求;“跳过”不固定内容,也不发送答复。回答失败时可重试原答复。
Expand Down
72 changes: 72 additions & 0 deletions internal/application/thread_change_diff.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package application

import (
"fmt"
"strings"
"time"

gitdiff "github.com/go-git/go-git/v5/utils/diff"
)

// threadChangeDiff retains equal lines between separate edits as context. The
// proposal diff's prefix/suffix-only algorithm would count those lines twice.
// go-git's existing line diff provides the same chunks used for these counts.
func threadChangeDiff(path, before, after string, budget time.Duration) (string, int, int, bool) {
if budget <= 0 {
return "", 0, 0, false
}
started := time.Now()
chunks := gitdiff.DoWithTimeout(before, after, min(budget, 100*time.Millisecond))
if time.Since(started) >= min(budget, 100*time.Millisecond) {
// The dependency can return a bulk replacement on timeout; do not
// present that incomplete calculation as a precise line count.
return "", 0, 0, false
}
var body strings.Builder
start, oldCount, newCount, additions, deletions := 1, 0, 0, 0, 0
for index, chunk := range chunks {
lines := strings.SplitAfter(chunk.Text, "\n")
if lines[len(lines)-1] == "" {
lines = lines[:len(lines)-1]
}
prefix := byte(' ')
switch chunk.Type {
case -1:
prefix = '-'
deletions += len(lines)
case 1:
prefix = '+'
additions += len(lines)
case 0:
if index == 0 && len(lines) > 3 {
start += len(lines) - 3
lines = lines[len(lines)-3:]
}
if index == len(chunks)-1 && len(lines) > 3 {
lines = lines[:3]
}
}
if prefix != '+' {
oldCount += len(lines)
}
if prefix != '-' {
newCount += len(lines)
}
for _, line := range lines {
body.WriteByte(prefix)
body.WriteString(line)
if !strings.HasSuffix(line, "\n") {
body.WriteString("\n\\ No newline at end of file\n")
}
}
}
oldStart, newStart := start, start
if oldCount == 0 {
oldStart = 0
}
if newCount == 0 {
newStart = 0
}
return fmt.Sprintf("--- a/%s\n+++ b/%s\n@@ -%d,%d +%d,%d @@\n%s", path, path,
oldStart, oldCount, newStart, newCount, body.String()), additions, deletions, true
}
Loading
Loading