diff --git a/src/cli/agent_profiles.c b/src/cli/agent_profiles.c index c98289251..3afd3781b 100644 --- a/src/cli/agent_profiles.c +++ b/src/cli/agent_profiles.c @@ -547,6 +547,16 @@ static bool render_profile_text(profile_buffer_t *buffer, cbm_graph_profile_dial } return true; case CBM_GRAPH_DIALECT_OPENCODE: + if (!profile_buffer_append(buffer, "---\ndescription: ") || + !profile_buffer_append(buffer, description) || + !profile_buffer_append( + buffer, "\nmode: subagent\npermission:\n \"*\": deny\n read: allow\n grep: " + "allow\n glob: allow\n tool_search: allow\n tool_search_regex: allow\n") || + (direct && !append_permission_mcp_tools(buffer, dialect, tier)) || + !profile_buffer_append(buffer, "---\n") || !profile_buffer_append(buffer, prompt)) { + return false; + } + return true; case CBM_GRAPH_DIALECT_KILO: if (!profile_buffer_append(buffer, "---\ndescription: ") || !profile_buffer_append(buffer, description) || diff --git a/src/cli/client_adapter.c b/src/cli/client_adapter.c index 4d136bb5e..db02d0d1b 100644 --- a/src/cli/client_adapter.c +++ b/src/cli/client_adapter.c @@ -299,7 +299,7 @@ char *cbm_client_adapter_opencode(const char *binary_path) { " const tool = input?.tool === 'grep' ? 'Grep' : input?.tool === 'glob' ? 'Glob' " ": null;\n" " if (!tool) return;\n" - " const extra = await augment(tool, output?.args);\n" + " const extra = await augment(tool, input?.args);\n" " if (extra && typeof output?.output === 'string') {\n" " output.output += '\\n' + extra;\n" " }\n" diff --git a/tests/test_agent_clients.c b/tests/test_agent_clients.c index 90ed75626..f9a4eee5a 100644 --- a/tests/test_agent_clients.c +++ b/tests/test_agent_clients.c @@ -1283,6 +1283,8 @@ TEST(client_adapter_opencode_sends_the_required_hook_event) { ASSERT_NOT_NULL(js); ASSERT_NOT_NULL(strstr(js, "hook_event_name: 'PreToolUse'")); ASSERT_NOT_NULL(strstr(js, "tool.execute.after")); + ASSERT_NOT_NULL(strstr(js, "augment(tool, input?.args)")); + ASSERT_NULL(strstr(js, "output?.args")); /* OpenCode reaches the tools over MCP already; this adapter must not * register any, or we reintroduce the second tool surface. */ ASSERT_NULL(strstr(js, "registerTool")); diff --git a/tests/test_agent_profiles.c b/tests/test_agent_profiles.c index 1d6064c05..838348736 100644 --- a/tests/test_agent_profiles.c +++ b/tests/test_agent_profiles.c @@ -360,6 +360,24 @@ TEST(agent_profiles_omp_direct_has_prefixed_tools_and_handoff_excludes_mcp) { PASS(); } +TEST(agent_profiles_opencode_allows_tool_search_and_subagent_permissions) { + char *direct = cbm_render_graph_profile(CBM_GRAPH_DIALECT_OPENCODE, CBM_GRAPH_TIER_VERIFY, + CBM_GRAPH_ACCESS_DIRECT, NULL); + ASSERT_NOT_NULL(direct); + ASSERT_NOT_NULL(strstr(direct, "mode: subagent\n")); + ASSERT_NOT_NULL(strstr(direct, " \"*\": deny\n")); + ASSERT_NOT_NULL(strstr(direct, " read: allow\n")); + ASSERT_NOT_NULL(strstr(direct, " grep: allow\n")); + ASSERT_NOT_NULL(strstr(direct, " glob: allow\n")); + ASSERT_NOT_NULL(strstr(direct, " tool_search: allow\n")); + ASSERT_NOT_NULL(strstr(direct, " tool_search_regex: allow\n")); + ASSERT_NOT_NULL(strstr(direct, " \"codebase-memory-mcp_search_graph\": allow\n")); + ASSERT_NOT_NULL(strstr(direct, " \"codebase-memory-mcp_check_index_coverage\": allow\n")); + ASSERT_NULL(strstr(direct, "delete_project")); + free(direct); + PASS(); +} + SUITE(agent_profiles) { RUN_TEST(agent_profiles_stable_tier_identity); RUN_TEST(agent_profiles_direct_dialects_are_coverage_aware_and_read_only); @@ -371,6 +389,7 @@ SUITE(agent_profiles) { RUN_TEST(agent_profiles_codex_declares_transport_and_escapes_binary_path); RUN_TEST(agent_profiles_vibe_uses_matching_prompt_identifier_and_contract); RUN_TEST(agent_profiles_omp_direct_has_prefixed_tools_and_handoff_excludes_mcp); + RUN_TEST(agent_profiles_opencode_allows_tool_search_and_subagent_permissions); RUN_TEST(agent_profiles_grok_uses_dispatcher_ids_and_named_inheritance); RUN_TEST(agent_profiles_render_deterministically_and_reject_invalid_inputs); }