From 8d46d2586255fc85e35f00ff8b814424b140e623 Mon Sep 17 00:00:00 2001 From: Choti Wongbussakorn <126886556+Chewji9875@users.noreply.github.com> Date: Sun, 30 Aug 2026 17:50:35 +0700 Subject: [PATCH] fix(opencode): pass input args to augment hook and allow tool search in subagents - Fix client adapter passing output?.args instead of input?.args in tool.execute.after hook (closes #1737) - Add tool_search and tool_search_regex permissions to OpenCode subagent profiles so deferred MCP tools can be discovered and unblocked - Add regression tests for OpenCode adapter hook args and subagent profile permissions Signed-off-by: Choti Wongbussakorn <126886556+Chewji9875@users.noreply.github.com> --- src/cli/agent_profiles.c | 11 +++++++++++ src/cli/client_adapter.c | 2 +- tests/test_agent_clients.c | 3 +++ tests/test_agent_profiles.c | 19 +++++++++++++++++++ 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/cli/agent_profiles.c b/src/cli/agent_profiles.c index c98289251..7ae55b82e 100644 --- a/src/cli/agent_profiles.c +++ b/src/cli/agent_profiles.c @@ -547,6 +547,17 @@ 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 ee8366e6e..48182bf7d 100644 --- a/src/cli/client_adapter.c +++ b/src/cli/client_adapter.c @@ -315,7 +315,7 @@ char *cbm_client_adapter_opencode(const char *binary_path) { " seen.add(sid);\n" " pieces.push(await lifecycle());\n" " }\n" - " const args = output?.args ?? {};\n" + " const args = input?.args ?? {};\n" " const search =\n" " input?.tool === 'grep' ? 'Grep' : input?.tool === 'glob' ? 'Glob' : null;\n" " if (search) {\n" diff --git a/tests/test_agent_clients.c b/tests/test_agent_clients.c index c2c728203..59d48b320 100644 --- a/tests/test_agent_clients.c +++ b/tests/test_agent_clients.c @@ -1283,6 +1283,9 @@ 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, "const args = input?.args ?? {};")); + ASSERT_NOT_NULL(strstr(js, "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); }