Skip to content

Commit 4de80aa

Browse files
Ambient Code Botclaude
andcommitted
fix(runner): pass --resume flag when rebuilding adapter after repo add
When a repo is added mid-session, mark_dirty() destroys the adapter and session manager. Previously the saved CLI session ID was lost, causing Claude Code to start a fresh session without chat history. Now the bridge preserves session IDs across adapter rebuilds and passes them to build_options() via the new resume_from parameter, which sets the resume option so the CLI resumes the existing conversation. Also adds get_all_session_ids() to SessionManager to avoid accessing private _workers attribute from the bridge. Fixes: RHOAIENG-52263 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 43a5d7d commit 4de80aa

File tree

3 files changed

+30
-2
lines changed

3 files changed

+30
-2
lines changed

components/runners/ambient-runner/ag_ui_claude_sdk/adapter.py

100644100755
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,7 @@ def build_options(
410410
self,
411411
input_data: Optional[RunAgentInput] = None,
412412
thread_id: Optional[str] = None,
413+
resume_from: Optional[str] = None,
413414
) -> "ClaudeAgentOptions":
414415
"""
415416
Build ClaudeAgentOptions from stored options (object/dict/None) plus dynamic tools.
@@ -419,6 +420,8 @@ def build_options(
419420
Args:
420421
input_data: Optional RunAgentInput for extracting dynamic tools
421422
thread_id: Optional thread_id for session resumption lookup
423+
resume_from: Optional CLI session ID to resume (preserves chat history
424+
across adapter rebuilds, e.g. after a repo is added mid-session)
422425
423426
Returns:
424427
Configured ClaudeAgentOptions instance
@@ -492,6 +495,11 @@ def build_options(
492495

493496
# Remove api_key from options kwargs (handled via environment variable)
494497
merged_kwargs.pop("api_key", None)
498+
499+
# Resume from a previous CLI session (preserves chat context)
500+
if resume_from:
501+
merged_kwargs["resume"] = resume_from
502+
495503
logger.debug(f"Merged kwargs after pop: {merged_kwargs}")
496504

497505
# Apply forwarded_props as per-run overrides (before adding dynamic tools)

components/runners/ambient-runner/ambient_runner/bridges/claude/bridge.py

100644100755
Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ def __init__(self) -> None:
5858
self._stderr_lines: list[str] = []
5959
# Per-thread halt tracking to avoid race conditions on shared adapter
6060
self._halted_by_thread: dict[str, bool] = {}
61+
# Preserved session IDs across adapter rebuilds (e.g. repo additions)
62+
self._saved_session_ids: dict[str, str] = {}
6163

6264
# ------------------------------------------------------------------
6365
# PlatformBridge interface
@@ -101,7 +103,10 @@ async def run(self, input_data: RunAgentInput) -> AsyncIterator[BaseEvent]:
101103
# 4. Get or create session worker for this thread
102104
thread_id = input_data.thread_id or self._context.session_id
103105
api_key = os.getenv("ANTHROPIC_API_KEY", "")
104-
sdk_options = self._adapter.build_options(input_data, thread_id=thread_id)
106+
saved_session_id = self._saved_session_ids.pop(thread_id, None)
107+
sdk_options = self._adapter.build_options(
108+
input_data, thread_id=thread_id, resume_from=saved_session_id
109+
)
105110
worker = await self._session_manager.get_or_create(
106111
thread_id, sdk_options, api_key
107112
)
@@ -186,6 +191,9 @@ def mark_dirty(self) -> None:
186191
self._adapter = None
187192
self._halted_by_thread.clear()
188193
if self._session_manager:
194+
# Preserve session IDs so --resume works after adapter rebuild.
195+
# Must be captured synchronously before the async shutdown task runs.
196+
self._saved_session_ids.update(self._session_manager.get_all_session_ids())
189197
manager = self._session_manager
190198
self._session_manager = None
191199
_async_safe_manager_shutdown(manager)

components/runners/ambient-runner/ambient_runner/bridges/claude/session.py

100644100755
Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,11 @@ async def _run(self) -> None:
100100

101101
os.environ["ANTHROPIC_API_KEY"] = self._api_key
102102

103-
from ambient_runner.bridges.claude.mock_client import MOCK_API_KEY, MockClaudeSDKClient
103+
from ambient_runner.bridges.claude.mock_client import (
104+
MOCK_API_KEY,
105+
MockClaudeSDKClient,
106+
)
107+
104108
if self._api_key == MOCK_API_KEY:
105109
logger.info("[SessionWorker] Using MockClaudeSDKClient (replay mode)")
106110
client: Any = MockClaudeSDKClient(options=self._options)
@@ -302,6 +306,14 @@ def get_session_id(self, thread_id: str) -> Optional[str]:
302306
return worker.session_id
303307
return self._session_ids.get(thread_id)
304308

309+
def get_all_session_ids(self) -> dict[str, str]:
310+
"""Return a snapshot of all known session IDs (live workers + cached)."""
311+
result = dict(self._session_ids)
312+
for tid, worker in self._workers.items():
313+
if worker.session_id:
314+
result[tid] = worker.session_id
315+
return result
316+
305317
async def destroy(self, thread_id: str) -> None:
306318
"""Stop and remove the worker for *thread_id*.
307319

0 commit comments

Comments
 (0)