b39e4eac88
Three features turn out to be one mechanism: a command waiting to be approved, a question the model wants answered, and "this reply is waiting for you" are all — stop the generation, put an interactive block in the bubble, wait for a POST, carry on. So there is one primitive, and the only thing using it so far is `ask_user`: a model can offer you a few answers and a box to write your own. The shell executor is not here yet. This lands first on purpose, because it is the riskiest machinery in the feature and it is worth having working before any subprocess exists to complicate it. Two things about where the pause sits. It pauses a round, not a call: a round's calls run together under a semaphore, and parking four coroutines on four separate answers inside that gather would queue them behind each other invisibly. And Stop had to be taught about it — `cancel` is read between streamed chunks and there are no chunks while paused, so the button did nothing at all until `request_stop` learned to resolve the pause itself. Also here: a risk class on every tool (read, write, execute), which is what the four permission modes will be a table over, and the systemd unit loses ProtectKernelTunables. That last one is not tidying — it bind-mounts /proc/sys read-only, which stops bubblewrap mounting /proc at all, and the obvious workaround would expose this process's environment and with it the encryption key. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
37 lines
1.1 KiB
Python
37 lines
1.1 KiB
Python
"""Agentic execution: running commands and touching files on the model's behalf.
|
|
|
|
Four parts, and the split is the safety argument. `policy` decides what may
|
|
happen without asking and knows nothing about how anything runs. `base` is the
|
|
interface a target implements. `local` runs on this machine inside a bubblewrap
|
|
sandbox that cannot see the database or the encryption key; `ssh` runs on
|
|
somebody else's machine, where nothing is sandboxed and the credential is the
|
|
whole of the trust.
|
|
|
|
The mode is enforced in the generation loop, not in the prompt. A model is told
|
|
which mode it is in so it can behave sensibly, but being told is not what stops
|
|
it: everything it reads is untrusted, and a rule written only into a system
|
|
message is a rule a poisoned README can argue with.
|
|
"""
|
|
|
|
from lembas.services.agent.policy import (
|
|
MODE_AUTO,
|
|
MODE_EDIT,
|
|
MODE_MANUAL,
|
|
MODE_PLAN,
|
|
MODES,
|
|
Decision,
|
|
Limits,
|
|
decide,
|
|
)
|
|
|
|
__all__ = [
|
|
"MODES",
|
|
"MODE_AUTO",
|
|
"MODE_EDIT",
|
|
"MODE_MANUAL",
|
|
"MODE_PLAN",
|
|
"Decision",
|
|
"Limits",
|
|
"decide",
|
|
]
|