---@diagnostic disable: duplicate-set-field, need-check-nil, param-type-mismatch, assign-type-mismatch local session = require 'bugabinga.pivi.session' local publish = require 'bugabinga.pivi.publish' local transcript = require 'bugabinga.pivi.transcript' local activity = require 'bugabinga.pivi.activity' local follow = require 'bugabinga.pivi.follow' local dialog = require 'bugabinga.pivi.dialog' local entry = require 'bugabinga.pivi.entry' local render = require 'bugabinga.pivi.render' local fold = require 'bugabinga.pivi.fold' local fake = dofile 'tests/helpers/pivi_fake.lua' local fixture = dofile 'tests/helpers/pivi_fixture.lua' local create = function( channel, options ) options = options or {} options.spawn = channel.spawn options.name = options.name or 'spec' options.extension = false options.skill = false options.executable = options.executable or 'pi' options.env = {} return session.create( options ) end -- Flags that would strip the user's own Pi configuration. local STRIPPING_FLAGS = { '--no-extensions', '--no-skills', '--no-prompt-templates', '--no-context-files', '--no-themes', '--no-builtin-tools', '--system-prompt', } local commands_of = function( channel, kind ) local found = {} for _, command in ipairs( channel.commands ) do if command.type == kind then table.insert( found, command ) end end return found end describe( 'pivi session', function() after_each( function() fake.drain() session.stop_all() transcript.forget_all() publish.reset() follow.reset() activity.clear_trail() fake.close_extra_windows() vim.cmd 'silent! %bwipeout!' end ) local temp_buffer = function( lines ) local path = vim.fs.normalize( vim.fn.tempname() .. '.txt' ) vim.fn.writefile( lines, path ) vim.cmd( 'edit! ' .. vim.fn.fnameescape( path ) ) return vim.api.nvim_get_current_buf(), path end it( 'starts pi in rpc mode inside the session working directory', function() local channel = fake.new() local current = create( channel, { cwd = vim.uv.cwd(), } ) assert.same( { 'pi', '--mode', 'rpc', '--name', 'spec', }, channel.command ) assert.equal( vim.uv.cwd(), channel.options.cwd ) assert.equal( 'spec', current.name ) assert.same( { 'spec', }, session.list() ) end ) it( 'resolves the pi executable to a real path', function() local built = session.build_command {} local resolved = built[1] assert.is_true( resolved:lower():find( 'pi', 1, true ) ~= nil ) -- A bare name would reach the spawner unresolved and fail on Windows. assert.equal( 1, vim.fn.executable( resolved ) ) end ) it( 'never strips the user\'s own pi configuration', function() local built = session.build_command { executable = 'pi', } for _, flag in ipairs( STRIPPING_FLAGS ) do assert.is_false( vim.list_contains( built, flag ) ) end assert.is_false( vim.list_contains( built, '--no-tools' ) ) end ) it( 'adds only its companion extension and its skill to the user\'s pi', function() local plain = session.build_command { extension = false, skill = false, executable = 'pi', } assert.same( { 'pi', '--mode', 'rpc', }, plain ) local built = session.build_command { executable = 'pi', } for index = 4, #built, 2 do local flag = built[index] assert.is_true( flag == '-e' or flag == '--skill' ) assert.is_true( built[index + 1]:find( 'pivi', 1, true ) ~= nil ) end end ) it( 'teaches a working session about pivi through its skill', function() local built = session.build_command {} local index = vim.fn.index( built, '--skill' ) + 1 assert.is_true( index > 0 ) assert.is_true( built[index + 1]:find( 'SKILL.md', 1, true ) ~= nil ) end ) it( 'sends an idle submission as a plain prompt', function() local channel = fake.new() local current = create( channel ) local accepted, rejection = current.submit( 'do the thing', { capture = false, } ) assert.is_true( accepted ) assert.is_nil( rejection ) local prompts = commands_of( channel, 'prompt' ) assert.equal( 1, #prompts ) assert.equal( 'do the thing', prompts[1].message ) end ) it( 'rejects a submission during a run instead of silently dropping it', function() local channel = fake.new() local current = create( channel ) channel.frame { type = 'agent_start', } assert.is_true( channel.settle( function() return current.status.run == 'running' end ) ) local accepted, rejection = current.submit( 'late thought', { capture = false, } ) assert.is_false( accepted ) assert.is_string( rejection ) assert.equal( 0, #commands_of( channel, 'prompt' ) ) end ) it( 'accepts an explicit steering delivery during a run', function() local channel = fake.new() local current = create( channel ) channel.frame { type = 'agent_start', } assert.is_true( channel.settle( function() return current.status.run == 'running' end ) ) local accepted = current.submit( 'focus on errors', { capture = false, delivery = 'steer', } ) assert.is_true( accepted ) local steers = commands_of( channel, 'steer' ) assert.equal( 1, #steers ) assert.equal( 'focus on errors', steers[1].message ) end ) it( 'accepts an explicit follow-up delivery during a run', function() local channel = fake.new() local current = create( channel ) channel.frame { type = 'agent_start', } assert.is_true( channel.settle( function() return current.status.run == 'running' end ) ) assert.is_true( current.submit( 'then summarize', { capture = false, delivery = 'follow_up', } ) ) assert.equal( 1, #commands_of( channel, 'follow_up' ) ) end ) it( 'refuses an unknown delivery', function() local channel = fake.new() local current = create( channel ) local accepted, rejection = current.submit( 'x', { capture = false, delivery = 'shout', } ) assert.is_false( accepted ) assert.is_string( rejection ) end ) it( 'streams assistant text into the transcript buffer', function() local channel = fake.new() local current = create( channel ) channel.frame { type = 'message_start', message = { role = 'assistant', }, } channel.frame { type = 'message_update', assistantMessageEvent = { type = 'text_delta', contentIndex = 0, delta = 'hello ', }, } channel.frame { type = 'message_update', assistantMessageEvent = { type = 'text_delta', contentIndex = 0, delta = 'editor', }, } assert.is_true( channel.settle( function() return vim.list_contains( transcript.lines( current.buffer ), 'hello editor' ) end ) ) end ) it( 'opens no answer entry for a turn that only calls tools', function() local channel = fake.new() local current = create( channel ) channel.frame { type = 'message_start', message = { role = 'assistant', }, } channel.frame { type = 'tool_execution_start', toolCallId = 'c1', toolName = 'read', args = { path = 'greet.lua', }, } channel.frame { type = 'tool_execution_end', toolCallId = 'c1', toolName = 'read', isError = false, } channel.flush() local kinds = {} for _, item in ipairs( entry.list( current.buffer ) ) do table.insert( kinds, item.kind ) end assert.same( { 'tool', }, kinds ) assert.is_true( table.concat( transcript.lines( current.buffer ), '\n' ):find( 'read', 1, true ) ~= nil ) end ) it( 'opens exactly one answer entry once real text arrives', function() local channel = fake.new() local current = create( channel ) channel.frame { type = 'message_start', message = { role = 'assistant', }, } for _, piece in ipairs { 'all ', 'done', } do channel.frame { type = 'message_update', assistantMessageEvent = { type = 'text_delta', contentIndex = 0, delta = piece, }, } end channel.flush() local answers = {} for _, item in ipairs( entry.list( current.buffer ) ) do if item.kind == 'answer' then table.insert( answers, item ) end end assert.equal( 1, #answers ) assert.is_true( vim.list_contains( transcript.lines( current.buffer ), 'all done' ) ) end ) it( 'drops the blank lines a model opens with', function() local channel = fake.new() local current = create( channel ) channel.frame { type = 'message_start', message = { role = 'assistant', }, } channel.frame { type = 'message_update', assistantMessageEvent = { type = 'text_delta', contentIndex = 0, delta = '\n\nDone.', }, } channel.flush() local lines = transcript.lines( current.buffer ) local answer = entry.list( current.buffer )[1] assert.equal( 'answer', answer.kind ) assert.equal( 'Done.', lines[answer.first] ) assert.equal( answer.first, answer.last ) end ) it( 'ignores an empty text delta', function() local channel = fake.new() local current = create( channel ) channel.frame { type = 'message_start', message = { role = 'assistant', }, } channel.frame { type = 'message_update', assistantMessageEvent = { type = 'text_delta', contentIndex = 0, delta = '', }, } channel.flush() assert.same( {}, entry.list( current.buffer ) ) end ) it( 'names what a tool acts on, relative to the session directory', function() local channel = fake.new() local current = create( channel, { cwd = vim.uv.cwd(), } ) channel.frame { type = 'tool_execution_start', toolCallId = 'c1', toolName = 'edit', args = { path = vim.fs.normalize( vim.uv.cwd() .. '/lua/deep/file.lua' ), }, } channel.flush() local rendered = table.concat( transcript.lines( current.buffer ), '\n' ) assert.is_true( rendered:find( 'lua/deep/file.lua', 1, true ) ~= nil ) assert.is_nil( rendered:find( vim.uv.cwd(), 1, true ) ) end ) it( 'marks a failed tool call', function() local channel = fake.new() local current = create( channel ) channel.frame { type = 'tool_execution_start', toolCallId = 'c1', toolName = 'read', args = {}, } channel.frame { type = 'tool_execution_end', toolCallId = 'c1', toolName = 'read', isError = true, } channel.flush() assert.is_true( vim.list_contains( transcript.lines( current.buffer ), 'failed' ) ) assert.is_true( entry.list( current.buffer )[1].meta.failed ) end ) it( 'writes a submission exactly as it was typed', function() local channel = fake.new() local current = create( channel ) current.submit( 'first\nsecond', { capture = false, } ) local prompt = entry.list( current.buffer )[1] local lines = transcript.lines( current.buffer ) assert.equal( 'prompt', prompt.kind ) assert.equal( 'first\nsecond', table.concat( vim.list_slice( lines, prompt.first, prompt.last ), '\n' ) ) end ) it( 'yields the original message text when an answer is copied', function() local channel = fake.new() local current = create( channel ) local authored = '# Heading\n\nSome **bold** text and `code`.\n\n```lua\nlocal x = 1\n```' channel.frame { type = 'message_start', message = { role = 'assistant', }, } for piece in authored:gmatch '[^\n]*\n?' do if piece ~= '' then channel.frame { type = 'message_update', assistantMessageEvent = { type = 'text_delta', contentIndex = 0, delta = piece, }, } end end channel.flush() local answer = entry.list( current.buffer )[1] local lines = transcript.lines( current.buffer ) local copied = table.concat( vim.list_slice( lines, answer.first, answer.last ), '\n' ) assert.equal( 'answer', answer.kind ) assert.equal( authored, copied ) end ) it( 'frames entries with decoration rather than text', function() local channel = fake.new() local current = create( channel ) current.submit( 'do it', { capture = false, } ) channel.frame { type = 'tool_execution_start', toolCallId = 'c1', toolName = 'read', args = { path = 'a.lua', }, } channel.flush() local rendered = table.concat( transcript.lines( current.buffer ), '\n' ) for _, framing in ipairs { '> ', '\u{25b8} ', '## pi', ' ! ', } do assert.is_nil( rendered:find( framing, 1, true ) ) end assert.is_true( #render.marks( current.buffer ) > 0 ) end ) it( 'makes every part of a turn an addressable entry', function() local channel = fake.new() local current = create( channel ) current.submit( 'do the thing', { capture = false, } ) channel.frame { type = 'message_start', message = { role = 'assistant', }, } channel.frame { type = 'tool_execution_start', toolCallId = 'c1', toolName = 'read', args = { path = 'greet.lua', }, } channel.frame { type = 'tool_execution_end', toolCallId = 'c1', toolName = 'read', isError = false, } channel.frame { type = 'message_update', assistantMessageEvent = { type = 'text_delta', contentIndex = 0, delta = 'all done', }, } channel.frame { type = 'agent_settled', } channel.flush() local kinds = {} for _, item in ipairs( entry.list( current.buffer ) ) do table.insert( kinds, item.kind ) end assert.same( { 'prompt', 'tool', 'answer', }, kinds ) local lines = transcript.lines( current.buffer ) for _, item in ipairs( entry.list( current.buffer ) ) do assert.is_true( item.first >= 1 and item.last <= #lines ) assert.is_true( item.first <= item.last ) end end ) it( 'keeps a tool entry addressable with its call and result', function() local channel = fake.new() local current = create( channel ) channel.frame { type = 'tool_execution_start', toolCallId = 'c1', toolName = 'edit', args = { path = 'greet.lua', }, } channel.frame { type = 'tool_execution_end', toolCallId = 'c1', toolName = 'edit', isError = false, result = { content = { { type = 'text', text = 'edited', }, }, }, } channel.flush() local tool = entry.list( current.buffer )[1] assert.equal( 'tool', tool.kind ) assert.equal( 'edit', tool.meta.tool ) assert.equal( 'greet.lua', tool.meta.args.path ) assert.is_table( tool.meta.result ) assert.is_false( tool.meta.failed ) end ) it( 'shows a tool result collapsed on arrival without changing history', function() local channel = fake.new() local current = create( channel ) local win = transcript.show( current.buffer ) channel.frame { type = 'tool_execution_start', toolCallId = 'c1', toolName = 'read', args = { path = 'a.lua', }, } channel.frame { type = 'tool_execution_end', toolCallId = 'c1', toolName = 'read', isError = false, result = { content = { { type = 'text', text = 'line one\nline two\nline three', }, }, }, } channel.flush() local tool = entry.list( current.buffer )[1] local lines = transcript.lines( current.buffer ) assert.equal( 'tool', tool.kind ) assert.is_true( tool.last > tool.first ) assert.is_true( vim.list_contains( lines, 'line two' ) ) vim.api.nvim_win_call( win, function() assert.is_true( fold.closed( tool.first ) ) end ) -- Collapsing is presentation: nothing was submitted, queued, or recorded. assert.same( { steering = {}, follow_up = {}, }, current.status.queue ) assert.same( {}, current.submissions ) assert.same( {}, commands_of( channel, 'prompt' ) ) end ) it( 'states how a tool ended beside what it acted on', function() local channel = fake.new() local current = create( channel ) local win = transcript.show( current.buffer ) channel.frame { type = 'tool_execution_start', toolCallId = 'c1', toolName = 'edit', args = { path = 'greet.lua', }, } channel.frame { type = 'tool_execution_end', toolCallId = 'c1', toolName = 'edit', isError = false, result = { content = { { type = 'text', text = 'Successfully replaced 1 block(s) in greet.lua.', }, }, details = { diff = ' 1 keep\n-2 old\n+2 new\n', firstChangedLine = 2, }, }, } channel.flush() local item = entry.list( current.buffer )[1] local lines = transcript.lines( current.buffer ) -- The outcome is decoration, never buffer text. assert.equal( 'edit greet.lua', lines[item.first] ) local outcomes = {} for _, mark in ipairs( render.marks( current.buffer ) ) do local virt = ( mark[4] or {} ).virt_text if virt then table.insert( outcomes, virt[1][1] ) end end assert.is_true( vim.iter( outcomes ):any( function( text ) return text:find( '+1 -1', 1, true ) ~= nil end ) ) -- And it survives collapsing. local collapsed = vim.api.nvim_win_call( win, function() vim.v.foldstart = item.first vim.v.foldend = item.last return fold.text() end ) assert.is_true( collapsed:find( 'greet.lua', 1, true ) ~= nil ) assert.is_true( collapsed:find( '+1 -1', 1, true ) ~= nil ) end ) it( 'shows the output of a running tool as it arrives', function() local channel = fake.new() local current = create( channel ) channel.frame { type = 'tool_execution_start', toolCallId = 'c1', toolName = 'bash', args = { command = 'count', }, } channel.flush() local opened = entry.list( current.buffer )[1] assert.equal( opened.first, opened.last ) channel.frame { type = 'tool_execution_update', toolCallId = 'c1', toolName = 'bash', partialResult = { content = { { type = 'text', text = 'one', }, }, }, } channel.flush() local growing = entry.list( current.buffer )[1] assert.is_true( vim.list_contains( transcript.lines( current.buffer ), 'one' ) ) assert.equal( opened.first, growing.first ) assert.equal( growing.first + 1, growing.last ) channel.frame { type = 'tool_execution_update', toolCallId = 'c1', toolName = 'bash', partialResult = { content = { { type = 'text', text = 'one\ntwo\nthree', }, }, }, } channel.flush() local grown = entry.list( current.buffer )[1] local lines = transcript.lines( current.buffer ) -- The tail was rewritten rather than appended twice. assert.equal( opened.first, grown.first ) assert.equal( grown.first + 3, grown.last ) assert.same( { 'bash count', 'one', 'two', 'three', }, vim.list_slice( lines, grown.first, grown.last ) ) channel.frame { type = 'tool_execution_end', toolCallId = 'c1', toolName = 'bash', isError = false, result = { content = { { type = 'text', text = 'one\ntwo\nthree\ndone', }, }, }, } channel.flush() local finished = entry.list( current.buffer )[1] assert.same( { 'bash count', 'one', 'two', 'three', 'done', }, vim.list_slice( transcript.lines( current.buffer ), finished.first, finished.last ) ) end ) it( 'keeps later entries intact while a tool streams', function() local channel = fake.new() local current = create( channel ) current.submit( 'do it', { capture = false, } ) channel.frame { type = 'tool_execution_start', toolCallId = 'c1', toolName = 'bash', args = { command = 'go', }, } for _, text in ipairs { 'a', 'a\nb', 'a\nb\nc', } do channel.frame { type = 'tool_execution_update', toolCallId = 'c1', toolName = 'bash', partialResult = { content = { { type = 'text', text = text, }, }, }, } end channel.flush() local listed = entry.list( current.buffer ) local prompt = listed[1] assert.equal( 'prompt', prompt.kind ) assert.equal( 'do it', transcript.lines( current.buffer )[prompt.first] ) assert.is_true( listed[2].first > prompt.last ) end ) it( 'says nothing about a tool that reported nothing', function() local channel = fake.new() local current = create( channel ) channel.frame { type = 'tool_execution_start', toolCallId = 'c1', toolName = 'write', args = { path = 'new.lua', }, } channel.frame { type = 'tool_execution_end', toolCallId = 'c1', toolName = 'write', isError = false, result = { content = { { type = 'text', text = 'wrote new.lua', }, }, }, } channel.flush() local item = entry.list( current.buffer )[1] assert.is_nil( require( 'bugabinga.pivi.tool' ).outcome( item.meta ) ) assert.equal( 'write new.lua', transcript.lines( current.buffer )[item.first] ) end ) it( 'runs a banged prompt as a command instead of saying it', function() local channel = fake.new() local current = create( channel ) assert.is_true( current.submit( '!just test', { capture = false, } ) ) local ran for _, sent in ipairs( channel.commands ) do if sent.type == 'bash' then ran = sent end assert.is_not.equal( 'prompt', sent.type ) end assert.is_table( ran ) assert.equal( 'just test', ran.command ) assert.is_false( ran.excludeFromContext ) end ) it( 'keeps a doubly banged command out of the conversation', function() local channel = fake.new() local current = create( channel ) current.submit( '!!git status', { capture = false, } ) local ran for _, sent in ipairs( channel.commands ) do if sent.type == 'bash' then ran = sent end end assert.is_table( ran ) assert.equal( 'git status', ran.command ) assert.is_true( ran.excludeFromContext ) end ) it( 'shows what a command produced inside its own entry', function() local channel = fake.new() local current = create( channel ) current.submit( '!echo hi', { capture = false, } ) local ran for _, sent in ipairs( channel.commands ) do if sent.type == 'bash' then ran = sent end end channel.frame { id = ran.id, type = 'response', command = 'bash', success = true, data = { output = 'hi', exitCode = 0, }, } channel.flush() local item = entry.list( current.buffer )[1] local lines = transcript.lines( current.buffer ) assert.equal( 'tool', item.kind ) assert.equal( 'bang', item.meta.tool ) assert.is_true( vim.list_contains( lines, 'hi' ) ) assert.is_false( item.meta.failed ) end ) it( 'marks a command that exited badly', function() local channel = fake.new() local current = create( channel ) current.submit( '!false', { capture = false, } ) local ran for _, sent in ipairs( channel.commands ) do if sent.type == 'bash' then ran = sent end end channel.frame { id = ran.id, type = 'response', command = 'bash', success = true, data = { output = '', exitCode = 1, }, } channel.flush() assert.is_true( entry.list( current.buffer )[1].meta.failed ) end ) it( 'sends a referenced file with what was said', function() local path = vim.fs.normalize( vim.fn.tempname() .. '.lua' ) vim.fn.writefile( { 'local referenced = true', }, path ) local channel = fake.new() local current = create( channel ) current.submit( 'explain @' .. path, { capture = false, } ) local sent = commands_of( channel, 'prompt' )[1] assert.is_table( sent ) assert.is_true( sent.message:find( 'local referenced = true', 1, true ) ~= nil ) assert.is_true( sent.message:find( ' 0 end ) ) assert.equal( 'spec', seen[#seen].session ) assert.equal( 'pi:run', publish.summary 'spec' ) assert.equal( 'running', publish.read().sessions.spec.run ) vim.api.nvim_del_augroup_by_id( group ) end ) it( 'marks the session stopped when the child exits', function() local channel = fake.new() local current = create( channel ) channel.close( 0 ) assert.is_true( channel.settle( function() return current.status.connection == 'stopped' end ) ) assert.equal( 'pi:off', require( 'bugabinga.pivi.status' ).summary( current.status ) ) end ) it( 'removes a stopped session from the registry and the published state', function() local channel = fake.new() local current = create( channel ) current.stop() assert.same( {}, session.list() ) assert.is_nil( publish.read().sessions.spec ) assert.is_true( channel.stdin_closed ) end ) it( 'shows streaming edit intent without touching the buffer', function() local bufnr, path = temp_buffer { 'local value = 1', } local revision = vim.api.nvim_buf_get_changedtick( bufnr ) local before = vim.api.nvim_buf_get_lines( bufnr, 0, -1, false ) local channel = fake.new() create( channel ) channel.frame { type = 'message_update', assistantMessageEvent = { type = 'toolcall_start', contentIndex = 0, id = 'c1', toolName = 'edit', }, } channel.frame { type = 'message_update', assistantMessageEvent = { type = 'toolcall_delta', contentIndex = 0, delta = '{"path":"' .. path:gsub( '\\', '\\\\' ) .. '","new_text":"local value = 2', }, } assert.is_true( channel.settle( function() return #activity.ghost_marks( bufnr ) > 0 end ) ) assert.equal( revision, vim.api.nvim_buf_get_changedtick( bufnr ) ) assert.same( before, vim.api.nvim_buf_get_lines( bufnr, 0, -1, false ) ) channel.frame { type = 'message_update', assistantMessageEvent = { type = 'toolcall_end', contentIndex = 0, toolCall = { id = 'c1', name = 'edit', arguments = { path = path, new_text = 'local value = 2', }, }, }, } assert.is_true( channel.settle( function() return #activity.ghost_marks( bufnr ) == 0 end ) ) assert.same( before, vim.api.nvim_buf_get_lines( bufnr, 0, -1, false ) ) end ) it( 'follows a located tool call when following is enabled', function() local _, path = temp_buffer { 'one', 'two', 'three', } local working = vim.api.nvim_get_current_win() follow.set 'window' local channel = fake.new() create( channel ) channel.frame { type = 'tool_execution_start', toolCallId = 'c1', toolName = 'read', args = { path = path, line = 3, }, } assert.is_true( channel.settle( function() return follow.window() ~= nil end ) ) assert.equal( working, vim.api.nvim_get_current_win() ) assert.equal( 3, vim.api.nvim_win_get_cursor( follow.window() )[1] ) end ) it( 'answers an extension dialog with a matching identifier', function() local original = vim.ui.select vim.ui.select = function( items, _, on_choice ) on_choice( items[1] ) end local channel = fake.new() create( channel ) channel.frame { type = 'extension_ui_request', id = 'uuid-1', method = 'select', title = 'Allow?', options = { 'Allow', 'Block', }, } assert.is_true( channel.settle( function() for _, entry in ipairs( channel.commands ) do if entry.type == 'extension_ui_response' then return true end end return false end ) ) local answer for _, entry in ipairs( channel.commands ) do if entry.type == 'extension_ui_response' then answer = entry end end assert.equal( 'uuid-1', answer.id ) assert.equal( 'Allow', answer.value ) vim.ui.select = original end ) it( 'replays a recorded run made with the user\'s own pi configuration', function() local channel = fake.new() local current = create( channel ) local settled = false current.on( function( event ) if event.type == 'agent_settled' then settled = true end end ) for _, frame in ipairs( fixture.frames 'rpc-tool-stream.jsonl' ) do channel.frame( frame ) end assert.is_true( channel.settle( function() return settled end ) ) assert.equal( 'idle', current.status.run ) assert.same( {}, current.client.failures() ) local rendered = table.concat( transcript.lines( current.buffer ), '\n' ) assert.is_true( rendered:find( 'read', 1, true ) ~= nil ) local answered = false for _, frame in ipairs( fixture.frames 'rpc-tool-stream.jsonl' ) do if frame.type == 'message_end' and frame.message and frame.message.role == 'assistant' then for _, block in ipairs( frame.message.content or {} ) do -- The transcript drops the blank lines a model opens with. local text = vim.trim( block.text or '' ) if text ~= '' then answered = answered or rendered:find( text:sub( 1, 12 ), 1, true ) ~= nil end end end end assert.is_true( answered ) end ) it( 'absorbs extension status and widget traffic from a recorded run', function() dialog.reset() local channel = fake.new() local current = create( channel ) local frames = fixture.frames 'rpc-tool-stream.jsonl' local last_set, requests = 0, 0 for index, frame in ipairs( frames ) do if frame.type == 'extension_ui_request' then requests = requests + 1 if frame.statusText or frame.widgetLines then last_set = index end end end assert.is_true( requests > 0 ) assert.is_true( last_set > 0 ) for index = 1, last_set do channel.frame( frames[index] ) end channel.flush() assert.is_true( next( dialog.statuses() ) ~= nil or next( dialog.widgets() ) ~= nil ) for index = last_set + 1, #frames do channel.frame( frames[index] ) end channel.flush() -- The recorded extensions clear their own status and widgets on shutdown. assert.is_nil( next( dialog.statuses() ) ) assert.is_nil( next( dialog.widgets() ) ) -- Fire and forget requests are never answered. for _, command in ipairs( channel.commands ) do assert.is_not.equal( 'extension_ui_response', command.type ) end assert.same( {}, current.client.failures() ) dialog.reset() end ) it( 'keeps several sessions with distinct names', function() local first = create( fake.new(), { name = 'one', } ) local second = create( fake.new(), { name = 'two', } ) assert.same( { 'one', 'two', }, session.list() ) assert.is_not.equal( first.buffer, second.buffer ) session.set_active 'two' assert.equal( 'two', session.active().name ) end ) end )