repositories / dotfiles
dotfiles
bugabingas dorkfiles
owned by admin
neovim/lua/bugabinga/pivi/backfill.lua
Raw--- Turns a session's recorded history into an ordered list of render ops.
--- Pure: it reads `get_entries` data and never touches a buffer or Pi.
--- The stored shape differs from the live stream, so this is a distinct
--- adapter: tool calls live inside assistant messages and tool results are
--- their own `toolResult` messages, correlated by call id. A stored result
--- carries only text, so a restored tool entry degrades to its output rather
--- than a fabricated diff.
--- The text of a message's content, whether a string or a list of parts.
local text_of = function( content )
if type( content ) == 'string' then return content end
if type( content ) ~= 'table' then return '' end
local pieces = {}
for _, part in ipairs( content ) do
if type( part ) == 'table' and part.type == 'text' and type( part.text ) == 'string' then
table.insert( pieces, part.text )
end
end
return table.concat( pieces, '' )
end
--- A content value as a list of parts.
local parts_of = function( content )
if type( content ) == 'string' then return { { type = 'text', text = content, }, } end
if type( content ) ~= 'table' then return {} end
return content
end
--- Indexes tool results by the call they answer.
local results_by_call = function( entries )
local results = {}
for _, item in ipairs( entries ) do
local message = item.type == 'message' and item.message or nil
if type( message ) == 'table' and message.role == 'toolResult' and message.toolCallId then
results[message.toolCallId] = { content = message.content, failed = message.isError == true, }
end
end
return results
end
--- Plans the entries a resumed transcript should rebuild, in order.
--- @param data table a `get_entries` response payload, or its `entries`
--- @return table ops ordered render operations
local plan = function( data )
local entries = {}
if type( data ) == 'table' then
entries = type( data.entries ) == 'table' and data.entries or data
end
if type( entries ) ~= 'table' then return {} end
local results = results_by_call( entries )
local ops = {}
for _, item in ipairs( entries ) do
local message = item.type == 'message' and item.message or nil
if type( message ) == 'table' then
local role = message.role
if role == 'user' then
local text = text_of( message.content )
if vim.trim( text ) ~= '' then table.insert( ops, { kind = 'prompt', text = text, } ) end
elseif role == 'assistant' then
local pending = {}
local flush = function()
local text = table.concat( pending, '' )
if vim.trim( text ) ~= '' then table.insert( ops, { kind = 'answer', text = text, } ) end
pending = {}
end
for _, part in ipairs( parts_of( message.content ) ) do
if type( part ) == 'table' then
if part.type == 'text' and type( part.text ) == 'string' then
table.insert( pending, part.text )
elseif ( part.type == 'tool_call' or part.type == 'tool_use' ) and part.id then
flush()
local answer = results[part.id] or {}
table.insert( ops, {
kind = 'tool',
tool = part.name,
args = part.arguments,
call = part.id,
result = { content = answer.content, },
failed = answer.failed == true,
} )
end
end
end
flush()
end
end
end
return ops
end
return {
plan = plan,
}