Skip to content

Commit 29a929f

Browse files
authored
fix: follow up on the load session by id (#205)
1 parent 388cb49 commit 29a929f

6 files changed

Lines changed: 116 additions & 83 deletions

File tree

README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -596,14 +596,16 @@ require("agentic").add_files_to_context({
596596
```
597597

598598
`restore_session_by_id(session_id)` accepts a **session_id** argument with the
599-
ID of the session you want to restore.
599+
ID of the session you want to restore. Session IDs come from your provider's
600+
session storage (e.g. provider logs, project metadata, or scripts that track
601+
them). Unlike `restore_session()`, this does not require the provider to
602+
support listing sessions.
600603

601604
```lua
602605
-- Restore a session by ID
603606
require("agentic").restore_session_by_id("58e5cf8a-1277-4e43-bc29-10c1246a2c66")
604607
```
605608

606-
607609
### Built-in Keybindings
608610

609611
These keybindings are automatically set in Agentic buffers:
@@ -794,6 +796,11 @@ Call `require("agentic").restore_session()` to:
794796
(including sessions started in the terminal)
795797
2. Select a session to restore the full conversation history
796798

799+
If you know the session ID, call
800+
`require("agentic").restore_session_by_id(session_id)` to restore a specific
801+
session directly. This skips listing sessions, so it also works with providers
802+
that don't support session listing.
803+
797804
**Conflict handling:**
798805

799806
If you try to restore a session when the current tab already has an active

doc/agentic.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ agentic.restore_session()
440440

441441
*agentic.restore_session_by_id()*
442442
agentic.restore_session_by_id({session_id})
443-
Restore a previous session by it's ID.
443+
Restore a previous session by its ID.
444444

445445
Parameters: ~
446446
{session_id} (string) Session ID to restore.

doc/tags

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ agentic.new_session_with_provider() agentic.txt /*agentic.new_session_with_provi
6464
agentic.nvim agentic.txt /*agentic.nvim*
6565
agentic.open() agentic.txt /*agentic.open()*
6666
agentic.restore_session() agentic.txt /*agentic.restore_session()*
67+
agentic.restore_session_by_id() agentic.txt /*agentic.restore_session_by_id()*
6768
agentic.rotate_layout() agentic.txt /*agentic.rotate_layout()*
6869
agentic.setup() agentic.txt /*agentic.setup()*
6970
agentic.stop_generation() agentic.txt /*agentic.stop_generation()*

lua/agentic/init.lua

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -315,8 +315,8 @@ function Agentic.restore_session()
315315
end)
316316
end
317317

318-
-- restore a session by it's ID.
319-
-- @param session_id string: the ID of the session to restore.
318+
--- Restore a session by its ID.
319+
--- @param session_id string
320320
function Agentic.restore_session_by_id(session_id)
321321
SessionRegistry.get_session_for_tab_page(nil, function(session)
322322
SessionRestore.restore_by_id(session, session_id)

lua/agentic/session_restore.lua

Lines changed: 39 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -32,46 +32,6 @@ local function with_conflict_check(current_session, on_restore)
3232
end
3333
end
3434

35-
--- Show ACP session picker
36-
--- @param sessions agentic.acp.SessionInfo[]
37-
--- @param current_session agentic.SessionManager
38-
local function show_acp_picker(sessions, current_session)
39-
local items = {}
40-
for _, s in ipairs(sessions) do
41-
local date = s.updatedAt and s.updatedAt:sub(1, 16):gsub("T", " ")
42-
or "unknown date"
43-
local title = s.title or "(no title)"
44-
table.insert(items, {
45-
display = string.format("%s - %s", date, title),
46-
session_id = s.sessionId,
47-
title = s.title,
48-
updated_at = date,
49-
})
50-
end
51-
52-
vim.schedule(function()
53-
vim.ui.select(items, {
54-
prompt = "Select session to restore:",
55-
format_item = function(item)
56-
return item.display
57-
end,
58-
}, function(choice)
59-
if not choice then
60-
return
61-
end
62-
63-
with_conflict_check(current_session, function()
64-
current_session:load_acp_session(
65-
choice.session_id,
66-
choice.title,
67-
choice.updated_at
68-
)
69-
current_session.widget:show()
70-
end)
71-
end)
72-
end)
73-
end
74-
7535
--- Show session picker and restore selected session
7636
--- @param current_session agentic.SessionManager
7737
function SessionRestore.show_picker(current_session)
@@ -93,7 +53,41 @@ function SessionRestore.show_picker(current_session)
9353
return
9454
end
9555

96-
show_acp_picker(sessions, current_session)
56+
local items = {}
57+
for _, s in ipairs(sessions) do
58+
local date = s.updatedAt
59+
and s.updatedAt:sub(1, 16):gsub("T", " ")
60+
or "unknown date"
61+
local title = s.title or "(no title)"
62+
table.insert(items, {
63+
display = string.format("%s - %s", date, title),
64+
session_id = s.sessionId,
65+
title = s.title,
66+
updated_at = date,
67+
})
68+
end
69+
70+
vim.schedule(function()
71+
vim.ui.select(items, {
72+
prompt = "Select session to restore:",
73+
format_item = function(item)
74+
return item.display
75+
end,
76+
}, function(choice)
77+
if not choice then
78+
return
79+
end
80+
81+
with_conflict_check(current_session, function()
82+
current_session:load_acp_session(
83+
choice.session_id,
84+
choice.title,
85+
choice.updated_at
86+
)
87+
current_session.widget:show()
88+
end)
89+
end)
90+
end)
9791
end)
9892
end)
9993
end
@@ -102,44 +96,11 @@ end
10296
--- @param current_session agentic.SessionManager
10397
--- @param session_id string
10498
function SessionRestore.restore_by_id(current_session, session_id)
105-
local cwd = vim.fn.getcwd()
10699
current_session.agent:when_ready(function()
107-
current_session.agent:list_sessions(cwd, function(result, err)
108-
if err or not result then
109-
Logger.notify(
110-
"Failed to list sessions: "
111-
.. (err and err.message or "unknown error"),
112-
vim.log.levels.WARN
113-
)
114-
return
115-
end
116-
117-
local match = nil
118-
for _, s in ipairs(result.sessions or {}) do
119-
if s.sessionId == session_id then
120-
match = s
121-
break
122-
end
123-
end
124-
125-
if not match then
126-
Logger.notify(
127-
"Session not found: " .. session_id,
128-
vim.log.levels.WARN
129-
)
130-
return
131-
end
132-
133-
local title = match.title or "(no title)"
134-
local date = match.updatedAt
135-
and match.updatedAt:sub(1, 16):gsub("T", " ")
136-
or "unknown date"
137-
138-
vim.schedule(function()
139-
with_conflict_check(current_session, function()
140-
current_session:load_acp_session(session_id, title, date)
141-
current_session.widget:show()
142-
end)
100+
vim.schedule(function()
101+
with_conflict_check(current_session, function()
102+
current_session:load_acp_session(session_id, nil, nil)
103+
current_session.widget:show()
143104
end)
144105
end)
145106
end)

lua/agentic/session_restore.test.lua

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,70 @@ describe("SessionRestore", function()
129129
end)
130130
end)
131131

132+
describe("restore_by_id", function()
133+
it(
134+
"calls load_acp_session with the given id without listing sessions",
135+
function()
136+
local session = create_mock_session()
137+
138+
SessionRestore.restore_by_id(
139+
session --[[@as agentic.SessionManager]],
140+
"abc-123"
141+
)
142+
143+
assert.spy(session.agent.list_sessions).was.called(0)
144+
assert.spy(session.load_acp_session).was.called(1)
145+
local call_args = session.load_acp_session.calls[1]
146+
assert.equal("abc-123", call_args[2])
147+
assert.is_nil(call_args[3])
148+
assert.is_nil(call_args[4])
149+
assert.spy(session.widget.show).was.called(1)
150+
end
151+
)
152+
153+
it("prompts on conflict and only restores on confirm", function()
154+
local session = create_mock_session({
155+
chat_history = { messages = { { type = "user" } } },
156+
session_id = "existing-session",
157+
})
158+
159+
SessionRestore.restore_by_id(
160+
session --[[@as agentic.SessionManager]],
161+
"abc-123"
162+
)
163+
164+
assert.spy(vim_ui_select_stub).was.called(1)
165+
166+
local conflict_callback = vim_ui_select_stub.calls[1][3]
167+
conflict_callback("Clear current session and restore")
168+
169+
assert.spy(session.load_acp_session).was.called(1)
170+
local call_args = session.load_acp_session.calls[1]
171+
assert.equal("abc-123", call_args[2])
172+
assert.spy(session.widget.show).was.called(1)
173+
end)
174+
175+
it("does not restore when conflict prompt is cancelled", function()
176+
local session = create_mock_session({
177+
chat_history = { messages = { { type = "user" } } },
178+
session_id = "existing-session",
179+
})
180+
181+
SessionRestore.restore_by_id(
182+
session --[[@as agentic.SessionManager]],
183+
"abc-123"
184+
)
185+
186+
assert.spy(vim_ui_select_stub).was.called(1)
187+
188+
local conflict_callback = vim_ui_select_stub.calls[1][3]
189+
conflict_callback("Cancel")
190+
191+
assert.spy(session.load_acp_session).was.called(0)
192+
assert.spy(session.widget.show).was.called(0)
193+
end)
194+
end)
195+
132196
describe("show_picker with ACP session list", function()
133197
local acp_sessions = {
134198
{

0 commit comments

Comments
 (0)