* 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>, )
| 4394 | * and leave an orphaned tool_result on disk. |
| 4395 | */ |
| 4396 | function transformMessagesForExternalTranscript( |
| 4397 | messages: Transcript, |
| 4398 | replIds: Set<string>, |
| 4399 | ): Transcript { |
| 4400 | return messages.flatMap(m => { |
| 4401 | if (m.type === 'assistant' && Array.isArray(m.message.content)) { |
| 4402 | const content = m.message.content |
| 4403 | const hasRepl = content.some( |
| 4404 | b => b.type === 'tool_use' && b.name === REPL_TOOL_NAME, |
| 4405 | ) |
| 4406 | const filtered = hasRepl |
| 4407 | ? content.filter( |
| 4408 | b => !(b.type === 'tool_use' && b.name === REPL_TOOL_NAME), |
| 4409 | ) |
| 4410 | : content |
| 4411 | if (filtered.length === 0) return [] |
| 4412 | if (m.isVirtual) { |
| 4413 | const { isVirtual: _omit, ...rest } = m |
| 4414 | return [{ ...rest, message: { ...m.message, content: filtered } }] |
| 4415 | } |
| 4416 | if (filtered !== content) { |
| 4417 | return [{ ...m, message: { ...m.message, content: filtered } }] |
| 4418 | } |
| 4419 | return [m] |
| 4420 | } |
| 4421 | if (m.type === 'user' && Array.isArray(m.message.content)) { |
| 4422 | const content = m.message.content |
| 4423 | const hasRepl = content.some( |
| 4424 | b => b.type === 'tool_result' && replIds.has(b.tool_use_id), |
| 4425 | ) |
| 4426 | const filtered = hasRepl |
| 4427 | ? content.filter( |
| 4428 | b => !(b.type === 'tool_result' && replIds.has(b.tool_use_id)), |
| 4429 | ) |
| 4430 | : content |
| 4431 | if (filtered.length === 0) return [] |
| 4432 | if (m.isVirtual) { |
| 4433 | const { isVirtual: _omit, ...rest } = m |
| 4434 | return [{ ...rest, message: { ...m.message, content: filtered } }] |
| 4435 | } |
| 4436 | if (filtered !== content) { |
| 4437 | return [{ ...m, message: { ...m.message, content: filtered } }] |
| 4438 | } |
| 4439 | return [m] |
| 4440 | } |
| 4441 | // string-content user, system, attachment |
| 4442 | if ('isVirtual' in m && m.isVirtual) { |
| 4443 | const { isVirtual: _omit, ...rest } = m |
| 4444 | return [rest] |
| 4445 | } |
| 4446 | return [m] |
| 4447 | }) as Transcript |
| 4448 | } |
| 4449 | |
| 4450 | export function cleanMessagesForLogging( |
| 4451 | messages: Message[], |
no test coverage detected