Skip to content

Commit 31f6cd7

Browse files
committed
fix(sync): normalize tool_result roles across parsers
1 parent ca9a70d commit 31f6cd7

1 file changed

Lines changed: 58 additions & 16 deletions

File tree

main.go

Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,9 +1226,10 @@ func parseCodexSession(filePath string) (SyncSession, bool) {
12261226
}
12271227
content := parseMessageContent(payload["content"])
12281228
if content != "" {
1229+
role := normalizeStructuredMessageRole(stringFrom(payload["role"]), payload["content"])
12291230
session.Messages = append(session.Messages, SyncMessage{
12301231
Index: msgIndex,
1231-
Role: stringFrom(payload["role"]),
1232+
Role: role,
12321233
Content: content,
12331234
})
12341235
msgIndex++
@@ -3849,10 +3850,7 @@ func parseAmpThread(path string) (SyncSession, bool) {
38493850
msgIndex := 0
38503851
for _, item := range entries {
38513852
msg := mapFrom(item)
3852-
role := strings.TrimSpace(stringFrom(msg["role"]))
3853-
if role == "" {
3854-
continue
3855-
}
3853+
role := normalizeStructuredMessageRole(stringFrom(msg["role"]), msg["content"])
38563854

38573855
tsMs := int64From(mapFrom(msg["meta"])["sentAt"], 0)
38583856
if tsMs > 0 {
@@ -5070,10 +5068,7 @@ func parseDroidSession(path string) (SyncSession, bool) {
50705068
if content == "" {
50715069
continue
50725070
}
5073-
role := message.Role
5074-
if role == "" {
5075-
role = "user"
5076-
}
5071+
role := normalizeStructuredMessageRole(message.Role, message.Content)
50775072
timestamp := stringFrom(item["timestamp"])
50785073
session.Messages = append(session.Messages, SyncMessage{
50795074
Index: msgIndex,
@@ -5253,13 +5248,7 @@ func parseQoderSession(path string) (SyncSession, bool) {
52535248
}
52545249

52555250
messageObj := mapFrom(item["message"])
5256-
role := strings.TrimSpace(stringFrom(messageObj["role"]))
5257-
if role == "" {
5258-
role = strings.TrimSpace(stringFrom(item["type"]))
5259-
}
5260-
if role == "" {
5261-
role = "assistant"
5262-
}
5251+
role := resolveQoderRole(item, messageObj)
52635252
content := parseQoderContent(messageObj["content"])
52645253
if content == "" {
52655254
continue
@@ -5339,6 +5328,59 @@ func parseQoderContent(value any) string {
53395328
}
53405329
}
53415330

5331+
func resolveQoderRole(item map[string]any, messageObj map[string]any) string {
5332+
role := strings.ToLower(strings.TrimSpace(stringFrom(messageObj["role"])))
5333+
if role == "" {
5334+
role = strings.ToLower(strings.TrimSpace(stringFrom(item["type"])))
5335+
}
5336+
return normalizeStructuredMessageRole(role, messageObj["content"])
5337+
}
5338+
5339+
func normalizeStructuredMessageRole(role string, content any) string {
5340+
normalizedRole := strings.ToLower(strings.TrimSpace(role))
5341+
hasText, hasToolUse, hasToolResult := structuredContentFlags(content)
5342+
// Some tools encode tool events as user role; force these to assistant-side turns.
5343+
if hasToolResult && !hasText && !hasToolUse {
5344+
return "assistant"
5345+
}
5346+
if hasToolUse {
5347+
return "assistant"
5348+
}
5349+
switch normalizedRole {
5350+
case "user", "assistant", "system", "tool":
5351+
return normalizedRole
5352+
case "":
5353+
return "assistant"
5354+
default:
5355+
return normalizedRole
5356+
}
5357+
}
5358+
5359+
func structuredContentFlags(value any) (hasText bool, hasToolUse bool, hasToolResult bool) {
5360+
items, ok := value.([]any)
5361+
if !ok {
5362+
return false, false, false
5363+
}
5364+
for _, item := range items {
5365+
entry, ok := item.(map[string]any)
5366+
if !ok {
5367+
continue
5368+
}
5369+
entryType := strings.ToLower(strings.TrimSpace(stringFrom(entry["type"])))
5370+
switch entryType {
5371+
case "text":
5372+
if strings.TrimSpace(stringFrom(entry["text"])) != "" {
5373+
hasText = true
5374+
}
5375+
case "tool_use":
5376+
hasToolUse = true
5377+
case "tool_result":
5378+
hasToolResult = true
5379+
}
5380+
}
5381+
return hasText, hasToolUse, hasToolResult
5382+
}
5383+
53425384
func parseClaudeContent(value any) string {
53435385
switch v := value.(type) {
53445386
case string:

0 commit comments

Comments
 (0)