M6.6a: the call record (L7). Stacked on #178 (the spec); retarget to main after #178 merges.
What gets recorded: every model call of every agent, in <state stem>.calls.db beside its state.db.
The request exactly as sent, never headers.
The raw response, including reasoning.
The model asked and the one that answered, timing and tokens, and llama-server's cache_n.
Failed calls, with their error.
Each call's run, role and purpose. Driver runs and helper delegations are nested runs; compaction, consults, the classifier and the go-ahead reader label their calls.
For each message, its provenance: the log event ids it came from, what fitting did to it (shortened and by how much, or dropped), and what compaction replaced.
How it's built:
New nisse_core/models/calls.py: CallRecorder (writes), CallReader (read-only, for the page), and the run/purpose scope.
BudgetedClient records each call and computes provenance. Both LLM clients keep last_exchange (body, status, response; never headers).
Content is stored once, zlib-compressed and keyed by hash.
The file and its -wal/-shm are deleted at each start; the layout carries a version and never migrates.
A recording failure is logged once (calls.record_failed), then recording stops; the call itself always goes ahead.
While recording, the system prompt ends with "Your model calls are recorded for the operator's study."
Config: call_record: false turns it off, in both nisse-dev and nisse-review (default on).
Benchmark: each run records beside its own temporary state.db. scripts/benchmark.py --keep-runs DIR keeps both files for the page.
Note: the benchmark's system prompt now ends with the recording sentence, so runs from here on are not strictly comparable with earlier ones. The task hash doesn't include the prompt yet; that's a follow-up once #177 lands.
Locally, the targeted runs pass. Each new test was checked to fail when its code is broken: no dedup, no notice, wrong provenance, recorder errors escaping, the token leaking into the record, missing labels.
M6.6a: the call record (L7). Stacked on #178 (the spec); retarget to main after #178 merges.
**What gets recorded:** every model call of every agent, in `<state stem>.calls.db` beside its `state.db`.
- The request exactly as sent, never headers.
- The raw response, including reasoning.
- The model asked and the one that answered, timing and tokens, and llama-server's `cache_n`.
- Failed calls, with their error.
- Each call's run, role and purpose. Driver runs and helper delegations are nested runs; compaction, consults, the classifier and the go-ahead reader label their calls.
- For each message, its provenance: the log event ids it came from, what fitting did to it (shortened and by how much, or dropped), and what compaction replaced.
**How it's built:**
- New `nisse_core/models/calls.py`: `CallRecorder` (writes), `CallReader` (read-only, for the page), and the `run`/`purpose` scope.
- `BudgetedClient` records each call and computes provenance. Both LLM clients keep `last_exchange` (body, status, response; never headers).
- Content is stored once, zlib-compressed and keyed by hash.
- The file and its -wal/-shm are deleted at each start; the layout carries a version and never migrates.
- A recording failure is logged once (`calls.record_failed`), then recording stops; the call itself always goes ahead.
- While recording, the system prompt ends with "Your model calls are recorded for the operator's study."
- Config: `call_record: false` turns it off, in both nisse-dev and nisse-review (default on).
- Benchmark: each run records beside its own temporary state.db. `scripts/benchmark.py --keep-runs DIR` keeps both files for the page.
**Note:** the benchmark's system prompt now ends with the recording sentence, so runs from here on are not strictly comparable with earlier ones. The task hash doesn't include the prompt yet; that's a follow-up once #177 lands.
**Tests:**
- `nisse-core/tests/test_calls.py`: 14 tests.
- Agent wiring: nisse-dev `test_config` (2), `test_bench`, `test_loop_classifier`, `test_loop_labels`, `test_delegation`; nisse-review `test_review`.
- Locally, the targeted runs pass. Each new test was checked to fail when its code is broken: no dedup, no notice, wrong provenance, recorder errors escaping, the token leaking into the record, missing labels.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk
nisse-ui (X6) shows the operator each agent's work and, per item, a
walkthrough down to every model request and response, for studying how
context, compaction and memory work. It reads each agent's state store,
call record and memory folder through read-only mounts, so agent code is
unchanged and each agent stays the only writer of its state; it is
released with the agents and tested against stores their own code wrote.
The call record (L7) is new in nisse-core: every request as sent, the raw
response with its reasoning, timing, tokens, prompt reuse and each
message's provenance. It is diagnostic only, never read by the agent, and
deleted at start, so it never migrates.
Commands from the page, including a hold on a task or a whole agent, are
an open question (Part Q). The shared database first proposed in #109 is
dropped. The plan gets M6.6.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk
A run that repeats itself, or keeps getting the same correction, ends in
code as escalated instead of running to its turn cap (L2, M7.8), and the
page flags such runs (X6). While the call record is on, each system
prompt says so.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk
nisse-core records each model call of every agent into a file beside its
state store (<state>.calls.db), for the status page (L7, X6):
- the request body exactly as sent (never headers), the raw response with
its reasoning, the model asked and the one that answered, time, tokens
and llama-server's own prompt reuse (timings.cache_n);
- its work item, role, run and purpose: Driver.run and nisse-dev's
delegation loop open runs, nested; compaction, expert consults, the
classifier and the go-ahead reader label their calls;
- provenance, worked out in BudgetedClient by comparing the events at
each stage: each event sent with its log id and content as sent, which
a compaction replaced, which fitting shortened (by how much) and
dropped.
Content is stored once by hash, compressed. It is diagnostic only: the
agent never reads it, a failed write is logged once and recording stops
while the call goes on, and the file is deleted when the agent starts,
so its layout never migrates; CallReader refuses a layout it doesn't
know. While recording, the system prompt ends with one sentence saying
so. `call_record: false` in either agent's YAML turns it off.
The benchmark records each run beside that run's own state store, and
`--keep-runs DIR` keeps both for the page.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
M6.6a: the call record (L7). Stacked on #178 (the spec); retarget to main after #178 merges.
What gets recorded: every model call of every agent, in
<state stem>.calls.dbbeside itsstate.db.cache_n.How it's built:
nisse_core/models/calls.py:CallRecorder(writes),CallReader(read-only, for the page), and therun/purposescope.BudgetedClientrecords each call and computes provenance. Both LLM clients keeplast_exchange(body, status, response; never headers).calls.record_failed), then recording stops; the call itself always goes ahead.call_record: falseturns it off, in both nisse-dev and nisse-review (default on).scripts/benchmark.py --keep-runs DIRkeeps both files for the page.Note: the benchmark's system prompt now ends with the recording sentence, so runs from here on are not strictly comparable with earlier ones. The task hash doesn't include the prompt yet; that's a follow-up once #177 lands.
Tests:
nisse-core/tests/test_calls.py: 14 tests.test_config(2),test_bench,test_loop_classifier,test_loop_labels,test_delegation; nisse-reviewtest_review.🤖 Generated with Claude Code
https://claude.ai/code/session_01J5Utyfzw7gF7UhjRDEtjTk