Security
- A call that should have stopped could run, on 0.6.0 to 0.6.8. Tool calls that run in parallel
(LangGraph's ToolNode runs a turn's calls in threads; async agents interleave them) could be
decided with the control a guard had before it read the state: the default mode, log, without a
freeze. In a test with two parallel first calls about one in three calls over a firm limit ran.
The control is now read and only then marked as seen, under a lock, and is never a permissive
default: a state that cannot be read keeps the last control read, and with none read yet the
call stops.
- The state file could be lost or undone between processes: every write used the same temporary
file name, an unreadable state read as "nothing set", and a writer that had read the state
earlier could undo a freeze or a mode written meanwhile. Writes now use a temporary file of
their own and an atomic rename; the controls are written in one locked step; any other save
keeps what changed on disk meanwhile; change detection uses time, inode and size.
- Policies compiled or reloaded from several threads at once could crash the process (Z3 is not
thread safe); compiling now takes turns.
- Wiring changes only files inside the folder the last scan covered: a workspace copied from
another folder could change the original agent's code.
- The Claude Code hook line no longer depends on one path: it runs the cslcore found when wiring,
else cslcore on PATH, else exits 2 (Claude Code lets a call through on any other failure).
- The lock between writers works on every OS (flock, msvcrt, or a lock file). Guards never take
it to read: a tool call never waits on the panel. The state needs a local disk: flock is not
reliable on NFS and some container volumes.
Added
- The protection board in setup: the riskiest agents first; Enter gives every agent standard
limits, a policy, block mode and the wiring change (shown and confirmed); a number runs one
agent's loop (limits, policy, wiring, check); s leaves an agent untouched.
- Limits in your own numbers:
cslcore limits (amounts, any numeric argument, allow, approval or
block per tool, extra tools), the check that decides sample calls with the real policy, and
csl-limits.ini with cslcore apply (--check for CI, --init).
- Approvals are real: Claude Code asks (the hook answers "ask"); a wired Python tool returns
ApprovalPending and the call waits in cslcore watch (key a); approved, the same call runs once
within ten minutes.
- A stopped call does not end the agent: a tool under LangChain's, OpenAI Agents' or CrewAI's
decorator returns Blocked, readable text with the reason; plain functions still raise
PermissionError.
cslcore watch: l changes an agent's limits, w wires or unwires it, a lists approvals.
- Several agents in one repository are found one by one (explicit agent definitions, folders).
- Before a Python agent is wired, its interpreter must import csl-core; the install command is
shown and asked for.
cslcore wire --diff; a one-line summary before every wiring diff.
Changed
- A tool whose arguments are known and hold no file path (deploy(service, version)) is no longer
judged by where it writes: its ordinary calls run, recorded. Tools named for credit, rebates,
reimbursements or top-ups move money; sending an invoice is sending, not paying.
- A tool set to need approval has that one rule (its path, command or destination checks are
replaced; the operator's own maximums stay), so it really waits for a person.
- A stop says its cause: a frozen agent and how to unfreeze it, a tool turned off or set to never
run and how to change that; "ask the operator to change the limits" only for a limit.
- Without --root, the board lists the agents outside the current folder and protects them only
when asked.
- An agent activated for the first time starts in block mode unless a mode was chosen; agents
already in log mode keep it.
- Everything is written under
.csl/ (kept out of git); policies/ in a workspace root from
0.6.8 is still read. The one file to commit is csl-limits.ini.
- The guard line is portable:
venom_guard(KEY, workspace="..", near=__file__), CSL_WORKSPACE, or
the nearest .csl above the file.
- Setup screens in plain words;
cslcore --help starts with "Start here: cslcore setup".
pip install -U csl-core
uvx --refresh csl-core venom