Repository navigation
Releases: stricttools/claudestream
Release list
v0.16.0
claudestream rewritten in Go: a library of typed Claude Code events with sessions started through claudewheel profile exec, strict TOML agent definitions, and a command line installed with go install or from the release archives. The Python package is no longer published; PyPI and npm keep 0.15.3.
Context
The stricttools projects are moving to Go; the Python and npm releases stop at 0.15.3.
Breaking
- claudestream is now a Go module. The Python package is gone: install the command with
go install github.com/stricttools/claudestream/cmd/claudestream@v0(or from the Linux and macOS archives on each GitHub Release) and the library withgo get github.com/stricttools/claudestream@v0. Nothing newer than 0.15.3 is published to PyPI or npm, and 0.15.3 stays there; Python programs that import claudestream stay on it. - Sessions start Claude Code through claudewheel. Claude Code runs as
claudewheel profile exec --name <profile> -- <claude> ..., so a session needs a claudewheel with theprofile execcommand, and the profile's environment reaches Claude Code from claudewheel rather than from the calling process. The library takes absolute paths of both binaries and never searchesPATH;ResolveOnPathfinds one for a caller that wants that. - The session config is explicit.
Configrequires the model, the profile, both binaries, and the session to use (NewSession,ResumeSession, orContinueLastSession), andConfig.Validaterefuses anything missing or contradictory instead of filling in a default. Tools areToolvalues with an explicit JSON Schema and a handler, replacing the@tooldecorator, its schemas from type hints, and handler injection. The sandbox dropsbare(which passed--bareand disabled token login) andlog_violations; the hooks, the cost log, the tool context, and the poll, join, and stuck timeouts are gone. - One session type, read turn by turn.
Startreturns aSession;Sendreturns aTurnwhoseNextyields typed events untilio.EOF, replacing the async and sync sessions,ask, andprint_prompt. Callbacks and lifecycle hooks are gone, and a session no longer restarts Claude Code when it goes quiet: a wedged turn waits until its context ends, and an authentication failure, a process exit, or a cancelled turn makes the session unusable. - Agent definitions are strict TOML. An agent is
.claudestream/agents/<name>.agent.tomlwithformat_version = 2;.agent.jsonfiles are refused, naming the steps to convert one by hand (keys one to one,format_version = 2, deletebareandlog_violations, give every tool aserver, rename the file). Unknown keys are refused,[sandbox]requiresskip_permissions, and agents are found only in the agents directory of the working directory. - The command line asks for everything it acts on.
ask,stream,send,events, andagent runtake the prompt as--promptor--prompt-from-stdin, and require--profile, one of--new-session,--resume <id>, or--continue-session, and one of--skip-permissionsor--no-skip-permissions.replandconfigare removed (config --profileprinted the profile's token), as aresend --rawand--json-output;agent run --modelis required when the definition declares no model and refused when it declares one, and agents that declare tools are refused.
Features
- Command line additions.
ask --jsonputs the reply and its metadata, including the session ID to resume, in the document's payload;doctor --profilestarts Claude Code through the profile once;--claude-binaryand--claudewheel-binarychoose the programs;--color/--no-colorand--footer/--no-footercontrol the rendering; andsendmarks error tool results, permission decisions, tool calls the session served, and unrecognized frames.
Fixes
- Claude Code never waits for an answer that does not come. A control request the session does not handle, and an MCP message for an unknown server or method, are answered with an error and reported as events; a permission request a sandbox decides is answered and reported as
PermissionDecidedrather than also yielded as a request; and the SDK MCPinitializeanswers with the protocol version Claude Code proposed when it is one claudestream supports. - Every line Claude Code writes reaches the consumer. Non-JSON lines, frames that do not decode, and unknown content blocks become events instead of log warnings;
FileWriteandFileEditfollow a successful tool result rather than the request; the permission mode, MCP server statuses, and authentication failures are read from the keys Claude Code sends; and rate limits are read from both of their shapes. - List options repeat their flag. Several MCP config files, added directories, files, plugin directories or URLs, betas, and allowed tools each get their own flag (
--mcp-config a --mcp-config b) instead of one comma-joined value, which broke a session given more than one MCP config file. - A failed turn fails the command. Every session command exits 1, with the result text on stderr, when Claude Code ends the turn with an error.
v0.15.3
Archived documentation versions are no longer published, and the documentation moved onto selfdoc's .stricttools/ layout.
Fixes
- The tool describes itself with one sentence everywhere, including
--help. The--helpheader, README, docs site, PyPI and npm metadata now carry the same line. - Archived documentation versions are no longer published. The docs site serves the current version only; the per-version archives built from old tags are gone.
v0.15.2
v0.15.1
The project describes itself consistently on PyPI, npm and in its README, and its documentation base is the unified site.
Fixes
- The project describes itself consistently on PyPI, npm and in its README. The package summary, the npm wrapper summary and the README opening each said something different, and the PyPI page carried no project links, keywords or classifiers.
- Documentation links point at the unified site. The declared docs base was the retired per-project host; it is
https://smmh.dev/claudestream/now, so generated sitemaps, feeds and llms.txt name the address that serves the pages.
v0.15.0
Every CLI flag and argument declares its presence explicitly, on strictcli 0.41.0.
Context
strictcli 0.41.0 makes the per-flag presence declaration mandatory -- exactly
one of presence="required", presence="optional" or default=, never
inferred from the shape of another declaration -- and bans value defaults on
any command declaring effect="mutating", on the grounds that absence must never
resolve to a value the invocation did not state.
claudestream's five session commands plus agent run are all mutating, so the
ban reached 48 of the CLI's 51 declared defaults, and 16 further declarations
carried no presence at all (they were implicitly required under the old rules).
0.41.0 also removed required= from arg(), so the previous spelling did not even
import.
The triage was per site rather than mechanical. Every default="" turned out to
be an empty-string sentinel whose absence was reconstructed downstream by
testing the value for falsiness -- cwd or None, if profile:,
system_prompt or None -- so those flags declare optionality and deliver None,
which is passed straight through to SessionConfig. The behavioral switches
(--footer, --color, --raw, --stdin, --skip-permissions, --json-output) took the
third remedy the ban allows: they declare optionality and name their fallback
in their own help text, with one helper as the single place absence becomes
that fallback. --var keeps default=[], the one default the ban leaves legal.
The machine-output migration is deliberately not part of this. claudestream's
JSON output is a stream of one object per line, not the single document
strictcli's envelope describes, and the per-line framing that would be needed
does not exist in the framework yet. The design questions are written down in
todo/per-line-envelope-for-the-jsonl-event-stream.md rather than answered
privately here.
Breaking
- strictcli 0.41.0 is now required, and every CLI flag and argument declares its presence explicitly.
--helpstates for each one whether it is required, optional or carries a default, and an optional flag's help says what omitting it means. One behavior follows from it: an explicitly supplied empty prompt is now a supplied prompt, soclaudestream send "" --stdinreports the both-sources conflict instead of silently letting--stdinwin.
v0.14.2
Ships the second machine-local path fix: probe_user_dialogs.py now requires --captures-dir instead of defaulting to an operator scratchpad path.
Context
0.14.1 fixed a cassette privacy leak and added a repo-wide hygiene guard. That
guard immediately found a second leak of the same class: scripts/probe_user_dialogs.py
hardcoded an absolute operator scratchpad path (including a session UUID) as the
default for its captures directory. The fix landed right after 0.14.1 was cut, so
the offending path still ships in the 0.14.1 sdist. 0.14.2 exists to get the fixed
artifact published.
Fixes
- A second machine-local path is gone from the published sdist.
scripts/probe_user_dialogs.pydefaulted its capture directory to an absolute path naming the author's account; it is now a required--captures-dirargument. The hygiene guardrail was extended from the recorded cassettes to every git-tracked text file, which is what found it.
v0.14.1
Privacy fix: the published sdist no longer carries the recording machine's paths or private tool inventory.
Context
An audit of the 0.14.0 test infrastructure found that the VCR recorder's
scrubber emptied only list-valued inventory keys. memory_paths arrives from
the CLI as a dict, so it was copied verbatim and a real
/tmp/.../-home--Projects-claudestream/... path shipped inside the
committed cassette -- and inside the published sdist, which packages tests/.
The cassette also carried the recording machine's full tool inventory.
Scrubbing is now type-agnostic (dict, list, str) rather than list-shaped, the
tools inventory is filtered to a public-tool allowlist, and both cassettes were
re-recorded through the credential-less free lane (total_cost_usd 0, zero API
spend). tests/test_cassette_hygiene.py is the backstop that does not need to
know the schema: it greps every committed cassette for home directories, temp
directories, $HOME, the operator username as a path segment and absolute-path
shapes, so the next unknown protocol key cannot leak silently.
The same audit found the spend guard weaker than advertised, all fixed here and
pinned by tests: tests/test_integration.py hardcoded an absolute path to the
real binary (the poisoned PATH only intercepts name lookups, so 19 tests were
outside the guard); the guard hooks lived in tests/conftest.py, so pytest scripts/ ran unguarded; the opt-in assertion checked only a skip count, which a
broken local profile reproduces; and two spend-capable operator scripts in
scripts/ were named test_*.py, so pytest collected them.
Fixes
- Fixed a privacy defect in the published sdist. The 0.14.0 tarball's test cassettes carried the recording machine's filesystem paths and private tool inventory: the recorder scrubbed only list-valued inventory keys, and
memory_pathsarrives as a dict. Scrubbing is now type-agnostic, thetoolsinventory is filtered to a public-tool allowlist, both cassettes were re-recorded, and a hygiene test greps every committed cassette for machine-local shapes.
v0.14.0
Agent definitions are schema-validated at load, the CLI runs on strictcli 0.36, and the test suite can no longer spend money.
Context
This release carries the strictspec at-rest gate for .agent.json documents
(a breaking change: an integer format_version is now required), the strictcli
0.36 command-classification migration and its dependency floor, two new
documentation guides, and a fix that makes agent discovery report the deprecated
budget fields as helpfully as load_agent already did.
Behind that, the test suite changed shape. Integration tests used to drive the
real claude binary against a real profile on every full-suite run, so the
release gate itself billed a real account. They are now opt-in behind
CLAUDESTREAM_INTEGRATION=1, backed by a poisoned PATH shim so nothing else can
reach a real binary either, and a recorded-replay (VCR) lane covers the wire
protocol -- real subprocess, real pipes, real control round-trips -- from
committed cassettes at zero cost.
Breaking
- Agent definitions are now schema-validated at load.
.agent.jsondocuments loaded viaload_agent/discover_agents/theagentCLI are validated against a strictspec schema before decoding: they now REQUIRE an integerformat_version(currently1), and unknown keys (with did-you-mean hints) or wrong-typed fields are rejected with hardAgentValidationErrors. Stamp existing agent files with"format_version": 1one time.
Features
- Architecture and agents guides. The documentation site gains a guide to claudestream's four layers and a guide to agent definitions, plus a generated table of every stream event type.
- Fuller CLI help text. Every command and flag description was expanded, so
claudestream <command> --helpexplains what a command does and when to reach for it.
Fixes
- The CLI works again on strictcli 0.36.0. strictcli made per-command effect classification mandatory, which hard-errored claudestream's command registration --
claudestream --help, every subcommand and any import ofclaudestream._clifailed. All 11 commands are now classified: the six that spawn a Claude Code subprocess aremutating, the five inspection commands areread_only. No command prompts for confirmation. - Agent discovery now explains the deprecated budget fields. A
.agent.jsonstill usingmax_cost_usd,max_turnsormax_tokensreported a generic unknown-key error when found bydiscover_agents(orclaudestream agent list) whileload_agentgave the migration hint. Both paths now name the replacement threshold fields. - Installs can no longer resolve a strictcli too old to run claudestream. The manifest now declares
strictcli>=0.36.0; below that, command registration fails and every CLI invocation errors on import.
v0.13.1
Real-CLI integration tests now skip gracefully when the claude binary or a claudewheel profile is unavailable, so CI and credential-less environments no longer fail the suite.
Context
The 0.13.0 release tagged and pushed successfully but its PyPI and npm publish
was blocked because the CI test suite failed on the GitHub runner: the newly
scaffolded full-suite CI ran the real-CLI integration tests in a credential-less
environment with no claude binary and no claudewheel profiles, and claudewheel
0.22's resolve_profile raises instead of failing soft, turning what should be
skips into 20 hard errors. This hotfix adds a shared skip guard so those tests
skip gracefully when their external prerequisites are absent, unblocking the
publish that the 0.13.0 CI gate held back. bump = hotfix because the change is
test-infrastructure only (no user-facing changelog entries).
Hotfix
- Real-CLI integration tests now skip gracefully when the claude binary or a claudewheel profile is unavailable, so CI and credential-less environments no longer fail the suite.
v0.13.0
Control-protocol extensions for interactive clients: interrupt, live model and permission-mode switching, context usage, user dialogs, enriched permission prompts, and renderer observability.
Context
These additions power alternative frontends that drive the claude CLI directly. AskUserQuestion is answered by feeding the choice back through a permission updatedInput, so interactive tools resolve without a separate answer channel. A correlation registry tracks pending control requests with restart-safe hard failures, so a dropped or restarted session surfaces an explicit error rather than a silently unresolved future.
Breaking
- Breaking. Renamed
--no-colorflag to--color(default: true) across all commands. Use--no-colorto disable color output.
Features
- New feature. Added
__version__attribute to theclaudestreampackage. - Control-request wire structs. New
ControlRequestmessage struct and aControlResponse.errorfield expose the CLI control protocol for interrupt, model, and mode requests. - Control-request correlation. Sessions can issue correlated control requests with pending-future tracking, turn-loop and between-turns response resolution, timeouts, and restart/close safety.
- Interactive session controls.
interrupt(),set_permission_mode(),set_model(), andget_context_usage()are now available on bothAsyncSessionandSyncSession, andSessionConfig.permission_modesets the initial permission mode via--permission-mode. - Dialog and enriched-permission responses. New
DialogCompletedandDialogCancelledmessages answer user dialogs,AllowPermissionacceptsupdated_permissionsto apply permission-rule updates on allow, andInitializeRequestcan declaresupported_dialog_kinds. - Tool-permission prompts now decode correctly. The current Claude CLI sends permission prompts as
can_use_tool; these now surface asPermissionRequest(with new fields likepermission_suggestions,display_name, andrequires_user_interaction) instead of being missed. A newUserDialogRequestevent carries CLI-driven user dialogs. - User-dialog handling on sessions.
respond_dialog()andrespond_dialog_cancelled()answer dialog requests,respond_allow()acceptsupdated_permissions, andSessionConfig.supported_dialog_kindsdeclares which dialogs the consumer can render (forcing the initialize handshake). Available on bothAsyncSessionandSyncSession. - Renderer observability.
ToolResultnow carriestool_name(correlated from the matchingToolUse), andResultexposes per-modelmodel_usagefrom the CLI'smodelUsagepayload. - Permission interception. New
SessionConfig.intercept_permissionsroutes permission prompts and interactive tools like AskUserQuestion to the consumer as PermissionRequest events. - Tool error signal.
ToolResultandToolResultBlocknow exposeis_error, so consumers can distinguish failed tool executions from successful ones.
Fixes
- Fix.
ToolSchemafieldsdescription,input_schema, andserverare now optional, supporting name-only tool entries.