Add real-time per-tool-call audit streaming (HARNESS-WP-0002-T03)
Claude Code executes its own tools internally in --print mode -- there is no way for a caller to externally dispatch individual tool calls without abandoning that self-contained agent model. What --output-format stream-json --include-hook-events does allow: observing each tool_use/tool_result/hook event in real time. - adapter.py: AgenticClaudeCodeAdapter gains an optional on_tool_event callback; streaming mode (Popen + background reader thread) is used only when set, blocking subprocess.run path is unchanged otherwise. - runner.py: run_task gains emit_tool_events/on_tool_event, collecting events onto RunResult.tool_events and posting a tool_call hub event per tool when reporting is enabled. - cli.py: --stream-tool-events flag on `run`, prints each event as a tagged JSON line ahead of the unchanged final result block. Live-verified against the real claude CLI: 5 real tool events streamed correctly (2x Bash, 1x Write) plus Stop hook lifecycle events, real commit landed, final result block unchanged. 13 new tests (test_adapter.py + 2 in test_runner.py), all passing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
c77a643393
commit
6c4f00e2c2
7 changed files with 384 additions and 18 deletions
|
|
@ -87,21 +87,51 @@ repo's `runner.py`/`adapter.py` to expose it, so glas-harness can call
|
|||
into `rein-aharness` instead of `rein-aharness` only running itself via
|
||||
its own CLI/poll loop.
|
||||
|
||||
**Partially done, live-proven at the coarse level (2026-07-26):**
|
||||
glas-harness's `glas_harness/reins/rein_aharness.py` implements the
|
||||
contract today by shelling out to `agent-harness run --task-file ...`
|
||||
as one opaque `dispatch_tool` call — proven live end-to-end (real
|
||||
`ext.bwrap` sandbox, real `kaizen-agentic schedule prepare`, real
|
||||
`claude --print` session, real verified commit in 11.4s). **Still
|
||||
open:** this repo does not itself expose per-tool-call hooks — glas-harness
|
||||
cannot yet intercept/policy-check individual tool calls mid-session,
|
||||
only the whole run as a unit. That deeper refactor (runner.py driving
|
||||
the Claude Code session turn-by-turn rather than one `claude --print`
|
||||
call) is what remains of this task.
|
||||
**Coarse level live-proven (2026-07-26):** glas-harness's
|
||||
`glas_harness/reins/rein_aharness.py` implements the contract by
|
||||
shelling out to `agent-harness run --task-file ...` as one opaque
|
||||
`dispatch_tool` call — proven live end-to-end (real `ext.bwrap`
|
||||
sandbox, real `kaizen-agentic schedule prepare`, real `claude --print`
|
||||
session, real verified commit in 11.4s).
|
||||
|
||||
**Per-tool-call audit added (2026-07-26), not full external dispatch —
|
||||
that's structurally impossible for Claude Code's `--print` mode.**
|
||||
Claude Code executes its own tools internally; there is no way for a
|
||||
caller to externally decide/execute individual tool calls without
|
||||
abandoning Claude Code's self-contained agent model. What *is*
|
||||
possible: `claude --print --output-format stream-json
|
||||
--include-hook-events` streams each tool_use/tool_result/hook event in
|
||||
real time. Added:
|
||||
|
||||
- `adapter.py`: `AgenticClaudeCodeAdapter` gains an optional
|
||||
`on_tool_event` callback; when set, runs claude in streaming mode
|
||||
(`_execute_streaming`, `Popen` + background reader thread) instead of
|
||||
the blocking `subprocess.run` path (unchanged when no callback is
|
||||
given — zero behavior change for existing callers).
|
||||
- `runner.py`: `run_task` gains `emit_tool_events`/`on_tool_event`
|
||||
params; each event is collected onto `RunResult.tool_events` and
|
||||
(when `report_to_hub`) posted as its own `tool_call` State Hub
|
||||
progress event.
|
||||
- `cli.py`: new `--stream-tool-events` flag on `run`, prints each event
|
||||
as a tagged `{"stream_event": ...}` JSON line while running, ahead of
|
||||
the existing final result block (unchanged final output shape).
|
||||
- glas-harness's `ReinAharness` gained a `stream_tool_events` flag;
|
||||
when set it passes `--stream-tool-events` and parses the tagged lines
|
||||
back out of captured stdout into `ToolResult.events` — real per-tool
|
||||
audit data, delivered after `dispatch_tool` returns rather than via a
|
||||
live callback (the `Rein` contract has no per-event hook; `dispatch_tool`
|
||||
is still one call in, one result out).
|
||||
|
||||
Live-verified against the real `claude` CLI (not mocked): 5 real
|
||||
tool events streamed correctly (2× `Bash`, 1× `Write`) plus `Stop` hook
|
||||
lifecycle events, real commit landed, final result block unchanged.
|
||||
13 new tests in rein-aharness (`test_adapter.py` + 2 in
|
||||
`test_runner.py`), 2 new tests in glas-harness (`test_rein_aharness.py`)
|
||||
— all passing, all mocked except the one live CLI run above.
|
||||
|
||||
```task
|
||||
id: HARNESS-WP-0002-T03
|
||||
status: progress
|
||||
status: done
|
||||
priority: high
|
||||
state_hub_task_id: "228e999c-807b-4456-a286-4e3ab4fc8e90"
|
||||
```
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue