Skip to content

feat(adapters): agentic tool calling surface (CAC-02, DSE-1807) - #67

Merged
ernestprovo23 merged 2 commits into
mainfrom
feat/cac-02-agentic-tools
Sep 21, 2026
Merged

ernestprovo23 merged 2 commits into
mainfrom
feat/cac-02-agentic-tools

Conversation

@ernestprovo23

Copy link
Copy Markdown
Member

Adds an additive agentic tool-calling surface to the provider adapter protocol. Prerequisite for DSE TUI P0 (DSE-1807).

What changes

  • ToolSpec, ToolCall, AgenticResponse dataclasses in adapters/base.py.
  • Neutral content blocks (text / tool_call / tool_result) with assistant_tool_call_message() and tool_result_message() helpers; each adapter translates to its own wire shape.
  • supports_tool_calls class flag. The agent loop must check it — capability is never inferred from a model name.
  • build_request / stream_request gain tools and tool_choice; messages widened to list[dict] so content may be a block list.
  • New parse_agentic_response() — parse_response() -> tuple[str, TokenUsage|None] cannot express a tool call, so this is parallel rather than a breaking change.
  • Anthropic adapter implements the surface (tool_use / tool_result, tool_use_id carried end-to-end, stop-reason normalization, parallel calls preserved in emission order). OpenAI-compat and Gemini declare supports_tool_calls = False and raise ProviderError.
  • tools and output_contract are mutually exclusive — raises ValueError. Forced-tool structured output and agentic tool calling are different modes; parse_agentic_response never consults forced-tool state.

Additive guarantee

String-content request bodies are byte-identical to before. Asserted by test_string_content_body_is_unchanged_by_the_new_surface; the pre-existing suite required zero changes.

Verification (builder, fresh)

  • 870 passed in 37.97s — full suite
  • 28 passed in 0.08s — tests/test_agentic_tools.py
  • ruff check src/conclave tests — All checks passed
  • ruff format --check — 6 files already formatted

Release control

Classifies security-specific on accumulated keywords only: the sole trigger is redact appearing on a changed line because adding five names re-wrapped a one-line import in adapters/__init__.py. redact() is unchanged; every other file classifies routine-non-security in isolation. Receipt granted by Ernest Provo in authenticated session at head SHA d3603f7b706cf9dd2cd7445b1f96b7a101f4e7b8.

🤖 Generated with Claude Code

https://claude.ai/code/session_01H5GFPdJDb2VEuqv7X8Wn6E

@ernestprovo23
ernestprovo23 merged commit f94dc60 into main Sep 21, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant