* For external users, make REPL invisible in the persisted transcript: strip * REPL tool_use/tool_result pairs and promote isVirtual messages to real. On * --resume the model then sees a coherent native-tool-call history (assistant * called Bash, got result, called Read, got result) without the R
( messages: Transcript, replIds: Set<string>, )
| 4620 | * and leave an orphaned tool_result on disk. |
| 4621 | */ |
| 4622 | function transformMessagesForExternalTranscript( |
| 4623 | messages: Transcript, |
| 4624 | replIds: Set<string>, |
| 4625 | ): Transcript { |
| 4626 | return messages.flatMap(m => { |
| 4627 | if (m.type === 'assistant' && Array.isArray(m.message.content)) { |
| 4628 | const content = m.message.content |
| 4629 | const hasRepl = content.some( |
| 4630 | b => b.type === 'tool_use' && b.name === REPL_TOOL_NAME, |
| 4631 | ) |
| 4632 | const filtered = hasRepl |
| 4633 | ? content.filter( |
| 4634 | b => !(b.type === 'tool_use' && b.name === REPL_TOOL_NAME), |
| 4635 | ) |
| 4636 | : content |
| 4637 | if (filtered.length === 0) return [] |
| 4638 | if (m.isVirtual) { |
| 4639 | const { isVirtual: _omit, ...rest } = m |
| 4640 | return [{ ...rest, message: { ...m.message, content: filtered } }] |
| 4641 | } |
| 4642 | if (filtered !== content) { |
| 4643 | return [{ ...m, message: { ...m.message, content: filtered } }] |
| 4644 | } |
| 4645 | return [m] |
| 4646 | } |
| 4647 | if (m.type === 'user' && Array.isArray(m.message.content)) { |
| 4648 | const content = m.message.content |
| 4649 | const hasRepl = content.some( |
| 4650 | b => b.type === 'tool_result' && replIds.has(b.tool_use_id), |
| 4651 | ) |
| 4652 | const filtered = hasRepl |
| 4653 | ? content.filter( |
| 4654 | b => !(b.type === 'tool_result' && replIds.has(b.tool_use_id)), |
| 4655 | ) |
| 4656 | : content |
| 4657 | if (filtered.length === 0) return [] |
| 4658 | if (m.isVirtual) { |
| 4659 | const { isVirtual: _omit, ...rest } = m |
| 4660 | return [{ ...rest, message: { ...m.message, content: filtered } }] |
| 4661 | } |
| 4662 | if (filtered !== content) { |
| 4663 | return [{ ...m, message: { ...m.message, content: filtered } }] |
| 4664 | } |
| 4665 | return [m] |
| 4666 | } |
| 4667 | // string-content user, system, attachment |
| 4668 | if ('isVirtual' in m && m.isVirtual) { |
| 4669 | const { isVirtual: _omit, ...rest } = m |
| 4670 | return [rest] |
| 4671 | } |
| 4672 | return [m] |
| 4673 | }) as Transcript |
| 4674 | } |
| 4675 | |
| 4676 | // Cache for cleanMessagesForLogging keyed by the messages array reference. |
| 4677 | // Eliminates O(n) re-filtering when the same array grows via .push() between |
no test coverage detected