Capture intent
The plugin remembers the user's request for the task. That request is the only trusted statement of intent. A command can never nominate itself.
"clear the temp build output"
The agent proposes the command. sino checks it against the request you actually made, then allows or denies it before a process starts.
Every incident below had a permission system. The agent was allowed to run commands.
The problem is that allowed never meant asked for.
“@Replit goes rogue during a code freeze and shutdown and deletes our entire database.”
what the agent didReplitdestructive DB operation during an explicit freeze
1,206 executive records deleted · 4,000 fake rows invented after
View post
AG
Alexey Grigorev
@Al_Grigor
“Claude Code wiped our production database with a Terraform command. It took down the platform and 2.5 years of submissions.”
what ranClaude Codeterraform destroy · stale state file
24 hours to restore · VPC, RDS, ECS and every snapshot destroyed
View post
AP
Alex Prompter
@alex_prompter
“The command was supposed to remove a project folder. But the escape character collapsed across four different shell parsers.”
what cmd receivedClaude Coderd /S /Q \
717 GB deleted in 90 seconds
View post
UK
Udaya Kumar P.L.
@pluday
“The safety layer permitted the destruction, then stood between me and the off switch.”
the taskClaude Codeclear a cache · quoting error
5+ years of heritage docs gone · 4 minutes deleting in the background
View post
A deny-list knows what a command looks like. It does not know what you asked for. So it blocks the cleanup you wanted, waves through the destructive command you didn't, and trains you to turn it off.
rm -rf * in /tmp is blocked. rm -rf * in your home directory is also blocked. One is a cleanup; the other is an incident. The rule cannot tell.
The bounded delete in /tmp serves the request. The unbounded delete in your home directory does not. The decision is about the task, not the spelling.
Pick a preset to see the same command flip. Then edit either field and make the example yours.
Edit either field, then run. Editing detaches the preset.
/Users/you/app
platform darwin
/v1/decisions
No shell is spawned until the decision is made. The judge sees the whole line, the parsed subcommands, the effective working directory, and the request that started the task.
The plugin remembers the user's request for the task. That request is the only trusted statement of intent. A command can never nominate itself.
"clear the temp build output"
Comments and heredoc bodies come out. Substitutions and pipes stay intact. `cd` updates the effective cwd. Tripwire content is flagged and never reaches the model.
cd /tmp && rm -rf *
A decision model scores Allow or Deny against the request. The command is untrusted data: instructions, fake scores, and hidden characters are not evidence.
Allow 0.94 / Deny 0.03
One binary effect. Any timeout, transport error, or malformed answer is a deny. Every record includes the command, parsed scope, reason, probability, and latency.
allow · logged
Deny-lists, sandboxes, scoped tokens, and audit logs all matter. None of them answers the question this layer answers: did the user ask for this?
Swipe to compare columns
| Capability | Static ruleshooks · deny-lists · Blast Radius | Sandboxescontainers · microVMs · Seatbelt | Agent IAMscoped tokens · OAuth | Audit logstraces · SIEM · outputs | sinointent arbitration |
|---|---|---|---|---|---|
| Reads the user's request | — | — | — | — | ✓ |
| Blocks known-dangerous shapes | ✓ | ◐ | — | — | ✓ |
Distinguishes /tmp from $HOME |
◐ | ✓ | — | — | ✓ |
Understands pipes, $(…), heredocs, cwd |
◐ | — | — | ◐ | ✓ |
| Resists instructions inside the command | ◐ | — | — | — | ✓ |
| Allows requested destructive work on real targets | ✕ | ✕ | — | — | ✓ |
| Fails closed on timeout or error | varies | ✓ | — | — | ✓ |
| Explains the verdict in a sentence | ◐ | — | — | ◐ | ✓ |
| Logs the command, scope, reason, probability | ◐ | — | ◐ | ◐ | ✓ |
Fast, cheap, and easy to bypass. The rule sees the string, not the task. It blocks the cleanup you asked for and misses the command that only becomes destructive because of where it runs.
Contain the blast radius. They do not decide whether the blast was intended. A sandbox that blocks the work is just a slower “no”, and most teams turn it down to get the work done.
Scoped tokens and short-lived credentials are necessary. They would not have stopped the Cursor agent in the PocketOS incident from using an over-privileged Railway token. Identity is not intent.
A log after the fact is a post-mortem, not a control. It tells you what happened after the volume and its backups are gone.
sino is an OpenCode plugin. Point it at any OpenAI-compatible /decisions endpoint, or use OpenCode's decision-model transport. No static deny rules required.
Install with opencode plugin add github:bsalbilla06/sino.
Set enforce: true and a 2s timeout.
Run the tests, then observe a session in enforce: false if you want a dry run.
// opencode.json
{
"plugins": [{
"package": "github:bsalbilla06/sino",
"options": {
"enforce": true,
"transport": "fetch",
"fetch": {
"baseURL": "https://api.openai.com/v1",
"path": "/decisions",
"model": "gpt-6-luna",
"apiKeyEnv": "OPENAI_API_KEY"
},
"timeoutMs": 2000,
"allowThreshold": 0.55,
"cache": true,
"denyOnTripwire": true,
"log": {
"enabled": true,
"file": ".opencode/sino/verdicts.jsonl"
}
}
}]
}