Skip to content

Commit 23075f3

Browse files
anilkmr-a2zhaofeif
andauthored
fix(terminal-service): gracefully handle missing agent profiles in CAO store (#186)
load_agent_profile() raises FileNotFoundError for agents whose profiles only exist as .json files (e.g. ~/.kiro/agents/my-agent.json). The call in terminal_service.create_terminal() was unguarded, causing terminal creation to fail with: Failed to create terminal: Agent profile not found: my-agent This was introduced in #145 when profile loading was hoisted to a top-level call outside the allowed_tools block, but the FileNotFoundError guard was not moved with it. Wrap load_agent_profile() in try/except FileNotFoundError and gate allowed_tools resolution on profile is not None. For kiro_cli, the profile is resolved natively by kiro-cli itself — CAO not finding it in its own store is expected and non-fatal. This matches the documented behavior in docs/kiro-cli.md: If the agent is not found, CAO gracefully falls back — kiro-cli resolves .json profiles natively Co-authored-by: haofeif <56006724+haofeif@users.noreply.github.com>
1 parent d472f64 commit 23075f3

3 files changed

Lines changed: 51 additions & 16 deletions

File tree

src/cli_agent_orchestrator/services/terminal_service.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -144,20 +144,20 @@ def create_terminal(
144144
# Step 3b: Load the profile once for allowed tool resolution before
145145
# provider initialization. The skill catalog is global and does not
146146
# depend on profile contents.
147-
profile = load_agent_profile(agent_profile)
147+
try:
148+
profile = load_agent_profile(agent_profile)
149+
except FileNotFoundError:
150+
profile = None
148151
skill_prompt = build_skill_catalog()
149152

150153
# Step 3c: Resolve allowed_tools from profile if not explicitly provided
151-
if allowed_tools is None:
152-
try:
153-
from cli_agent_orchestrator.utils.tool_mapping import resolve_allowed_tools
154-
155-
mcp_server_names = list(profile.mcpServers.keys()) if profile.mcpServers else None
156-
allowed_tools = resolve_allowed_tools(
157-
profile.allowedTools, profile.role, mcp_server_names
158-
)
159-
except FileNotFoundError:
160-
pass # Profile not found; no tool restrictions
154+
if allowed_tools is None and profile is not None:
155+
from cli_agent_orchestrator.utils.tool_mapping import resolve_allowed_tools
156+
157+
mcp_server_names = list(profile.mcpServers.keys()) if profile.mcpServers else None
158+
allowed_tools = resolve_allowed_tools(
159+
profile.allowedTools, profile.role, mcp_server_names
160+
)
161161

162162
# Step 4: Create and initialize the CLI provider
163163
# This starts the agent (e.g., runs "kiro-cli chat --agent developer")

test/cli/commands/test_launch.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
from cli_agent_orchestrator.cli.commands.launch import launch
1010

1111

12-
def test_launch_includes_working_directory():
13-
"""Test that launch command includes current working directory in the params passed to subprocess."""
12+
def test_launch_passes_cwd_by_default():
13+
"""Test that launch command sends current working directory when not explicitly provided."""
1414
runner = CliRunner()
1515

1616
with (
@@ -33,9 +33,7 @@ def test_launch_includes_working_directory():
3333

3434
# Verify requests.post was called with working_directory parameter
3535
mock_post.assert_called_once()
36-
call_args = mock_post.call_args
37-
params = call_args.kwargs["params"]
38-
36+
params = mock_post.call_args.kwargs["params"]
3937
assert "working_directory" in params
4038
assert params["working_directory"] == os.path.realpath(os.getcwd())
4139

test/services/test_terminal_service_full.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,43 @@ def test_create_terminal_does_not_pass_skill_prompt_to_non_runtime_provider(
280280

281281
assert mock_provider_manager.create_provider.call_args.kwargs["skill_prompt"] is None
282282

283+
@patch("cli_agent_orchestrator.services.terminal_service.TERMINAL_LOG_DIR")
284+
@patch("cli_agent_orchestrator.services.terminal_service.provider_manager")
285+
@patch("cli_agent_orchestrator.services.terminal_service.db_create_terminal")
286+
@patch("cli_agent_orchestrator.services.terminal_service.tmux_client")
287+
@patch("cli_agent_orchestrator.services.terminal_service.generate_window_name")
288+
@patch("cli_agent_orchestrator.services.terminal_service.generate_session_name")
289+
@patch("cli_agent_orchestrator.services.terminal_service.generate_terminal_id")
290+
@patch("cli_agent_orchestrator.services.terminal_service.load_agent_profile")
291+
def test_create_terminal_profile_not_found(
292+
self,
293+
mock_load_profile,
294+
mock_gen_id,
295+
mock_gen_session,
296+
mock_gen_window,
297+
mock_tmux,
298+
mock_db_create,
299+
mock_provider_manager,
300+
mock_log_dir,
301+
):
302+
"""Terminal creation succeeds when agent profile is not in CAO store (e.g. JSON-only profiles)."""
303+
mock_gen_id.return_value = "test1234"
304+
mock_gen_session.return_value = "cao-session"
305+
mock_gen_window.return_value = "my-agent-abcd"
306+
mock_tmux.session_exists.return_value = False
307+
mock_load_profile.side_effect = FileNotFoundError("Agent profile not found: my-agent")
308+
mock_provider = MagicMock()
309+
mock_provider_manager.create_provider.return_value = mock_provider
310+
mock_log_path = MagicMock()
311+
mock_log_dir.__truediv__.return_value = mock_log_path
312+
313+
result = create_terminal("kiro_cli", "my-agent", new_session=True)
314+
315+
assert result.id == "test1234"
316+
mock_provider.initialize.assert_called_once()
317+
# allowed_tools should be None since profile was not found
318+
assert mock_provider_manager.create_provider.call_args.kwargs.get("allowed_tools") is None
319+
283320

284321
class TestGetTerminal:
285322
"""Tests for get_terminal function."""

0 commit comments

Comments
 (0)