Skip to content

[AAASM-4848] 🐛 (python): Surface positional tool args to content policy + assert pydantic_ai execution hook#279

Merged
Chisanan232 merged 3 commits into
masterfrom
v0.0.1/AAASM-4848/py_low
Jul 18, 2026
Merged

[AAASM-4848] 🐛 (python): Surface positional tool args to content policy + assert pydantic_ai execution hook#279
Chisanan232 merged 3 commits into
masterfrom
v0.0.1/AAASM-4848/py_low

Conversation

@Chisanan232

@Chisanan232 Chisanan232 commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Description

Fixes the AAASM-4848 LOW cluster: two governance-visibility gaps in the Python SDK adapters. Neither is a gate bypass today (the allow/deny gate still fires on the tool name), but both narrow what argument-CONTENT policy can see or could silently disable governance on a future framework version.

  1. Positional tool args were invisible to content policy. The Haystack, CrewAI, LlamaIndex and Microsoft Agent Framework adapters built tool_args from keyword arguments only, so a positionally-invoked tool (tool.invoke("secret")) presented an empty/partial mapping to check_tool_start. A new stdlib-only shared helper (_shared/positional_args.py) folds positional args in — mirroring how the smolagents/mcp adapters already handle them — so content policy sees the values. Microsoft recovers arguments/context from the positional slots. The gate behavior is unchanged.
  2. pydantic_ai apply() could select a vestigial hook. It tried Tool._run (the <0.3.0 hook) first and only fell back to AbstractToolset.call_tool (the >=0.3.0 execution hook). Correct today because Tool._run is absent on modern versions, but a future vestigial Tool._run off the execution path would satisfy the first branch and leave call_tool ungoverned. call_tool is now selected first, with Tool._run used only as the legacy fallback.

Type of Change

  • 🔧 Bug fix

Breaking Changes

  • No

Related Issues

Testing

  • Unit tests added/updated
  • Manual testing performed

New regression tests: test/unit/adapters/test_positional_args_content_policy.py drives each of the 4 adapters' real governance wrappers with a positional secret and asserts a deny-on-content policy both sees the secret and blocks the tool body. test/unit/adapters/pydantic_ai/test_apply_hook_selection.py exercises apply()'s hook selection directly (not the _apply_* helpers in isolation), asserting call_tool wins over a vestigial Tool._run and that Tool._run is still used as the legacy fallback.

Validation: full suite pytest test/ — 1180 passed, 16 skipped. ruff check + black clean on changed files. mypy agent_assembly clean (with --ignore-missing-imports, as the pre-commit hook runs it).

Checklist

  • Code follows project style guidelines
  • Self-review completed
  • Comments added for complex logic
  • Documentation updated if needed
  • All tests passing

🤖 Generated with Claude Code

Haystack, CrewAI, LlamaIndex and Microsoft Agent Framework built their
governance-visible tool_args from keyword arguments only, so a positionally
invoked tool presented an empty/partial mapping to check_tool_start —
argument-CONTENT policy went blind to the positional values (the allow/deny
gate still fired on the tool name). Add a stdlib-only shared helper that folds
positional args into tool_args (mirroring smolagents/mcp) and use it in the
four adapters; Microsoft recovers arguments/context from the positional slots.

Refs AAASM-4848
apply() tried Tool._run (the <0.3.0 hook) first and only fell back to
AbstractToolset.call_tool (the >=0.3.0 execution hook). That is correct today
because Tool._run is absent on modern versions, but a future vestigial
Tool._run — present yet off the execution path — would satisfy the first
branch, leave call_tool unpatched, and make governance a dead method. Select
call_tool first, using Tool._run only as the legacy fallback when no toolset
hook point exists, and count a concrete-toolset patch as a successful hook.
Add unit tests that exercise apply()'s hook selection directly.

Refs AAASM-4848
@codecov

codecov Bot commented Jul 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 92.10526% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
agent_assembly/adapters/_shared/positional_args.py 81.81% 2 Missing ⚠️
...sembly/adapters/microsoft_agent_framework/patch.py 90.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@Chisanan232

Copy link
Copy Markdown
Contributor Author

🤖 Claude Code — PR review

Scope (AAASM-4848, LOW): ✅ positional tool args now folded into governance-visible tool_args for 4 adapters (content policy sees them); pydantic_ai prefers the real call_tool execution hook over a vestigial Tool._run.

  1. CI ✅ green (19); 1180 pass. 2. Scope ✅ 4 adapters + shared helper + pydantic + tests. 3. Side effects none; the allow/deny gate was already firing — this only adds arg visibility. 4. FE n/a. Verdict: ready to merge pending Pioneer approval.

…xity

S3776: patched_invoke's cognitive complexity was 16 (>15). Pull the
positional-vs-keyword argument recovery into _recover_invoke_call_args so
the wrapper reads as a flat sequence of steps.

S5778: bind the FakeFunctionTool instance before the pytest.raises block so
only the invoke() call — the sole statement expected to raise — sits inside
it (AAASM-4848).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@Chisanan232
Chisanan232 merged commit d6f7b5b into master Jul 18, 2026
26 checks passed
@Chisanan232
Chisanan232 deleted the v0.0.1/AAASM-4848/py_low branch July 18, 2026 09:33
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