Skip to content

Commit a53dad9

Browse files
feat: Use per sessions user messages
1 parent a9c5f4d commit a53dad9

File tree

3 files changed

+83
-22
lines changed

3 files changed

+83
-22
lines changed

lua/opencode/core.lua

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -147,16 +147,24 @@ function M.send_message(prompt, opts)
147147
params.parts = context.format_message(prompt, opts.context)
148148
M.before_run(opts)
149149

150-
state.user_message_count = state.user_message_count + 1
150+
-- Capture the session ID to ensure we track the message count for the correct session
151+
local session_id = state.active_session.id
152+
local sent_message_count = vim.deepcopy(state.user_message_count)
153+
sent_message_count[session_id] = (sent_message_count[session_id] or 0) + 1
154+
state.user_message_count = sent_message_count
155+
151156
state.api_client
152-
:create_message(state.active_session.id, params)
157+
:create_message(session_id, params)
153158
:and_then(function(response)
154159
if not response or not response.info or not response.parts then
155160
-- fall back to full render. incremental render is handled
156161
-- event manager
157162
ui.render_output()
158163
end
159-
state.user_message_count = state.user_message_count - 1
164+
165+
local received_message_count = vim.deepcopy(state.user_message_count)
166+
received_message_count[response.info.sessionID] = (received_message_count[response.info.sessionID] ~= nil) and (received_message_count[response.info.sessionID] - 1) or 0
167+
state.user_message_count = received_message_count
160168

161169
M.after_run(prompt)
162170
end)
@@ -369,17 +377,26 @@ function M.initialize_current_model()
369377
return state.current_model
370378
end
371379

372-
local function on_user_message_count_change(_, new, old)
373-
local done_thinking = new == 0 and old > 0
374-
if config.hooks and config.hooks.on_done_thinking and done_thinking then
375-
pcall(config.hooks.on_done_thinking, state.active_session)
380+
function M._on_user_message_count_change(_, new, old)
381+
if config.hooks and config.hooks.on_done_thinking then
382+
local all_sessions = session.get_all_workspace_sessions() or {}
383+
local done_sessions = vim.tbl_filter(function(s)
384+
local msg_count = new[s.id] or 0
385+
local old_msg_count = (old and old[s.id]) or 0
386+
return msg_count == 0 and old_msg_count > 0
387+
end, all_sessions)
388+
389+
for _, done_session in ipairs(done_sessions) do
390+
pcall(config.hooks.on_done_thinking, done_session)
391+
end
376392
end
377393
end
378394

379-
local function on_current_permission_change(_, new, old)
395+
function M._on_current_permission_change(_, new, old)
380396
local permission_requested = old == nil and new ~= nil
381397
if config.hooks and config.hooks.on_permission_requested and permission_requested then
382-
pcall(config.hooks.on_permission_requested, state.active_session)
398+
local local_session = session.get_by_id(state.active_session.id) or {}
399+
pcall(config.hooks.on_permission_requested, local_session)
383400
end
384401
end
385402

@@ -391,8 +408,8 @@ end
391408

392409
function M.setup()
393410
state.subscribe('opencode_server', on_opencode_server)
394-
state.subscribe('user_message_count', on_user_message_count_change)
395-
state.subscribe('current_permission', on_current_permission_change)
411+
state.subscribe('user_message_count', M._on_user_message_count_change)
412+
state.subscribe('current_permission', M._on_current_permission_change)
396413

