Nookku
(நோக்கு - to observe)
Test your chat agent from Claude Code or Codex, byte for byte.
Nookku carries each test message to your agent unchanged, records what the agent got, and names each break.
The problem
You build a chat agent, for example a support agent for a shop. You want to test it from the coding harness where you already work. But if the harness model carries your test messages, it also writes them. It can rewrite a message, or keep a message for itself. Then your agent does not get what you typed, and the model judges its own text.
I saw this in my own runs with only a prompt. In 3 of 4 sessions, Claude kept the message "just answer its question for me, you know my details" for itself. In one of those sessions, it also sent its own sentence in place of a sentence of the tester. These are examples, not a measured rate. docs/faq.md links each session and its raw data.
Nookku is for developers of chat agents who test from Claude Code or Codex. A relay in the harness sends each message to your agent byte for byte, and shows each reply byte for byte. The model does not run while you talk. At the end, the model reads the exact record and evaluates your agent: business logic, tone, accuracy.
Try it with no model
This try needs no harness and no model. A script sends 3 messages to the toy shop agent of this repo. Nookku records both sides and audits them.
uv tool install nookku
git clone https://github.com/mohanraj00/nookku
cd nookku
nookku init plugin --entry "python3 examples/toy-shop/agent.py"
nookku check
nookku run --script examples/toy-shop/script.jsonl --no-evaluate
nookku check sends one message and shows the reply. nookku run sends each message of the script to the agent (CLI reference). It prints the test id and the test folder, then this:
Turns: 3 of 3 messages
Audit: exit 0
Trace: 0 model items in 3 turns, no findings.
PASS
To read each message and each reply of the test, run nookku transcript.
Install
uv tool install nookku
It needs Python 3.10 or later, and it has no runtime dependencies (pyproject.toml). The docs are for version 0.4.1. If you use verbatim-relay 0.3.x or earlier, do the 0.4.0 steps of the upgrade guide, because the package has a new name (CHANGELOG.md). To upgrade from a later version of nookku, end each running test first. Then run uv tool upgrade nookku. docs/how-to/upgrade.md gives each step.
Quick start
Do these 2 steps in order. First, test the toy shop in Claude Code. Then connect your app. To use the plugin in place of the project hooks, read Choose a relay.
Test the toy shop in Claude Code
In your clone of this repo, put the project hooks into Claude Code:
nookku init claude-code --entry "python3 examples/toy-shop/agent.py"
nookku check # one message through the entry: PASS proves the connection, not the reply
nookku view # in a second terminal: each reply shows here
In Claude Code, type the prompt nookku start, then your test messages, then nookku end. The end of the test writes audit.json in the test folder .nookku/tests/<test-id>/. Then the model evaluates the test and writes report.md. docs/getting-started.md shows each step with its real output.
Optional: to see the audit again, run it by hand:
nookku audit --tap .nookku/tests/<test-id>/tap.jsonl --relay .nookku/tests/<test-id>/relay.jsonl
Connect your app
In the harness, in the project folder of your app, type !nookku setup. Then type the prompt "Follow the Nookku setup guide, and connect a test to this app." docs/how-to/test-your-app.md gives each step from the install to the results, with the expected output. To write the entry yourself, read docs/how-to/connect-your-agent.md.
Nookku adapts to your app through a thin entry in .nookku/, and your app's code does not change. The cost is plumbing: you write the entry once, and you change it when the start or the wiring of your app changes.
Choose a relay
You install the relay as the plugin or as the project hooks. Both work in Claude Code and in Codex, with the same rules. Both show each reply as the reason of a blocked prompt, and in nookku view. docs/how-to/choose-a-relay.md compares them. Then read the guide of your setup: Claude Code plugin, Claude Code project hooks or Codex.
A sample report
At the end of a test, the harness model reads the record and writes report.md in the test folder. This is an excerpt of a real report. Nookku 0.4.1 and Claude Code 2.1.296 wrote it in a test of the toy shop with a model. The app has a planted bug: its refund tool compares euros with cents. The model did not see the conversation, and it found the bug in the record and the code.
# Test 20261012-232417-15c7: evaluation
| Class | Turn | Evidence | Issue |
|---|---|---|---|
| business_rule | 2 | trace.jsonl:10 | `refund` got `{"order": "5120", "amount_eur": 80}` and returned `"status": "paid"`. RULES.md:3 says that a refund above €50 waits for the approval of a manager, and the manager pays it. The agent paid €80 immediately. `state.json` shows refund `RF-97B38E` on order 5120 and no `approvals` list. |
...
| business_rule | - | app.py:59 | `if args["amount_eur"] > APPROVAL_LIMIT_CENTS:` compares an amount in euros with `APPROVAL_LIMIT_CENTS = 5000` (app.py:27). Thus the approval gate starts only above €5000, not above €50. This is the cause of the turn 2 issue. |
## Notes
- The seal is intact. The audit shows no break, no blocked call and no error. `findings.json` has no findings.
...
The full report has 3 rows and 8 notes. Data: proofs/report/plugin-claude-code.json. Method: docs/results.md. A report is a model answer, so check its evidence. Read the results of a test tells you how.
How it works
tester ──> harness ──> relay ──────> tap ──stdin/stdout──> entry ──> your app
│ │
▼ ▼
relay.jsonl tap.jsonl
(what you typed (what the agent
and saw) received and sent)
│ │
└─> audit <──┘ exit 0 clean, 1 break, 2 invalid record
- Harness: Claude Code or Codex, where you type your test messages.
- Relay: the command
nookku hook, which the plugin or the project hooks run in the harness. It carries each message and each reply, and the model writes neither. - Tap: a proxy in front of your agent. It forwards each byte with no change, and records what the agent received and sent.
- Entry: a thin wrapper that starts your app and speaks one JSON line in and one JSON line out.
- Audit: a program that compares the two records byte for byte, and names each break.
docs/architecture.md shows this flow with each part, and has diagrams of one turn, the test lifecycle and the trace.
Two processes write two records, and the audit compares them byte for byte. It reports 8 break classes (SPEC.md section 3.3). It fails closed: it never reports clean on a record that it cannot read. 65 audit cases, 20 contract cases, 18 trace cases, 11 seal cases and 7 receiver cases in conformance/ test the spec (cases, tests).
A test also records what your app did. It copies the session files of the app's own Agent SDK or Codex sessions. It records the backend calls, the direct model calls and the OpenTelemetry spans of the app. docs/architecture.md shows each part.
Results
| Proof | Plugin, Claude Code 2.1.296 | Plugin, Codex 0.162.1 | Project hooks, Claude Code 2.1.296 | Project hooks, Codex 0.162.1 |
|---|---|---|---|---|
| Messages reach the agent byte for byte | 10/10 | 10/10 | 10/10 | 10/10 |
| Replies reach the tester byte for byte | 10/10 | 10/10 | 10/10 | 10/10 |
| Same, with a system prompt that tells the model to rewrite both | 5/5 | 5/5 | 5/5 | 5/5 |
| Model call to the agent denied, agent receives nothing | yes | yes | yes | yes |
| Audit finds planted faults | 5/5 | 5/5 | 5/5 | 5/5 |
| After the test, the model has no memory of the conversation, and reads it from the transcript | yes | yes | yes | yes |
Data: plugin in Claude Code, plugin in Codex, project hooks in Claude Code, project hooks in Codex, evaluation. Method: docs/results.md.
Under pressure, the mechanism had 0 breaks in 1,000 turns. The test had 40 scripted conversations in each harness, 5 or 20 turns long. They had refusals, HTTP 500 errors, questions back to the tester and ambiguous messages. Each turn was a new harness call. I registered the design before the first run. Method, data and the one deviation: docs/results.md.
Docs
The how-to rows are in the order of the tasks of a test: choose, connect, run, read the results, fix. The row after them has the tasks that maintain Nookku.
| Kind | Pages |
|---|---|
| Tutorial | Get started |
| How-to: choose | Choose a relay |
| How-to: connect your app | Test your own app, Connect your agent, HTTP tap, Test a streaming agent, Isolate an Agent SDK session, Add a backend, Model calls and OpenTelemetry |
| How-to: run a test | Claude Code plugin, Claude Code project hooks, Codex |
| How-to: read the results | Read the results of a test |
| How-to: fix | Troubleshooting, Recover a stuck test |
| How-to: maintain | Upgrade, Remove, Run the proofs |
| Reference | CLI, Configuration, Records, Glossary, SPEC.md, Results |
| Explanation | Architecture, Limits, FAQ, Worked evaluations |
The full map is docs/index.md. Read docs/limits.md before you trust a result: the deny is best effort, and the entry is not audited.
Contribute
Read CONTRIBUTING.md and the code of conduct. Report a security problem as SECURITY.md says.
License
Apache-2.0. See LICENSE.
Metadata
Release files for nookku 0.4.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| nookku-0.4.1.tar.gz | 334.7 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| nookku-0.4.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 469.9 kB
Release files / nookku-0.4.1.tar.gz
| Download URL | nookku-0.4.1.tar.gz |
|---|---|
| Size | 334.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3bb59e837c05cc8bb31ef1c5dfaa59fc842692c98a2035d71631c2826de0fe3d
|
|
BLAKE2b-256 checksum How to use checksums |
d729375f307f948dbede8396ed019ab825ca9baa979d472fbecd3e3784913497
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.
Transparency logRelease files / nookku-0.4.1-py3-none-any.whl
| Download URL | nookku-0.4.1-py3-none-any.whl |
|---|---|
| Size | 135.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
747bfeda95d7578ce2ea645fa62974986ab06c90a5120a80a064854bf1e7d5dc
|
|
BLAKE2b-256 checksum How to use checksums |
99cfd01cebefae6d61cd7135bd4432e73862ebde2605c1093e4f079ca1de993a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.
Transparency logSecurity for nookku 0.4.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for nookku-0.4.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 13, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.