Skip to content

Commit f0d78a0

Browse files
author
SqlRush
committed
Accept wrapped single message fields
1 parent 61fe928 commit f0d78a0

5 files changed

Lines changed: 58 additions & 0 deletions

File tree

docs/cc-100-roadmap.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,8 @@ M7 补充:prompt history `LogEntry` 读取现在接受 `sessionID`/`session`/`
638638

639639
本轮补充:interaction script 的 direct message list 字段现在改为 raw payload 解析;`messages`/`appendMessages`/`transcriptMessages` 可递归解包 `resource.attributes``data[]``edge.node.attrs` 等 API/GraphQL wrapper,并保留 image paste id 等 message metadata。
640640

641+
本轮补充:interaction script 的 direct single message 字段现在也复用 raw message parser;`message`/`Message` 可递归解包 `resource.attributes``edge.node.attrs`,并在数组形态下回退为多条 message 追加,避免单条消息 wrapper 被基础解码成空消息。
642+
641643
本轮补充:interaction script 的 runtime mutation action 现在也递归解包 wrapped `removeTask`/`cancelPermission` ID 和 `cancelTasks` cancellation detail;`payload.resource.attributes``edge.node` 和相邻 API envelope 可直接驱动 task removal、permission cancellation 与 task bulk-cancel。
642644

643645
本轮补充:interaction script 的 direct runtime mutation alias 字段现在接受 object payload;`removeTask: {resource:{id}}``cancelPermission: {edge:{node:{id}}}``cancelTasks: {resource:{attributes:{reasonText}}}` 会走同一递归解析路径,不再被 string/bool alias 字段提前拒绝。

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ test/parity/ # golden tests against TS/official behavior
329329
- 本轮补充:interaction script 的 direct runtime mutation 字段现在改为 raw payload 解析;`requestPermission`/`request_permission``upsertTask`/`upsert_task` singular 字段也可接受单元素数组并取首项,避免基础 step unmarshal 提前失败。
330330
- 本轮补充:interaction script 的 direct mouse 字段现在改为 raw payload 解析;`mouse`/`mouse_event`/`mouseEvent` singular 字段可接受单元素数组并递归解包 `resource.attributes``edge.node.attrs` 等 wrapper,避免录制脚本把鼠标事件包成 API payload 时提前失败。
331331
- 本轮补充:interaction script 的 direct message list 字段现在改为 raw payload 解析;`messages`/`appendMessages`/`transcriptMessages` 可递归解包 `resource.attributes``data[]``edge.node.attrs` 等 API/GraphQL wrapper,并保留 image paste id 等 message metadata。
332+
- 本轮补充:interaction script 的 direct single message 字段现在也复用 raw message parser;`message`/`Message` 可递归解包 `resource.attributes``edge.node.attrs`,并在数组形态下回退为多条 message 追加,避免单条消息 wrapper 被基础解码成空消息。
332333
- 本轮补充:terminal CSI-u/kitty keyboard parser 接受 `codepoint:alternate``modifier:event-type` 冒号字段,按主 codepoint/modifier 解析 ctrl/alt/shift/rune 键,覆盖 kitty progressive keyboard protocol 的常见变体。
333334
- 本轮补充:terminal CSI-u/kitty keyboard parser 接受无 modifier 字段或 modifier `1` 的 base key 序列,覆盖 printable rune、Enter、Tab、Esc 和 Backspace,避免 extended-key 模式下普通键序列被解析成 unknown。
334335
- 本轮补充:terminal input parser 接受 xterm modified arrow 序列(如 `CSI 1;2D``CSI 1;6C``CSI 1;7D`),把 shift-arrow 降级为方向键、alt-arrow 映射到 word-motion key、ctrl/ctrl+alt-arrow 映射到 ctrl word-motion key,避免 extended navigation 序列落入 unknown。

docs/first-second-parity-audit.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,6 +383,7 @@ M7 progress now includes:
383383
- `internal/tui`: interaction script direct runtime mutation fields now use raw payload parsing, so singular `requestPermission`/`request_permission` and `upsertTask`/`upsert_task` fields can accept single-element arrays and choose the first payload without failing base step unmarshal.
384384
- `internal/tui`: interaction script direct mouse fields now use raw payload parsing, so singular `mouse`/`mouse_event`/`mouseEvent` fields can accept single-element arrays and JSON:API/GraphQL-style wrappers such as `resource.attributes` or `edge.node.attrs` without failing base step unmarshal.
385385
- `internal/tui`: interaction script direct message-list fields now use raw payload parsing, so `messages`/`appendMessages`/`transcriptMessages` can unwrap API/GraphQL envelopes such as `resource.attributes`, `data[]`, and `edge.node.attrs` while preserving message metadata such as image paste IDs.
386+
- `internal/tui`: interaction script direct single-message fields now reuse the raw message parser, so `message`/`Message` can unwrap `resource.attributes` or `edge.node.attrs`, and array-shaped single-message payloads fall back to appending multiple messages instead of producing an empty decoded message.
386387
- `internal/contracts`/`internal/session`: image content blocks now normalize source aliases such as `mediaType`/`mimeType`/`contentType` and `base64`/`payload`, including top-level image block fields, into canonical `ImageSource` values during transcript resume.
387388
- `internal/tui`: interaction script single-step JSON now unwraps `step`/`scriptStep`/`interactionStep`/`record`/`entry`/`item`/`event` objects in arrays, JSONL records, and wrapper-object step lists.
388389
- `internal/tui`: interaction script single-step JSON now also unwraps JSON:API/resource-style `resource`/`node`/`attributes`/`properties`/`attrs` step items, so API fixture arrays and JSONL records can keep step resource envelopes.

internal/tui/script_aliases.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,16 @@ func (step *ScriptStep) UnmarshalJSON(data []byte) error {
761761
"value",
762762
)
763763
}
764+
if messages, ok, err := scriptMessagesJSONField(rawFieldMap, "Message", "message"); err != nil {
765+
return err
766+
} else if ok {
767+
if len(messages) == 1 {
768+
message := messages[0]
769+
step.Message = &message
770+
} else {
771+
step.Messages = messages
772+
}
773+
}
764774
if messages, ok, err := scriptMessagesJSONField(rawFieldMap, "Messages", "messages"); err != nil {
765775
return err
766776
} else if ok {
@@ -3159,6 +3169,8 @@ func stripScriptStepRawScalarAliasFields(data []byte) []byte {
31593169
"paste",
31603170
"Status",
31613171
"status",
3172+
"Message",
3173+
"message",
31623174
"Messages",
31633175
"messages",
31643176
"append_messages",

internal/tui/tui_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5428,6 +5428,48 @@ func TestRunInteractionScriptAcceptsMessageContentAliases(t *testing.T) {
54285428
}
54295429
}
54305430

5431+
func TestRunInteractionScriptAcceptsWrappedMessageField(t *testing.T) {
5432+
steps, err := ParseInteractionScript([]byte(`[
5433+
{
5434+
"message": {
5435+
"resource": {
5436+
"type": "message",
5437+
"attributes": {"speaker": "assistant", "body": "wrapped direct"}
5438+
}
5439+
},
5440+
"snapshotName": "wrapped-direct",
5441+
"expectSnapshotContains": "assistant: wrapped direct"
5442+
},
5443+
{
5444+
"message": [
5445+
{"edge": {"node": {"attrs": {"role": "system", "text": "wrapped array system"}}}},
5446+
{"resource": {"attributes": {"role": "tool", "text": "wrapped array tool"}}}
5447+
],
5448+
"snapshotName": "wrapped-array",
5449+
"expectSnapshotContains": ["system: wrapped array system", "tool: wrapped array tool"]
5450+
}
5451+
]`))
5452+
if err != nil {
5453+
t.Fatal(err)
5454+
}
5455+
if len(steps) != 2 ||
5456+
steps[0].Message == nil || steps[0].Message.Role != RoleAssistant || steps[0].Message.Text != "wrapped direct" ||
5457+
len(steps[1].Messages) != 2 || steps[1].Messages[0].Role != RoleSystem || steps[1].Messages[1].Role != RoleTool {
5458+
t.Fatalf("steps = %#v", steps)
5459+
}
5460+
screen := NewREPLScreen(60, 8, nil)
5461+
result, err := RunInteractionScriptChecked(&screen, steps)
5462+
if err != nil {
5463+
t.Fatal(err)
5464+
}
5465+
if len(result.Snapshots) != 2 {
5466+
t.Fatalf("snapshots = %#v", result.Snapshots)
5467+
}
5468+
if len(screen.Messages) != 3 {
5469+
t.Fatalf("messages = %#v", screen.Messages)
5470+
}
5471+
}
5472+
54315473
func TestRunInteractionScriptAcceptsMessageListAliases(t *testing.T) {
54325474
steps, err := ParseInteractionScript([]byte(`[
54335475
{

0 commit comments

Comments
 (0)