397414
vim.schedule(function()
398415
M.opencode_ok()

lua/opencode/state.lua

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
---@field cost number
3333
---@field tokens_count number
3434
---@field job_count number
35-
---@field user_message_count number
35+
---@field user_message_count table<string, number>
3636
---@field opencode_server OpencodeServer|nil
3737
---@field api_client OpencodeApiClient
3838
---@field event_manager EventManager|nil
@@ -81,7 +81,7 @@ local _state = {
8181
tokens_count = 0,
8282
-- job
8383
job_count = 0,
84-
user_message_count = 0,
84+
user_message_count = {},
8585
opencode_server = nil,
8686
api_client = nil,
8787
event_manager = nil,

tests/unit/hooks_spec.lua

Lines changed: 53 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
local renderer = require('opencode.ui.renderer')
22
local config = require('opencode.config')
33
local state = require('opencode.state')
4+
local core = require('opencode.core')
45
local helpers = require('tests.helpers')
56
local ui = require('opencode.ui.ui')
67

@@ -115,23 +116,44 @@ describe('hooks', function()
115116
describe('on_done_thinking', function()
116117
it('should call hook when thinking is done', function()
117118
local called = false
119+
local called_session = nil
118120

119-
config.hooks.on_done_thinking = function()
121+
config.hooks.on_done_thinking = function(session)
120122
called = true
123+
called_session = session
124+
end
125+
126+
-- Mock session.get_all_workspace_sessions to return our test session
127+
local session_module = require('opencode.session')
128+
local original_get_all = session_module.get_all_workspace_sessions
129+
session_module.get_all_workspace_sessions = function()
130+
return { { id = 'test-session', title = 'Test' } }
121131
end
122132

123-
-- Simulate job count change from 1 to 0 (done thinking)
124-
state.user_message_count = 1
125-
state.user_message_count = 0
133+
state.subscribe('user_message_count', core._on_user_message_count_change)
134+
135+
-- Simulate job count change from 1 to 0 (done thinking) for a specific session
136+
state.active_session = { id = 'test-session', title = 'Test' }
137+
state.user_message_count = { ['test-session'] = 1 }
138+
state.user_message_count = { ['test-session'] = 0 }
139+
140+
-- Wait for async notification
141+
vim.wait(100, function() return called end)
142+
143+
-- Restore original function
144+
session_module.get_all_workspace_sessions = original_get_all
145+
state.unsubscribe('user_message_count', core._on_user_message_count_change)
126146

127147
assert.is_true(called)
148+
assert.are.equal(called_session.id, 'test-session')
128149
end)
129150

130151
it('should not error when hook is nil', function()
131152
config.hooks.on_done_thinking = nil
132-
state.user_message_count = 1
153+
state.active_session = { id = 'test-session', title = 'Test' }
154+
state.user_message_count = { ['test-session'] = 1 }
133155
assert.has_no.errors(function()
134-
state.user_message_count = 0
156+
state.user_message_count = { ['test-session'] = 0 }
135157
end)
136158
end)
137159

@@ -140,26 +162,48 @@ describe('hooks', function()
140162
error('test error')
141163
end
142164

143-
state.user_message_count = 1
165+
state.active_session = { id = 'test-session', title = 'Test' }
166+
state.user_message_count = { ['test-session'] = 1 }
144167
assert.has_no.errors(function()
145-
state.user_message_count = 0
168+
state.user_message_count = { ['test-session'] = 0 }
146169
end)
147170
end)
148171
end)
149172

150173
describe('on_permission_requested', function()
151174
it('should call hook when permission is requested', function()
152175
local called = false
176+
local called_session = nil
153177

154-
config.hooks.on_permission_requested = function()
178+
config.hooks.on_permission_requested = function(session)
155179
called = true
180+
called_session = session
181+
end
182+
183+
-- Mock session.get_by_id to return our test session
184+
local session_module = require('opencode.session')
185+
local original_get_by_id = session_module.get_by_id
186+
session_module.get_by_id = function(id)
187+
return { id = id, title = 'Test' }
156188
end
157189

190+
-- Set up the subscription manually
191+
state.subscribe('current_permission', core._on_current_permission_change)
192+
158193
-- Simulate permission change from nil to a value
194+
state.active_session = { id = 'test-session', title = 'Test' }
159195
state.current_permission = nil
160196
state.current_permission = { tool = 'test_tool', action = 'read' }
161197

198+
-- Wait for async notification
199+
vim.wait(100, function() return called end)
200+
201+
-- Restore original function
202+
session_module.get_by_id = original_get_by_id
203+
state.unsubscribe('current_permission', core._on_current_permission_change)
204+
162205
assert.is_true(called)
206+
assert.are.equal(called_session.id, 'test-session')
163207
end)
164208

165209
it('should not error when hook is nil', function()

0 commit comments

Comments
 (0)