Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 54 additions & 6 deletions lua/opencode/ui/chat.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ local M = {}
---@field streaming_message_index number|nil
---@field provider_id string
---@field model_id string
---@field session_creation_pending boolean

---@type opencode.ui.chat.State|nil
M.state = nil
Expand Down Expand Up @@ -86,6 +87,7 @@ function M.open(opts)
streaming_message_index = nil,
provider_id = opts.provider_id or config.provider_id or "anthropic",
model_id = opts.model_id or config.model_id or "claude-3-5-sonnet-20241022",
session_creation_pending = false,
}

-- Setup keymaps
Expand Down Expand Up @@ -235,10 +237,37 @@ function M.render()
end
end

---Validate that the chat is ready for interaction
---@return boolean ready True if ready, false otherwise (with notification shown)
local function validate_session_ready()
if not M.state or not M.state.port then
vim.notify("No connection to opencode", vim.log.levels.ERROR, { title = "opencode" })
return false
end

if not M.state.session_id then
-- Check if session creation is already in progress
if M.state.session_creation_pending then
vim.notify("Session is being created, please wait...", vim.log.levels.WARN, { title = "opencode" })
return false
end

vim.notify(
"No active session. Creating a new session...",
vim.log.levels.WARN,
{ title = "opencode" }
)
-- Try to create a session on-demand
M.new_session()
return false
end

return true
end

---Prompt for user input
function M.prompt_input()
if not M.state or not M.state.port or not M.state.session_id then
vim.notify("No active session", vim.log.levels.ERROR, { title = "opencode" })
if not validate_session_ready() then
return
end

Expand All @@ -252,8 +281,7 @@ end
---Send a message
---@param text string
function M.send_message(text)
if not M.state or not M.state.port or not M.state.session_id then
vim.notify("No active session", vim.log.levels.ERROR, { title = "opencode" })
if not validate_session_ready() then
return
end

Expand Down Expand Up @@ -356,18 +384,37 @@ function M.new_session()
return
end

-- Clear messages
-- Clear messages and mark session creation as pending
M.state.messages = {}
M.state.session_id = nil
M.state.streaming_message_index = nil
M.state.session_creation_pending = true
M.render()

-- Create new session
local client = require("opencode.cli.client")
client.tui_execute_command("session.new", M.state.port, function()
-- Session ID will be set via SSE event
vim.notify("New session started", vim.log.levels.INFO, { title = "opencode" })
end)

-- Add a timeout to reset the pending flag if session creation fails
vim.defer_fn(function()
if M.state and M.state.session_creation_pending then
M.state.session_creation_pending = false
-- Only notify if session still hasn't been created
if not M.state.session_id then
local port_info = M.state.port and (" (port: " .. M.state.port .. ")") or ""
local message = table.concat({
"Session creation timed out" .. port_info .. ".",
"The opencode server may not be responding. Try:",
"1. Close this chat and restart opencode server",
"2. Press 'n' to create a new session",
"3. Check if multiple opencode processes are running",
}, "\n")
vim.notify(message, vim.log.levels.WARN, { title = "opencode" })
end
end
end, 10000)
end

---Interrupt the current session
Expand Down Expand Up @@ -400,6 +447,7 @@ end
function M.set_session_id(session_id)
if M.state then
M.state.session_id = session_id
M.state.session_creation_pending = false
end
end

Expand Down
9 changes: 2 additions & 7 deletions lua/opencode/ui/chat_events.lua
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,8 @@ function M.subscribe(port)
local session = event.properties and event.properties.session
if session and session.id then
chat.set_session_id(session.id)
-- Add a system message to indicate new session
chat.add_message({
role = "system",
text = "Session started: " .. session.id,
streaming = false,
complete = true,
})
-- Notify that session is ready
vim.notify("Session ready", vim.log.levels.INFO, { title = "opencode" })
end
elseif event.type == "session.idle" then
-- Session finished responding
Expand Down
18 changes: 10 additions & 8 deletions lua/opencode/ui/chat_init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,7 @@ function M.start_chat(opts)
-- Subscribe to events first
require("opencode.ui.chat_events").subscribe(port)

-- Create new session via TUI command
local client = require("opencode.cli.client")
client.tui_execute_command("session.new", port, function()
-- Session will be set via SSE event
end)

-- Show welcome message
-- Show welcome message with instructions
vim.schedule(function()
if chat.get_state() then
local config = require("opencode.config").opts.chat or {}
Expand All @@ -49,7 +43,8 @@ function M.start_chat(opts)
chat.add_message({
role = "assistant",
text = string.format(
"Chat session starting... Type '%s' to send a message.\n\nKeybindings:\n %s - Send message\n %s - New session\n %s - Close\n %s - Yank message\n %s - Interrupt",
"Welcome to OpenCode Chat!\nConnected to opencode server on port %d\n\nType '%s' to send a message. A session will be created if needed.\n\nKeybindings:\n %s - Send message\n %s - New session\n %s - Close\n %s - Yank message\n %s - Interrupt",
port,
send_keys,
send_keys,
new_session_key,
Expand All @@ -62,6 +57,13 @@ function M.start_chat(opts)
})
end
end)

-- Try to create initial session, but don't block if it fails
local client = require("opencode.cli.client")
client.tui_execute_command("session.new", port, function()
-- Session will be set via SSE event
-- If this fails, session will be created on-demand when user sends first message
end)
end)
:catch(function(err)
vim.notify("Failed to start opencode: " .. err, vim.log.levels.ERROR, { title = "opencode" })
Expand Down