Repository navigation
Python: add AsyncRequestBroker and shell command audit log - #9166
Mark Licata (marklicata) wants to merge 6 commits into
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟡 Changes recommended
Confirmed broker lifecycle and audit-history integrity issues remain unresolved.
3 open findings
What changed in this PR
Adds Python foundations for asynchronous client responses and shell-command auditing. Approval-pipeline integration remains deferred.
Changes:
- Introduces and exports experimental
AsyncRequestBroker. - Records shell policy decisions and execution exit codes.
- Adds broker and shell-audit tests.
| File | Description |
|---|---|
python/packages/tools/tests/test_local_shell_tool.py |
Tests command audit records. |
python/packages/tools/agent_framework_tools/shell/_tool.py |
Records policy decisions and execution outcomes. |
python/packages/tools/agent_framework_tools/shell/_audit.py |
Defines audit records and their in-memory log. |
python/packages/tools/agent_framework_tools/shell/__init__.py |
Exports audit types. |
python/packages/core/tests/core/test_request_broker.py |
Tests broker responses, timeouts, and duplicate IDs. |
python/packages/core/agent_framework/_harness/_request_broker.py |
Implements asynchronous request coordination. |
python/packages/core/agent_framework/__init__.pyi |
Adds typed broker exports. |
python/packages/core/agent_framework/__init__.py |
Adds runtime broker exports. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
…snapshots Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Eduard van Valkenburg (eavanvalkenburg)
left a comment
There was a problem hiding this comment.
Mark Licata (@marklicata) The language split addresses the scope concern from #8995, but the user-facing use case for AsyncRequestBroker is still not clear from this PR. Can we add a small sample showing a server request being awaited and a client resolving or rejecting it?
…l audit Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Eduard van Valkenburg (@eavanvalkenburg) Added a sample in e5cd62d: |
…roker-python Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Eduard van Valkenburg (eavanvalkenburg)
left a comment
There was a problem hiding this comment.
Mark Licata (@marklicata) Looking only at the enhanced shell-tool audit logging: python/packages/tools/agent_framework_tools/shell/_tool.py:263 records the configured LocalShellTool._workdir, not the directory where the command actually executes. In persistent mode the shell can cd and retain a different cwd across calls, while audit records keep reporting the constructor workdir; with workdir=None, execution inherits a concrete process cwd but the record stores None. That makes the compliance/forensics trail inaccurate for identical relative commands.
Please record the effective execution cwd. For stateless execution, persist the concrete cwd used (os.getcwd() when omitted). For persistent execution, track/capture the shell cwd at the command boundary, or avoid claiming a concrete cwd when it can drift. Add coverage for a persistent session that changes directory before an audited command.
|
Mark Licata (@marklicata) I would like to split the request-handling example out of this PR and develop it as a standalone sample, rather than adding This follows the discussion in #8861: #8861 (comment). MAF deliberately returns control to the application for approvals and other human input, then accepts the response on a later run with the session/checkpoint. The application owns the transport and how long it waits; it should remain possible to shut the process down between those steps. The existing local-tool approval path is already fail-closed, as clarified in #8861 (comment). Please make the separate sample show more extensive application-owned request handling around that existing contract: receive a pending request, deliver it to a simulated client, correlate the reply, and resume the agent with the same session. Include approval/rejection and timeout/cancellation or late-response handling so readers can see where those policies belong. Any in-memory broker used to illustrate the transport should stay local to the sample, with its process-lifetime limitation made explicit. It should not replace the framework pause-and-resume contract or require a new public broker API. That gives developers a useful reference without committing the framework to one request-handling mechanism. |
|
Mark Licata (@marklicata) Separately, I am not yet convinced that the shell audit feature needs its own public logging abstraction rather than standard Python logging. As implemented, Neither an in-memory list nor Python logging alone provides a compliance audit trail. The current implementation has no session correlation or persistence, does not capture human approval decisions, and leaves failed or cancelled executions with My preference for a first version is structured events through standard Python logging for policy decisions and execution outcomes, with accurate working-directory, exit-code, timeout, and failure information. The application can configure collection, storage, and retention. Raw command text should be opt-in or redacted because it can contain secrets. Exceptions and cancellation must still propagate normally, and approval auditing belongs at the layer that owns approvals rather than being inferred by the shell tool. If there is a concrete need for a supported, programmatically queryable execution history beyond logging, please describe that use case before we add these public APIs. Otherwise, I would avoid the new recorder types and the compliance claims for this version. |
…th Python logging
- Remove AsyncRequestBroker from agent_framework public API and core harness;
any in-process broker belongs in the application, not the framework
- Remove ShellAuditLog and ShellCommandRecord public types; shell tool now emits
structured events through logging.getLogger('agent_framework.shell.audit') with
shell.policy and shell.execution records
- Fix effective workdir: record os.getcwd() for stateless mode when no workdir
configured, instead of None
- Fix outcome tracking: try/finally ensures exit_code and outcome are always logged
even on exception or cancellation
- Rewrite async_request_broker.py sample to show MAF pause-and-resume approval
contract with a locally-defined transport, not a framework import
- Addresses eavanvalkenburg feedback on microsoft#8861
Generated with Amplifier
Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>


Motivation & Context
Two foundations for safe autonomous agent infrastructure:
This is the Python half of the work previously proposed in #8995, split per language at the reviewers' request. The .NET counterpart (
AsyncRequestBroker<TResult>) is #9167.Description & Review Guide
What are the major changes?
AsyncRequestBroker[T](agent_framework._harness._request_broker):request_async(request_id, timeout?),resolve(request_id, result),reject(request_id, exc). A late-resolve race is handled via a bounded settled-ID deque (256 entries). Marked@experimental.ShellAuditLog/ShellCommandRecord: new moduleagent_framework_tools/shell/_audit.pyLocalShellTool.audit_log: records the policy decision on everyrun(), then updatesexit_codeafter executionWhat is the impact of these changes?
Additive. Wiring the broker into the tool approval pipeline (fail-closed
always_requireexecution) is intentionally deferred to the approval-policy work.What do you want reviewers to focus on?
resolve()returnsTrue(already settled); a secondrequest_async()for the same ID raisesValueErrorexit_codeisNonefor denied commands and when the executor raisesRelated Issue
Part of #8861, together with #9167 for .NET. The issue is closed manually once both PRs have merged.
Contribution Checklist
🤖 Generated with Claude Code