Netclaw 0.12.0
0.12.0 2026-04-13
Netclaw v0.12.0 — KV cache-friendly session routing, Slack PDF/document ingress, durable working context, structured compaction, and security hardening
Features
Added cache-stable message assembly with a volatile User-role tail — memory recall, current time, and working-context layers used to be injected as System messages immediately after the persisted prompt, which broke llama.cpp prompt-cache prefix matching on every turn. A new
SessionMessageAssemblernow partitions outgoing messages so cache-stable content (persisted prompt, static dynamic context, conversation history) comes first and volatile content is consolidated into a single User-role tail. Combined with session-sticky routing (#610), multi-turn conversations reuse the KV cache across turns. (#618)Added session-sticky LLM routing via
X-Session-Idheader — self-hosted inference servers behind a load balancer no longer defeat KV cache reuse across turns. ADelegatingHandlerpromotes the ambient session ID to anX-Session-IdHTTP header, which the load balancer can hash on (e.g., Caddylb_policy header X-Session-Id) to pin same-session requests to the same backend GPU. Works automatically for all providers; managed providers (Anthropic, OpenAI, OpenRouter) safely ignore the header. Sidecar calls (compaction, title generation, memory extraction) bypass the header so they don't compete with the main session's cache slot. (#610)Added llama.cpp
timingsparsing for cache and performance metrics — the OpenAI-compatible provider now surfaces KV cache hit counts (cache_n), prompt processing time (prompt_ms), and predicted throughput (predicted_per_second) from llama.cpp responses. New fields (cachedInputTokens,promptMs,predictedPerSecond,ttftMs,totalMs) flow throughUsageOutput, the SignalR wire DTO, and thechat -p --jsonCLI envelope. Graceful degradation: all fields stay null for providers that don't emittimings. (#615)Added
--resumeand-pflags tonetclaw chatfor scripted multi-turn sessions —netclaw chat -p "prompt"replaces the old top-levelnetclaw -pform, andnetclaw chat -p --resume <id> "prompt"creates-or-resumes a named session so multi-turn evals, KV cache benchmarking, and compaction regression tests can run against the same session across turns.netclaw chat -p --jsonemits structured JSON (sessionId,response,toolCalls,usage) for machine consumption. (#613)Added audience-gated attachment ingress contract with Slack PDF support — PDFs, DOCX files, and other non-image attachments were silently dropped from Slack messages with no feedback to the user. This release introduces a cross-channel
ChannelAttachmentPolicyonToolAudienceProfilewithAllowedCategories,MaxFileBytes(default 25 MiB), andMaxFilesPerMessage(default 10). Public audiences allow images only; Team audiences allow everything except unknown binaries. Slack now downloads and surfaces non-image attachments consistently, and the agent gets a dynamic system-prompt hint explaining what it received and whether the active model can view it. (#601)Added durable
WorkingContextgrounding that survives compaction and restart — sessions now persist aWorkingContextfield alongside conversation state, containingRecentFiles(bounded ring buffer of 10, updated automatically by file-taking tools),OpenGoals, andProgressMarkers. Unlike observer-reconstructed context,WorkingContextsurvives compaction, actor recovery, and daemon restart without depending on the observer LLM. (#598)Added structured compaction summary with monotonic boundary — session compaction now uses a 9-section structured template (adopted from Cline's LLM harness) with explicit anti-drift rules, truncate-only-at-user-message-boundaries enforcement (adopted from OpenCode), and self-session-id disambiguation so the observer can mark foreign session IDs from tool-call history.
SessionState.CompactionBoundaryIndexprovides monotonic metadata pointing at the most recent session-summary marker. Fixes a Slack failure where a session lost its own ID after a compaction following a turn investigating another session. (#597)Containerized
netclawdand moved the behavioral eval suite into ephemeral Docker —docker/Dockerfileproduces a release-gradeubuntu:24.04image pre-installed withgit,jq,sqlite3,python3,gh, and thenetclaw+netclawdbinaries. The eval suite now runs against a fresh container per invocation, so evals no longer contaminate the operator's real~/.netclawstate with seeded test docs and LLM-formed memories. The same image is the supported Docker-deployment artifact for self-hostednetclawd. (#603)Added multi-turn conversation eval category and Personal posture for the eval container — new Category 8 exercises
chat -p --resumeacross 2-5 turn scripted sessions and captures per-turn KV cache and timing metrics. Adds a "Multi-Turn Cache Evolution" report section showing cached vs uncached token trending per turn, plus fixes a latent eval container misconfiguration that was silently degrading shell-using cases to tool-call-marker-only checks. (#616)
Bug Fixes
Fixed control-plane writes bypassing approval policy — agents could silently edit
~/.netclaw/config/netclaw.jsonand other control-plane files, which could trigger daemon restart and drop the active session mid-turn.ToolPathPolicyis now split into three independent deny surfaces (write-deny, read-deny, shell-indicators), and a newFilePathApprovalMatchersupports argument-aware control-plane approvals with path-scoped patterns likefile_write:control-plane:netclaw.json. Approval-mode precedence is now deterministic for path-aware calls, and approve-once retry uses the same filtered unapproved pattern set the user saw in the prompt. Shell resource deny coverage is extended to lifecycle files (netclaw.db,netclaw.pid,netclaw.lock,cache/restart-manifest.json). (#617)Fixed Slack attachment contract parity and hardening —
SlackThreadHistoryFetcherno longer hard-filters thread-history attachments toimage/*, so historical PDFs, DOCX files, and other non-image attachments are now downloaded and scanned consistently with live ingress.SlackThreadBindingActorreplaces rawex.Messagein all user-facing rejected paths with stable messages (exception detail stays in structured logs).EscapeQuotednow strips control characters so hostile filenames can no longer embed newlines in the[attachment]announcement line. (#607)Fixed Claude Code marketplaces and
~/.claude/commandsskill resolution — theclaude-codesource alias now resolves to~/.claude/skills,~/.claude/commands, and every installed marketplace under~/.claude/plugins/marketplaces/*/skills/, so marketplace skills like dotnet-skills are discoverable and~/.claude/commandssupport is restored. Frontmatterless flat.mdfiles are accepted only for the Claude commands path, matching current Claude Code behavior; non-commands paths still require valid YAML frontmatter. (#599)Fixed system skills feed serving a stale manifest to daemons —
publish_skills.ymlwas runningwrangler pages deploywithout pinning a branch, so release-tag builds (which run in detached HEAD) shipped the new manifest to ahead.netclaw-feeds.pages.devpreview alias instead of the production custom domain.feeds.netclaw.devhad been frozen on the 2026-03-20 manifest for two releases, causing 5 of 6 skills to 404 on every daemon sync. The deploy now pins--branch=devand a post-deploy propagation check asserts the publishedupdatedAtmatches the freshly generated manifest. (#606)
Internal
- Disabled the
publish-dockerjob in the release pipeline — Docker image publishing to GHCR is gated off until the Docker release path is green-lit.publish-binariesand the Cloudflare Pages manifest job are unaffected, andvalidate_docker_image.yml(PR build, no push) is untouched. (#620)