You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Dangerous mode: let your agent run commands, when only you can give it work - #810
Your agent can read and change files in the folder you start it in, and that's all. It can't run anything: not your linter, not your tests, not git. So "fix this and make sure the tests pass" isn't something it can do. It edits the code and you run the tests.
This adds a dangerous mode you can switch on for your own agent:
basecamp connect setup -P agent --dangerous
With it on, the agent can run any command on your computer, as you. It can run the linter and tests, fix what fails, and commit. Turn it off with --dangerous=false.
Why it's called dangerous
The agent reads the whole thread it's working in, including other people's comments and attachments. With a shell, text anywhere in that thread could steer it into running something on your machine. Everything you can reach, it can reach: your files, SSH keys, your Basecamp login, cloud tokens, the network. So it's off by default, and switching it on is deliberate:
You have to type yes. Setup explains what it means and asks you at the terminal. With no terminal (a script, or an AI running the command) it refuses, so an agent can never switch this on for itself.
Only for an agent you alone can give work to. If others can give it work (--trust project or --allow), setup refuses and explains why. It gives the one command that makes it possible (--trust operator --dangerous). Letting people in while it's on is refused the same way, and so is changing who the operator is.
It's hard to miss. Setup's summary, connect status, connect doctor and the connector's first log line all say it's on.
Why not for shared agents yet
If anyone in a project could give your agent work, anyone in that project could get your computer to run something, as you. Keeping it to you alone means the only person making those requests is you. It will stay off for shared agents until the agent's work can run in a box that holds only the project, with nothing of yours in it to reach.
Making sure it only ever runs your requests
The connector reads trust once, when it starts. A connector started while others could give it work never uses dangerous mode, whatever the file says later.
If requests from other people are still waiting from when they could give the agent work, the connector starts with dangerous mode off and says so. Those requests run as usual, never with a shell. Once they're done, a restart turns it on.
Turning it off applies from the very next decision: the connector reads the setting fresh before every launch and follow-up. A worker already running in dangerous mode finishes what it's doing and is handed nothing more, not even new messages in its thread mid-turn. Once off, it stays off for that run: turning it back on takes a restart. Setup says so, and says how to stop it now if you want to. The off switch works with Basecamp unreachable, and with BASECAMP_TOKEN set.
--dangerous=false on its own is the off switch, and it's saved without asking Basecamp anything, so it works offline or with a credential Basecamp no longer takes.
Dangerous mode only applies while connect.json names the operator the connector started with, so turning it back on can never hand a previous operator a shell before a restart.
If the connector started with dangerous mode off for that run, connect status says so, rather than claiming it's on.
Turning it on while a connector is running asks you to restart it.
A connect.json edited by hand to be both dangerous and shared is refused when it's read, by setup, status and the running connector alike.
How it works
Claude Code workers get every built-in tool, Bash included, and --permission-mode bypassPermissions instead of acceptEdits.
Codex workers run without a sandbox, with the network on. The driver checks the session Codex reports is the unsandboxed one it asked for, as it already does for the sandboxed one.
Everything else is unchanged: your own Claude and Codex settings are still ignored, only the Basecamp tools are connected, and the worker's environment is still an allow-list.
…t work
basecamp connect setup --dangerous lets the agent run any command on this
computer, as you: tests, linters, git. It asks you to type yes at a terminal,
so a script or an AI can't turn it on, and it's refused while other people can
give the agent work, with the reasons and the one command that makes it
possible. Setup, doctor, status and the connector's first line say while it's
on, and --dangerous=false turns it off at the next task.
…t is needed
- A run only uses dangerous mode if nobody else's request is still waiting from
when others could give the agent work; those run without it.
- A worker started in dangerous mode gets nothing more once it's turned off.
- The operator can't change while dangerous mode is on.
- Setup says when a running connector needs a restart, and that a task already
running finishes as it started.
… the operator
- Dangerous mode is read fresh for every launch and follow-up, not from the
two-second cache, so turning it off reaches the very next decision.
- It applies only while connect.json names the operator the connector started
trusting, so turning it back on can't hand a previous operator a shell.
- --dangerous=false on its own is saved with nothing asked of Basecamp: the off
switch works offline, or with a credential Basecamp no longer takes.
- Status says when the running connector isn't using dangerous mode.
- Refusals print commands that run as printed.
…and tell status the truth
Turning dangerous mode off stopped follow-ups between turns, but a new message
could still join the task mid-turn and be fetched by the running worker. Now
nothing joins a task started in dangerous mode once the mode is off; the
message runs as its own task, without it, after this one ends.
Status no longer says dangerous mode is in use when the running connector
started without it, and turning it off no longer promises a folder boundary
that ordinary mode doesn't have.
Reject dangerous mode with unsupported ACP driver override
internal/commands/connect_run.go:402
This enables dangerous policy without checking the effective driverName. Although File.Validate requires the stored driver to be spawn, basecamp connect --driver acp overrides it after the file is loaded; ACP has no ModeAnything mapping, so startup succeeds but every worker launch is unusable and work enters the start-failure hold. Refuse the ACP override when dangerous mode is effective before constructing the dispatcher.
Emit dangerous-mode notice before any other startup logs
internal/commands/connect_run.go:455
This is not always the connector's first log line as promised in the PR description: --hold logs at line 297, and an existing hold logs at line 302, both before this switch. Emit the dangerous-mode notice immediately after dangerousAllowedThisRun is decided so every startup path leads with it.
…us honest
- Once it reads off, it stays off for the run, so off-then-on can't skip the
restart or feed a worker that was cut off.
- A request from someone else that's blocked is judged again under today's
trust, so it no longer keeps dangerous mode off for good. One whose worker
failed to start still counts.
- The off switch works with BASECAMP_TOKEN set.
- Status keeps the run's dangerous-mode note when work is taken again after a
hold, says nothing about it for --shadow, and the summary keeps its caveat.
- Turning it on for a new operator in one run is allowed; only a swap while
it's already on is refused.
- Guided setup shows it even with no projects.
The confirmation prose is written to stdout before the normal app.OK response. With --json (or auto format on a pipe), a successful enable therefore emits several plain-text paragraphs followed by JSON, so consumers cannot parse the command's output. Send the interactive warning to stderr, as refusedChange already does, and reserve stdout for the selected output format.
This shortcut bypasses the output writer entirely, so connect setup --dangerous=false --json (and auto output when piped) returns plain English rather than the repository's JSON success envelope; the already-off branch has the same problem. Keep direct prose only for styled output and use app.OK for machine-readable formats.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
commandsCLI command implementationstestsTests (unit and e2e)
2 participants
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Your agent can read and change files in the folder you start it in, and that's all. It can't run anything: not your linter, not your tests, not
git. So "fix this and make sure the tests pass" isn't something it can do. It edits the code and you run the tests.This adds a dangerous mode you can switch on for your own agent:
With it on, the agent can run any command on your computer, as you. It can run the linter and tests, fix what fails, and commit. Turn it off with
--dangerous=false.Why it's called dangerous
The agent reads the whole thread it's working in, including other people's comments and attachments. With a shell, text anywhere in that thread could steer it into running something on your machine. Everything you can reach, it can reach: your files, SSH keys, your Basecamp login, cloud tokens, the network. So it's off by default, and switching it on is deliberate:
yes. Setup explains what it means and asks you at the terminal. With no terminal (a script, or an AI running the command) it refuses, so an agent can never switch this on for itself.--trust projector--allow), setup refuses and explains why. It gives the one command that makes it possible (--trust operator --dangerous). Letting people in while it's on is refused the same way, and so is changing who the operator is.connect status,connect doctorand the connector's first log line all say it's on.Why not for shared agents yet
If anyone in a project could give your agent work, anyone in that project could get your computer to run something, as you. Keeping it to you alone means the only person making those requests is you. It will stay off for shared agents until the agent's work can run in a box that holds only the project, with nothing of yours in it to reach.
Making sure it only ever runs your requests
BASECAMP_TOKENset.--dangerous=falseon its own is the off switch, and it's saved without asking Basecamp anything, so it works offline or with a credential Basecamp no longer takes.connect.jsonnames the operator the connector started with, so turning it back on can never hand a previous operator a shell before a restart.connect statussays so, rather than claiming it's on.connect.jsonedited by hand to be both dangerous and shared is refused when it's read, by setup, status and the running connector alike.How it works
--permission-mode bypassPermissionsinstead ofacceptEdits.