feat(claw): the scoped agent token and the client-tool routes

The claw is the server half of the client tools. The middleware now
knows two bearer classes: the human token answers every /v1 route as
before; the agent token answers only the routes the contract
allowlists, and only with the contract header at the matching version
— consents, sessions, ingest, and the phones answer it 403, so a
model holding the token cannot prompt-inject its way past the scoping
to answer its own secret asks. Boot refuses the configuration where
agent_token is the human token: the scoping is the safety, and one
value playing both roles silently unscopes everything the shell
holds.

Three new routes write the tables the resident already reads: push to
the notification queue, schedule and cancel tasks the resident's tick
fires — the tools get native-shaped writes without the daemon growing
a second brain. A JSON fallback gives the one error shape everywhere,
unknown routes included. The daemon composes the tools' environment
(RHEMA_URL, RHEMA_TOKEN, the tools' bin dir on PATH) and hands it to
the resident's shell through the agent-tools env layer; with no
agent_token it provisions nothing, exactly as before.

The proof is the contract's conformance suite run against this router
over real HTTP — same answers as the mock, checked at test time.
This commit is contained in:
vhaudiquet committed 2026-10-24 01:02:28 +02:00
1 parent 2fbd93fd79
commit 98bc0fd623
9 files changed
+422 -28

No files matched your search

+4
View File
@@ -46,6 +46,10 @@ rhema-provider-llama-cpp = { path = "../rhema-provider-llama-cpp" }
rhema-provider-openai-compatible = { path = "../rhema-provider-openai-compatible" }
rhema-provider-openrouter = { path = "../rhema-provider-openrouter" }
rhema-rsd = { path = "../rhema-rsd" }
# The tool contract: the agent-token allowlist and version the auth
# middleware enforces (rhema-tools spec). The arrow runs claw ->
# contract only — never the reverse.
rhema-tools-contract = { path = "../../tools/contract" }
# The resident tier's durable memory: its tools join the resolver chain
rhema-memory = { path = "../rhema-memory" }
# The shared ModelCard the backend registry's listings return