Skip to content

Out-of-turn sys_call_async calls fail-closed (DENY) instead of evaluating tool-call policy #3233

Description

@andrewreid

Summary

A healthy out-of-turn sys_call_async dispatch reaches the harness policy evaluator with no bound TurnContext, so the tool-call phase has nothing to gate against and fail-closes to DENY. This is secure, but functionally breaks policy-guarded async calls: a sys_call_async to a tool covered by an ASK/DENY policy never executes.

Detail

_spawn_async_tool (omnigent/runner/tool_dispatch.py) launches the background task outside any turn context. When the inner SDK's policy callback fires for that call, ExecutorAdapter._stable_policy_evaluator (omnigent/runtime/harnesses/_executor_adapter.py, PHASE_TOOL_CALL) finds _current_ctx is None and returns the fail-closed default (DENY) — the correct safety posture for an unevaluable verdict, but it means the configured policy is never actually evaluated.

Net effect: an agent that dispatches a guarded tool via sys_call_async gets a hard DENY every time, rather than the ASK/DENY the policy intends.

Proposed fix

Carry or synthesize the originating turn's policy context into the async-dispatch path so the tool-call phase can be evaluated (ASK/DENY) for out-of-turn background calls, instead of fail-closing.

Scope note

Distinct from #522, which covers async completion delivery, not policy-context propagation. Surfaced during review of #1077 (turn-context desync recovery); intentionally deferred there as out of scope for the desync fix.

Metadata

Metadata

Assignees

Labels

BugSomething isn't workingP1-highPriority: major feature broken, no workaroundcomp:harnessesComponent: SDK harnesses (Claude, Cursor, etc.)comp:policiesComponent: safety policies, guardrailscomp:runnerComponent: agent runner, execution enginetriagedIssue has been triaged by the bot

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions