Skip to content

Commit 60d00e9

Browse files
author
SqlRush
committed
Accept script message paste aliases
1 parent 611c671 commit 60d00e9

7 files changed

Lines changed: 59 additions & 9 deletions

File tree

docs/cc-100-roadmap.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ M7 补充:keybinding config 的 page navigation key name 现在接受 `pgup`/`
128128

129129
M7 补充:interaction script 的 `message` step 现在接受 chat/transcript 风格的 `type`/`speaker` role 别名和 `content`/`body`/`message` text 别名;`image` step 接受 `fileName`/`file_name`/`name`、`mimeType`/`mime_type`/`contentType` 和 `data`/`base64` 内容别名;permission request step 接受 request/permission/tool-use ID、path、description 和 action 字段别名,并允许 `actions` 使用单字符串;`expectPrompt` 接受 `value`/`input`/`content`/`message`、`expandedText`/`fullText`、`cursorIndex`/`cursorPosition`、`isEmpty`/`blank` 等字段别名,且 `pastedContents` 断言接受 `pastedId`/`pastedContentId`、`kind`/`pastedType`、`value`/`data`/`base64`、`contentType`/`mimeType`、`fileName`/`name` 和 `contains` 等字段别名;`expectVim` 接受 `vimEnabled`/`isEnabled`、`vimMode`/`modeName`/`currentMode`、`vimRegister`/`registerValue`/`yankRegister`、`registerLinewise`/`linewise` 等字段别名;`expectTasks` 接受 `taskCount`/`total`/`size`/`length` 和 `statusCounts`/`countsByState` 等字段别名;`expectScreen`/`expectViewport` 接受 `columns`/`rows`、`screenWidth`/`screenHeight`、`scrollOffset`/`viewportOffset`、`visibleRows`/`lineCount` 等字段别名;`expectReverseSearch` 接受 `isActive`/`visible`/`open`、`search`/`term`/`pattern`、`cursorIndex`、`currentResult`、`matchCount`/`matches`、`noMatches` 等字段别名;`expectDialog` 可断言 body contains/not-contains、actions/action contains/not-contains、action count 和 focused action,runtime-aware scripts 会在步骤间保留 dialog focused action,且接受 `isActive`/`visible`、`dialogId`/`dialogID`、`dialogKind`、`heading`/`header`、`content`/`text`/`message` 等字段别名;`expectEvent`/`expectDialogResult` 接受 `eventType`/`event`/`name`、`payload`/`text`/`message`、`dialogId`/`dialogID`/`dialogKind`、`actionValue`/`resultStatus`/`exists`/`isStale` 等字段别名。
130130

131-
M7 补充:interaction script 现在接受 `messages``append_messages`/`appendMessages``transcript_messages`/`transcriptMessages` 批量消息注入字段,且这些字段既可用单个对象也可用对象数组,消息对象沿用 chat/transcript role/text 别名。
131+
M7 补充:interaction script 现在接受 `messages``append_messages`/`appendMessages``transcript_messages`/`transcriptMessages` 批量消息注入字段,且这些字段既可用单个对象也可用对象数组,消息对象沿用 chat/transcript role/text 别名;message 注入也接受 `pastedContent`/`attachments` 粘贴内容别名、单数 `imagePasteId` 图片引用别名,以及 pasted-content 的 `kind`/`value`/`data` 内容字段别名
132132

133133
M7 补充:interaction script 的 direct `dialog` step 现在接受 `dialogId`/`dialogID``dialogKind``heading`/`header``content`/`text`/`message``options`/`choices`/`buttons``focusedIndex`/`selectedIndex` 等字段别名,并允许 actions/options 使用单字符串。
134134

docs/claude-code-go-rewrite-plan.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ test/parity/ # golden tests against TS/official behavior
306306
- 本轮补充:interaction script 的 `message` step 接受 `type`/`speaker` role 别名和 `content`/`body`/`message` text 别名;`image` step 接受 `fileName`/`file_name`/`name`、`mimeType`/`mime_type`/`contentType` 和 `data`/`base64` 内容别名;permission request step 接受 request/permission/tool-use ID、path、description 和 action 字段别名,并允许 `actions` 使用单字符串;`expectPrompt` 接受 `value`/`input`/`content`/`message`、`expandedText`/`fullText`、`cursorIndex`/`cursorPosition`、`isEmpty`/`blank` 等字段别名,且 `pastedContents` 断言接受 `pastedId`/`pastedContentId`、`kind`/`pastedType`、`value`/`data`/`base64`、`contentType`/`mimeType`、`fileName`/`name` 和 `contains` 等字段别名;`expectVim` 接受 `vimEnabled`/`isEnabled`、`vimMode`/`modeName`/`currentMode`、`vimRegister`/`registerValue`/`yankRegister`、`registerLinewise`/`linewise` 等字段别名;`expectTasks` 接受 `taskCount`/`total`/`size`/`length` 和 `statusCounts`/`countsByState` 等字段别名;`expectScreen`/`expectViewport` 接受 `columns`/`rows`、`screenWidth`/`screenHeight`、`scrollOffset`/`viewportOffset`、`visibleRows`/`lineCount` 等字段别名;`expectReverseSearch` 接受 `isActive`/`visible`/`open`、`search`/`term`/`pattern`、`cursorIndex`、`currentResult`、`matchCount`/`matches`、`noMatches` 等字段别名;`expectDialog` 可断言 body contains/not-contains、actions/action contains/not-contains、action count 和 focused action,runtime-aware scripts 会在步骤间保留 dialog focused action,且接受 `isActive`/`visible`、`dialogId`/`dialogID`、`dialogKind`、`heading`/`header`、`content`/`text`/`message` 等字段别名;`expectEvent`/`expectDialogResult` 接受 `eventType`/`event`/`name`、`payload`/`text`/`message`、`dialogId`/`dialogID`/`dialogKind`、`actionValue`/`resultStatus`/`exists`/`isStale` 等字段别名。
307307
- 本轮补充:interaction script step 接受 `resize`/`terminalSize`/`screenSize` 对象或 `[width,height]` 数组、顶层 `columns`/`rows` resize 别名、`focus`/`focused`/`blur`/`focusIn`/`focusOut` focus event 别名、`snapshot`/`snapshotId`/`snapshotLabel` capture 名称别名,以及 runtime-aware mutation 别名如 `permission`/`permissionRequest``task`/`taskStatus``removeTask`/`deleteTask``cancelPermission``cancelTasks`/`cancelReason``openTasks`/`showTasks`
308308
- 本轮补充:interaction script step 可通过 `status`/`setStatus`/`statusLine`/`baseStatus` 设置状态行;runtime-aware scripts 会把它作为 base status,并继续叠加 permission/task 计数,方便复用带 status line 的 ANSI/interaction fixture。
309-
- 本轮补充:interaction script 批量消息注入接受 `messages``append_messages`/`appendMessages``transcript_messages`/`transcriptMessages` 字段,且这些字段可用单对象或对象数组,复用 chat/transcript role/text 别名。
309+
- 本轮补充:interaction script 批量消息注入接受 `messages``append_messages`/`appendMessages``transcript_messages`/`transcriptMessages` 字段,且这些字段可用单对象或对象数组,复用 chat/transcript role/text 别名;message 注入也接受 `pastedContent`/`attachments` 粘贴内容别名、单数 `imagePasteId` 图片引用别名,以及 pasted-content 的 `kind`/`value`/`data` 内容字段别名
310310
- 本轮补充:interaction script direct `dialog` step 接受 `dialogId`/`dialogKind``heading`/`header``content`/`text`/`message``options`/`choices`/`buttons``focusedIndex`/`selectedIndex` 等字段别名,且 actions/options 可用单字符串。
311311
- 本轮补充:interaction script loader 接受 `scriptSteps`/`script_steps``interactionSteps`/`interaction_steps` wrapper 字段,并支持一层 `scenario`/`test`/`case`/`fixture`/`interaction` 嵌套对象。
312312
- 本轮补充:ANSI snapshot corpus 比对支持 `.ansi` only baseline fallback,strict stale-baseline 检查同时覆盖 `.txt``.ansi`

docs/first-second-parity-audit.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ M7 progress now includes:
275275
- `internal/tui`: prompt pasted-content expectations now accept a single `pastedContents` object as well as an array.
276276
- `internal/tui`: interaction script task expectations now accept a single `contains` object as well as an array for expected task matches.
277277
- `internal/tui`: interaction script message steps now accept chat/transcript-style `type`/`speaker` role aliases and `content`/`body`/`message` text aliases.
278-
- `internal/tui`: interaction script message injection now accepts `messages`, `append_messages`/`appendMessages`, and `transcript_messages`/`transcriptMessages` object or object-array aliases.
278+
- `internal/tui`: interaction script message injection now accepts `messages`, `append_messages`/`appendMessages`, and `transcript_messages`/`transcriptMessages` object or object-array aliases, plus pasted-content attachment aliases such as `pastedContent`, `attachments`, single `imagePasteId`, and `kind`/`value`/`data` pasted-content fields.
279279
- `internal/tui`: interaction script image steps now accept filename aliases such as `fileName`/`file_name`/`name`, media-type aliases such as `mimeType`/`mime_type`/`contentType`, and content aliases such as `data`/`base64`.
280280
- `internal/tui`: interaction script direct dialog steps now accept ID, kind, title/body, action-list, and focused-index aliases aligned with dialog expectations.
281281
- `internal/tui`: interaction script permission request steps now accept request/permission/tool-use ID aliases, path aliases, description aliases, action-list aliases, and single-string `actions`.

internal/session/history_aliases.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ func (c *PastedContent) UnmarshalJSON(data []byte) error {
4848
type PastedContentJSON PastedContent
4949
var aux struct {
5050
*PastedContentJSON
51+
Kind string `json:"kind"`
52+
PastedType string `json:"pastedType"`
53+
PastedTypeSnake string `json:"pasted_type"`
54+
Value string `json:"value"`
55+
Data string `json:"data"`
56+
Base64 string `json:"base64"`
5157
MediaTypeSnake string `json:"media_type"`
5258
MimeType string `json:"mimeType"`
5359
MimeTypeSnake string `json:"mime_type"`
@@ -67,6 +73,12 @@ func (c *PastedContent) UnmarshalJSON(data []byte) error {
6773
return err
6874
}
6975
*c = PastedContent(base)
76+
if c.Type == "" {
77+
c.Type = firstHistoryString(aux.Kind, aux.PastedType, aux.PastedTypeSnake)
78+
}
79+
if c.Content == "" {
80+
c.Content = firstHistoryString(aux.Value, aux.Data, aux.Base64)
81+
}
7082
if c.MediaType == "" {
7183
c.MediaType = firstHistoryString(aux.MediaTypeSnake, aux.MimeType, aux.MimeTypeSnake, aux.ContentType, aux.ContentTypeSnake)
7284
}

internal/session/history_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,12 +153,12 @@ func TestLoadPromptHistoryAcceptsFieldAliases(t *testing.T) {
153153

154154
func TestHistoryEntryAcceptsPastedContentFieldAliases(t *testing.T) {
155155
var entry HistoryEntry
156-
err := json.Unmarshal([]byte(`{"display":"restore [Image #1]","pasted_contents":{"1":{"id":1,"type":"image","mimeType":"image/png","name":"chart.png","path":"/tmp/chart.png","dimensions":{"width":4000,"height":2000}}}}`), &entry)
156+
err := json.Unmarshal([]byte(`{"display":"restore [Image #1]","pasted_contents":{"1":{"id":1,"kind":"image","base64":"AAAA","mimeType":"image/png","name":"chart.png","path":"/tmp/chart.png","dimensions":{"width":4000,"height":2000}}}}`), &entry)
157157
if err != nil {
158158
t.Fatal(err)
159159
}
160160
got := entry.PastedContents[1]
161-
if got.Type != PastedContentImage || got.MediaType != "image/png" || got.Filename != "chart.png" || got.SourcePath != "/tmp/chart.png" || got.Dimensions == nil || got.Dimensions.OriginalHeight != 2000 {
161+
if got.Type != PastedContentImage || got.Content != "AAAA" || got.MediaType != "image/png" || got.Filename != "chart.png" || got.SourcePath != "/tmp/chart.png" || got.Dimensions == nil || got.Dimensions.OriginalHeight != 2000 {
162162
t.Fatalf("pasted content aliases = %#v", got)
163163
}
164164
}

internal/tui/script_aliases.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1199,19 +1199,41 @@ func (message *Message) UnmarshalJSON(data []byte) error {
11991199
if len(message.ImagePasteIDs) == 0 {
12001200
message.ImagePasteIDs = intListJSONField(
12011201
fields,
1202+
"imagePasteId",
1203+
"imagePasteID",
1204+
"image_paste_id",
12021205
"imagePasteIds",
12031206
"imagePasteIDs",
12041207
"image_paste_ids",
1208+
"imageId",
1209+
"imageID",
1210+
"image_id",
12051211
"imageIds",
12061212
"imageIDs",
12071213
"image_ids",
1214+
"pastedImageId",
1215+
"pastedImageID",
1216+
"pasted_image_id",
12081217
"pastedImageIds",
12091218
"pastedImageIDs",
12101219
"pasted_image_ids",
12111220
)
12121221
}
12131222
if len(message.PastedContents) == 0 {
1214-
message.PastedContents = pastedContentsJSONField(fields, "pastedContents", "pasted_contents", "pastes", "pasteContents", "paste_contents")
1223+
message.PastedContents = pastedContentsJSONField(
1224+
fields,
1225+
"pastedContents",
1226+
"pasted_contents",
1227+
"pastedContent",
1228+
"pasted_content",
1229+
"pastes",
1230+
"pasteContents",
1231+
"paste_contents",
1232+
"pasteContent",
1233+
"paste_content",
1234+
"attachments",
1235+
"attachment",
1236+
)
12151237
}
12161238
return nil
12171239
}

internal/tui/tui_test.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4675,6 +4675,7 @@ func TestRunInteractionScriptAcceptsMessageListAliases(t *testing.T) {
46754675
"transcript_messages": [
46764676
{"role": "tool", "text": "tool output"},
46774677
{"role": "user", "text": "image history", "imagePasteIds": [9]},
4678+
{"role": "user", "text": "single image history", "imagePasteId": 10},
46784679
{
46794680
"role": "user",
46804681
"content": [
@@ -4683,11 +4684,20 @@ func TestRunInteractionScriptAcceptsMessageListAliases(t *testing.T) {
46834684
],
46844685
"imagePasteIds": [11],
46854686
"pasted_contents": {"12": {"id": 12, "type": "text", "content": "memo"}}
4687+
},
4688+
{
4689+
"role": "user",
4690+
"text": "attached [Pasted text #13] [Image #14]",
4691+
"attachments": [
4692+
{"id": 13, "kind": "text", "value": "attached memo"},
4693+
{"id": 14, "pastedType": "image", "data": "BBBB", "contentType": "image/png", "fileName": "attached.png"}
4694+
],
4695+
"pastedImageId": 14
46864696
}
46874697
],
46884698
"snapshot": "transcript",
46894699
"expectSnapshotContains": "tool: tool output",
4690-
"expectPrompt": {"nextPastedID": 13}
4700+
"expectPrompt": {"nextPastedID": 15}
46914701
}
46924702
]`))
46934703
if err != nil {
@@ -4701,15 +4711,21 @@ func TestRunInteractionScriptAcceptsMessageListAliases(t *testing.T) {
47014711
if len(result.Snapshots) != 3 {
47024712
t.Fatalf("snapshots = %#v", result.Snapshots)
47034713
}
4704-
if len(screen.Messages) != 6 {
4714+
if len(screen.Messages) != 8 {
47054715
t.Fatalf("messages = %#v", screen.Messages)
47064716
}
47074717
if got := screen.Messages[4].ImagePasteIDs; len(got) != 1 || got[0] != 9 {
47084718
t.Fatalf("image paste ids = %#v", got)
47094719
}
4710-
if got := screen.Messages[5]; len(got.ContentBlocks) != 2 || got.ImagePasteIDs[0] != 11 || got.PastedContents[12].Content != "memo" {
4720+
if got := screen.Messages[5].ImagePasteIDs; len(got) != 1 || got[0] != 10 {
4721+
t.Fatalf("single image paste id = %#v", got)
4722+
}
4723+
if got := screen.Messages[6]; len(got.ContentBlocks) != 2 || got.ImagePasteIDs[0] != 11 || got.PastedContents[12].Content != "memo" {
47114724
t.Fatalf("content block message = %#v", got)
47124725
}
4726+
if got := screen.Messages[7]; len(got.ImagePasteIDs) != 1 || got.ImagePasteIDs[0] != 14 || got.PastedContents[13].Content != "attached memo" || got.PastedContents[14].Filename != "attached.png" {
4727+
t.Fatalf("attachment message = %#v", got)
4728+
}
47134729
}
47144730

47154731
func TestRunInteractionScriptAcceptsInputFieldAliases(t *testing.T) {

0 commit comments

Comments
 (0)