Luigit
repositories / dotfiles

dotfiles

bugabingas dorkfiles

owned by admin

neovim/lua/bugabinga/pivi/status.lua

Raw
local copy = function( state )
  local next_state = vim.deepcopy( state )
  return next_state
end

local initial = function()
  return {
    connection = 'starting',
    run = 'idle',
    tool = nil,
    queue = { steering = {}, follow_up = {}, },
    compacting = false,
    retry = nil,
    error = nil,
    model = nil,
    thinking = nil,
    session = nil,
    context = nil,
    exit_code = nil,
  }
end

--- Folds one RPC event into cached semantic status.
--- Pure: no editor state is read or written here.
--- @param state table current status
--- @param event table one RPC event
--- @return table state the next status
local reduce = function( state, event )
  if type( event ) ~= 'table' or type( event.type ) ~= 'string' then return state end
  local next_state = copy( state )
  local kind = event.type

  if kind == 'agent_start' then
    next_state.connection = 'running'
    next_state.run = 'running'
    next_state.error = nil
  elseif kind == 'agent_end' then
    next_state.run = event.willRetry and 'retrying' or 'running'
  elseif kind == 'agent_settled' then
    next_state.run = 'idle'
    next_state.tool = nil
    next_state.retry = nil
  elseif kind == 'tool_execution_start' then
    next_state.run = 'running'
    next_state.tool = { id = event.toolCallId, name = event.toolName, }
  elseif kind == 'tool_execution_end' then
    if next_state.tool and next_state.tool.id == event.toolCallId then next_state.tool = nil end
  elseif kind == 'queue_update' then
    next_state.queue = {
      steering = event.steering or {},
      follow_up = event.followUp or {},
    }
  elseif kind == 'compaction_start' then
    next_state.compacting = true
  elseif kind == 'compaction_end' then
    next_state.compacting = false
    if event.errorMessage then next_state.error = event.errorMessage end
  elseif kind == 'auto_retry_start' then
    next_state.run = 'retrying'
    next_state.retry = { attempt = event.attempt, max = event.maxAttempts, delay = event.delayMs, }
    next_state.error = event.errorMessage
  elseif kind == 'auto_retry_end' then
    next_state.retry = nil
    if event.success then
      next_state.run = 'running'
      next_state.error = nil
    else
      next_state.run = 'idle'
      next_state.error = event.finalError
    end
  elseif kind == 'extension_error' then
    next_state.error = event.error
  elseif kind == 'message_end' then
    local message = event.message
    if message and message.errorMessage then next_state.error = message.errorMessage end
  end

  return next_state
end

--- Folds a `get_state` response payload into cached status.
local reduce_state = function( state, data )
  if type( data ) ~= 'table' then return state end
  local next_state = copy( state )
  next_state.connection = 'running'
  next_state.model = data.model and data.model.id or next_state.model
  next_state.thinking = data.thinkingLevel or next_state.thinking
  next_state.session = {
    id = data.sessionId,
    name = data.sessionName,
    file = data.sessionFile,
    messages = data.messageCount,
  }
  if data.isStreaming ~= nil and next_state.run == 'idle' and data.isStreaming then
    next_state.run = 'running'
  end
  next_state.compacting = data.isCompacting == true
  return next_state
end

--- Folds a `get_session_stats` response payload into cached status.
local reduce_stats = function( state, data )
  if type( data ) ~= 'table' then return state end
  local next_state = copy( state )
  local usage = data.contextUsage
  if usage then
    next_state.context = {
      tokens = usage.tokens,
      window = usage.contextWindow,
      percent = usage.percent,
    }
  end
  next_state.cost = data.cost
  return next_state
end

local stopped = function( state, exit_code )
  local next_state = copy( state )
  next_state.connection = 'stopped'
  next_state.run = 'idle'
  next_state.tool = nil
  next_state.exit_code = exit_code
  return next_state
end

local is_busy = function( state )
  return state.run == 'running' or state.run == 'retrying' or state.compacting == true
end

local pending_count = function( state )
  return #( state.queue.steering or {} ) + #( state.queue.follow_up or {} )
end

--- Short human summary suitable for a statusline.
local summary = function( state )
  if state.connection == 'stopped' then return 'pi:off' end
  if state.run == 'retrying' then return 'pi:retry' end
  if state.error then return 'pi:error' end
  if state.compacting then return 'pi:compacting' end
  if state.tool then return 'pi:' .. tostring( state.tool.name ) end
  if state.run == 'running' then return 'pi:run' end
  local pending = pending_count( state )
  if pending > 0 then return 'pi:idle+' .. tostring( pending ) end
  return 'pi:idle'
end

return {
  initial = initial,
  reduce = reduce,
  reduce_state = reduce_state,
  reduce_stats = reduce_stats,
  stopped = stopped,
  is_busy = is_busy,
  pending_count = pending_count,
  summary = summary,
}