[AAASM-1928] ✨ (client): Add GatewayClient.dispatch_tool wrapper#60
Conversation
Frozen dataclass carrying the outcome of `GatewayClient.dispatch_tool` (AAASM-1920 SDK surface): * `resolved_args: dict[str, Any]` — post-substitution args ready to hand to the tool sink. Carries resolved credential values; don't log. * `names_substituted: list[str]` — placeholder names that were resolved during this call (names only, never values). Re-exported from `agent_assembly.client` alongside `GatewayClient`. Refs AAASM-1928.
Public SDK surface for AAASM-1920 Secret Injection. POSTs
`{ tool, args }` to `/dispatch_tool`, decodes the response into a
`DispatchToolResult`, and surfaces HTTP errors as `GatewayError`.
The LLM never observes the resolved credential value: the agent code
holds the placeholder `${NAME}`, Assembly resolves it gateway-side,
and the resolved args are forwarded to the tool sink only.
Refs AAASM-1928.
Four tests pinning the SDK contract:
1. success — round-trips placeholder `${DB_PASSWORD}` args; asserts
the result wraps the gateway's `resolved_args` +
`names_substituted`; asserts the body sent over the wire is the
placeholder-form (never the resolved value).
2. unknown placeholder → `GatewayError` (422 from the gateway is
surfaced as the SDK exception type).
3. network failure → `GatewayError` (`httpx.ConnectError` maps
through).
4. empty server response → well-formed empty result (defensive).
`.venv/bin/python -m pytest test/unit/client/test_dispatch_tool.py`
→ 4 passed; `ruff check` + `ruff format` clean.
Refs AAASM-1928.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
Chisanan232
left a comment
There was a problem hiding this comment.
🤖 Claude Code self-review — AAASM-1928 (ST6)
CI
- 20 success + 4 skipped checks: all green ✅ (no failures).
mergeStateStatus: CLEAN.mergeable: MERGEABLE.
Scope vs Subtask ticket
| AC | Evidence |
|---|---|
agent_assembly.client.DispatchToolResult — frozen dataclass with resolved_args + names_substituted |
agent_assembly/client/dispatch.py |
GatewayClient.dispatch_tool(tool_name, args) POSTs to /dispatch_tool and returns a DispatchToolResult |
agent_assembly/client/gateway.py:179-218 |
Wire body is placeholder-form (${NAME}); resolved value lives only in the response |
Pinned by test 1 |
Re-exported from agent_assembly.client package |
client/__init__.py |
| 4 unit tests: success / 422 unknown-placeholder / network error / defensive empty-response | test/unit/client/test_dispatch_tool.py — 4 / 4 passed locally |
.venv/bin/python -m pytest test/unit/client/test_dispatch_tool.py clean |
Yes |
.venv/bin/ruff check + .venv/bin/ruff format --check clean on new files |
Yes |
mypy --strict clean on dispatch.py |
Yes |
| Commit granularity — dataclass / method / tests in separate commits | 3 commits |
The httpx.HTTPError catch covers both 4xx (HTTPStatusError) and network failures (ConnectError) via the common parent class; the test suite proves both map to GatewayError with the tool-name-prefixed message format that matches the existing report_edge / register_agent patterns.
Verdict
Ready for merge. Independent of the agent-assembly repo PRs. No CI blockers, no semantic blockers.
Chisanan232
left a comment
There was a problem hiding this comment.
🤖 Claude Code self-review — AAASM-1928 (ST6) — final
CI
- 20 success + 4 skipped checks: all green ✅ (no failures).
mergeable: UNKNOWN — python-sdk's branch-protection rule may need a fresh check evaluation, but every actual gate passed.
Scope vs Subtask ticket
| AC | Evidence |
|---|---|
agent_assembly.client.DispatchToolResult — frozen dataclass with resolved_args + names_substituted |
agent_assembly/client/dispatch.py |
GatewayClient.dispatch_tool(tool_name, args) POSTs to /dispatch_tool and returns a DispatchToolResult |
agent_assembly/client/gateway.py:179-218 |
Wire body is placeholder-form (${NAME}); resolved value lives only in the response |
Pinned by test 1 |
Re-exported from agent_assembly.client package |
client/__init__.py |
| 4 unit tests: success / 422 unknown-placeholder / network error / defensive empty-response | test/unit/client/test_dispatch_tool.py — 4 / 4 passed |
pytest test/unit/client/test_dispatch_tool.py clean |
Yes |
ruff check + ruff format --check clean on new files |
Yes |
mypy --strict clean on dispatch.py |
Yes |
| Commit granularity — dataclass / method / tests in separate commits | 3 commits |
Independent of the agent-assembly stack — opens against python-sdk master directly with no rebases needed. Now that the agent-assembly HTTP route (AAASM-1926, merged) is in production, the SDK surface this PR ships is wired end-to-end.
Verdict
Ready for merge. No CI blockers, no semantic blockers. Pairs with the merged AAASM-1926 (HTTP route) in the agent-assembly repo.



Description
ST6 of Story AAASM-1920 (Secret Injection). SDK surface for the new
POST /api/v1/dispatch_toolroute shipped in the agent-assembly repo (AAASM-1926, #789).Adds:
agent_assembly.client.DispatchToolResult— frozen dataclass withresolved_args+names_substituted.GatewayClient.dispatch_tool(tool_name, args)— POSTs the placeholder-form args to the new gateway route and returns aDispatchToolResult. Maps HTTP errors (including 422 unknown-placeholder) and network failures toGatewayError.The SDK contract complements the gateway's audit-shape contract: the wire body always carries the placeholder-form args (
${NAME}); the resolved credential value lives only in the response, returned for the caller to forward to the tool sink.Type of Change
Breaking Changes
Related Issues
Testing
test/unit/client/test_dispatch_tool.py, 4 tests:GatewayError.httpx.ConnectError) →GatewayError..venv/bin/python -m pytest test/unit/client/test_dispatch_tool.py→ 4 passed..venv/bin/ruff check+.venv/bin/ruff format --checkclean on new files.mypy --strictclean onagent_assembly/client/dispatch.py.