mirror of
https://github.com/multica-ai/multica.git
synced 2026-08-05 09:30:05 +02:00
Both Codex protocol paths recorded a file edit as a bare call ID and set no payload, so a run that edited six files left six blank, unexpandable rows in the transcript. The same task on Claude or Grok showed a readable diff, and the branch Codex pushed was the only surviving record of what it changed (GH #6157). The omission was specific to this one tool, not to the adapter: the exec_command handlers directly above already captured command and output. Both paths are fixed, since the protocol is sniffed at runtime. Their wire shapes differ more than they appear, and the normalizer reconciles that: - legacy patch_apply_begin/end carry map[path]FileChange, internally tagged on `type`, where add/delete hold whole-file `content` and only update holds a `unified_diff` plus `move_path`. There is no diff for every case, so the normalized form keeps diff and content as alternatives. - v2 fileChange items carry an ordered array of {path, kind, diff} where `kind` is an object, not a string — reading it as a string silently yields "" and loses the add/delete/update distinction. - status spellings differ too: legacy is snake_case, v2 is camelCase and adds inProgress. Both normalize onto one vocabulary, and a legacy event predating `status` falls back to its `success` bool. Legacy map iteration is sorted by path so a replayed event does not reshuffle the file list. Completion events now also produce a non-empty output (status, file count, and any apply_patch stdout/stderr), because an empty output renders as an unexpandable blank row just like a missing input. Anything unrecognised — absent, wrongly typed, or malformed changes — returns no payload, preserving exactly the previous degradation rather than risking the transcript. Total diff/content bytes are bounded at 64 KiB with UTF-8-safe truncation, recording `truncated` and `original_bytes`; paths and kinds always survive, since they are what a reviewer needs when the body is gone. The bound is deliberately scoped to this new payload: other providers stream tool inputs through unbounded, and clamping them here would silently truncate transcripts that render correctly today. Unifying the limit at the persistence boundary is left as a follow-up. Verified: the new tests reproduce the reported symptom (Input:map[], Output:"") against the previous call sites and pass now; ./pkg/agent and ./pkg/redact green, go vet and gofmt clean. Co-authored-by: multica-agent <github@multica.ai>