mirror of
https://github.com/patriceckhart/zot.git
synced 2026-06-26 21:36:31 +02:00
rename: /lock -> /jail, /unlock -> /unjail
User-facing slash commands renamed to /jail and /unjail. The internal Sandbox type (Lock/Unlock/Locked methods, atomic.Bool field) keeps its mutex-style names because those describe the implementation, not the feature. Everything the user sees swaps: - slashCatalog: /jail + /unjail entries and descriptions. - runSlash handlers: case "/jail" / case "/unjail"; status line reports "jailed to <cwd>" / "unjailed". - Status bar tag: "· jailed · ~/cwd" (was "· locked ·"). - Sandbox error messages: "jailed: path X is outside sandbox root Y (use /unjail to disable)" etc. - README: table rows, section heading, body text, busy-mode section all updated. - Website (/Users/pat/Sites/zot): Tools section prose updated. - SDK doc comment in pkg/zotcore refers to /jail. Internal identifiers (Sandbox, Lock(), Unlock(), Locked(), CheckPath, CheckCommand, slashCancelsTurn switch) unchanged. Verified: go vet clean, go test -race ./... clean, bun typecheck + lint + build clean on the site.
This commit is contained in:
parent
c610a3a645
commit
b6fc3fd886
8 changed files with 23 additions and 22 deletions
13
README.md
13
README.md
|
|
@ -161,7 +161,7 @@ zot --help
|
|||
- `edit`: one or more exact-match replacements in an existing file.
|
||||
- `bash`: run a shell command in the session cwd, with merged stdout/stderr and a timeout.
|
||||
|
||||
When the sandbox is on (see `/lock`), all four tools refuse paths outside the session cwd.
|
||||
When the sandbox is on (see `/jail`), all four tools refuse paths outside the session cwd.
|
||||
|
||||
## Modes
|
||||
|
||||
|
|
@ -194,8 +194,8 @@ Type `/` in the TUI to open the autocomplete popup. Available commands:
|
|||
| `/btw` | Side chat with full context that doesn't add to the main thread. |
|
||||
| `/skills` | List discovered skills (SKILL.md files) and preview their bodies. |
|
||||
| `/compact` | Summarize the transcript into one message to free up context. |
|
||||
| `/lock` | Confine tools to the current directory. |
|
||||
| `/unlock` | Allow tools to touch paths outside again. |
|
||||
| `/jail` | Confine tools to the current directory. |
|
||||
| `/unjail` | Allow tools to touch paths outside again. |
|
||||
| `/reload-ext` | Hot-reload all extensions (re-read manifests, respawn subprocesses, rebuild tool registry). |
|
||||
| `/yolo` | Turn off `--no-yolo` confirmation for the rest of this session. |
|
||||
| `/clear` | Clear the chat transcript. |
|
||||
|
|
@ -236,9 +236,9 @@ Sends the current transcript through the model with a structured summarization p
|
|||
|
||||
zot also auto-compacts in the background: after any turn that leaves context usage at or above **85%** of the model's window, the agent kicks off a condense pass on its own. You'll see `condensing history, esc to cancel` above the status bar and an `(auto)` tag next to the context percentage; `esc` aborts it without touching the transcript.
|
||||
|
||||
### `/lock`
|
||||
### `/jail`
|
||||
|
||||
Enforces a sandbox rooted at the cwd shown in the status bar. `read`, `write`, and `edit` resolve their target path (including through symlinks) and refuse anything outside the sandbox. `bash` refuses obvious escape patterns: `sudo`, `rm -rf /`, leading `cd /`, `cd ..`, `cd ~`, `chmod -R`, `dd of=/`, and similar. The status bar shows `locked, ~/your/cwd` while active.
|
||||
Enforces a sandbox rooted at the cwd shown in the status bar. `read`, `write`, and `edit` resolve their target path (including through symlinks) and refuse anything outside the sandbox. `bash` refuses obvious escape patterns: `sudo`, `rm -rf /`, leading `cd /`, `cd ..`, `cd ~`, `chmod -R`, `dd of=/`, and similar. The status bar shows `jailed, ~/your/cwd` while active.
|
||||
|
||||
This is a guardrail against accidents, not a hard security boundary. If you need real isolation, run zot under docker or a proper sandbox.
|
||||
|
||||
|
|
@ -273,7 +273,8 @@ Frames containing images are full-repainted (no differential diff) to prevent st
|
|||
|
||||
You can keep typing while the agent is working. Pressing `enter` during a turn queues the message instead of interrupting: it shows up above the status bar as `sliding in: <text>` and is delivered as the next user turn the moment the current one finishes. Queue as many as you want; they run in order. `esc` or `ctrl+c` cancels the active turn and drops the queue so a runaway turn doesn't flood you with stale follow-ups.
|
||||
|
||||
Slash commands also work while the agent is busy. Read-only ones (`/help`, `/jump`, `/btw`, `/sessions`, `/skills`, `/lock`, `/unlock`, `/exit`) take effect immediately. Destructive ones (`/clear`, `/compact`, `/login`, `/logout`, `/model`, `/reload-ext`) cancel the active turn first and then run.
|
||||
Slash commands also work while the agent is busy. Read-only ones (`/help`, `/jump`, `/btw`, `/sessions`, `/skills`, `/jail`, `/unjail`, `/exit`) take effect immediately. Destructive ones (`/clear`, `/compact`, `/login`, `/logout`, `/model`, `/reload-ext`) cancel the active turn first and then run.
|
||||
|
||||
|
||||
## Keys (interactive mode)
|
||||
|
||||
|
|
|
|||
|
|
@ -301,7 +301,7 @@ func (r Resolved) NewClient() provider.Client {
|
|||
}
|
||||
|
||||
// UseSandbox replaces the sandbox pointer that every tool in r's
|
||||
// registry references. Used to keep the /lock state stable across
|
||||
// registry references. Used to keep the /jail state stable across
|
||||
// agent rebuilds (e.g. /login, /model switching providers).
|
||||
func (r *Resolved) UseSandbox(s *tools.Sandbox) {
|
||||
if s == nil || r == nil {
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ type InteractiveConfig struct {
|
|||
// chat. Nil channel = no banner, no startup cost.
|
||||
UpdateInfoChan <-chan UpdateInfo
|
||||
|
||||
// Sandbox is the shared sandbox pointer. Toggled by /lock and /unlock.
|
||||
// Sandbox is the shared sandbox pointer. Toggled by /jail and /unjail.
|
||||
Sandbox *tools.Sandbox
|
||||
|
||||
// LoadSession swaps the current session for the one at path. The
|
||||
|
|
@ -1052,7 +1052,7 @@ func (i *Interactive) handleKey(ctx context.Context, k tui.Key) (done bool) {
|
|||
// /compact, /logout, /login, /model) cancel the active turn
|
||||
// first and wait for the goroutine to wind down so they don't
|
||||
// race with a streaming response. Safe commands (/help,
|
||||
// /jump, /sessions, /lock, /unlock, /exit) run immediately
|
||||
// /jump, /sessions, /jail, /unjail, /exit) run immediately
|
||||
// without disturbing the active turn.
|
||||
if slashCancelsTurn(head) {
|
||||
i.cancelAndWaitForIdle()
|
||||
|
|
@ -1238,7 +1238,7 @@ func (i *Interactive) runSlash(ctx context.Context, cmd string) (done bool) {
|
|||
i.openSkillsDialog()
|
||||
case "/compact":
|
||||
i.runCompact(ctx, false)
|
||||
case "/lock":
|
||||
case "/jail":
|
||||
if i.cfg.Sandbox == nil {
|
||||
i.mu.Lock()
|
||||
i.statusErr = "sandbox not available in this build"
|
||||
|
|
@ -1247,10 +1247,10 @@ func (i *Interactive) runSlash(ctx context.Context, cmd string) (done bool) {
|
|||
}
|
||||
i.cfg.Sandbox.Lock()
|
||||
i.mu.Lock()
|
||||
i.statusOK = "locked to " + i.cfg.CWD + " (tools cannot touch paths outside this directory)"
|
||||
i.statusOK = "jailed to " + i.cfg.CWD + " (tools cannot touch paths outside this directory)"
|
||||
i.statusErr = ""
|
||||
i.mu.Unlock()
|
||||
case "/unlock":
|
||||
case "/unjail":
|
||||
if i.cfg.Sandbox == nil {
|
||||
i.mu.Lock()
|
||||
i.statusErr = "sandbox not available in this build"
|
||||
|
|
@ -1259,7 +1259,7 @@ func (i *Interactive) runSlash(ctx context.Context, cmd string) (done bool) {
|
|||
}
|
||||
i.cfg.Sandbox.Unlock()
|
||||
i.mu.Lock()
|
||||
i.statusOK = "unlocked"
|
||||
i.statusOK = "unjailed"
|
||||
i.statusErr = ""
|
||||
i.mu.Unlock()
|
||||
case "/reload-ext":
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@ var slashCatalog = []slashCommand{
|
|||
{Name: "/btw", Desc: "side-chat that doesn't add to the main thread (saves tokens)"},
|
||||
{Name: "/skills", Desc: "list discovered skills (SKILL.md files)"},
|
||||
{Name: "/compact", Desc: "summarize and replace the transcript to free up context"},
|
||||
{Name: "/lock", Desc: "confine tools to the current directory"},
|
||||
{Name: "/unlock", Desc: "allow tools to touch paths outside this directory"},
|
||||
{Name: "/jail", Desc: "confine tools to the current directory"},
|
||||
{Name: "/unjail", Desc: "allow tools to touch paths outside this directory"},
|
||||
{Name: "/reload-ext", Desc: "hot-reload all extensions (re-read manifests and respawn)"},
|
||||
{Name: "/yolo", Desc: "turn off --no-yolo confirmation for the rest of this session"},
|
||||
{Name: "/clear", Desc: "clear the chat transcript"},
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ const (
|
|||
// ReadTool reads file contents from disk.
|
||||
type ReadTool struct {
|
||||
CWD string
|
||||
Sandbox *Sandbox // when locked, confines reads to the sandbox root
|
||||
Sandbox *Sandbox // when jailed, confines reads to the sandbox root
|
||||
}
|
||||
|
||||
type readArgs struct {
|
||||
|
|
|
|||
|
|
@ -51,13 +51,13 @@ func (s *Sandbox) CheckPath(path string) error {
|
|||
return fmt.Errorf("sandbox path: %w", err)
|
||||
}
|
||||
if !isUnder(rootAbs, target) {
|
||||
return fmt.Errorf("locked: path %q is outside sandbox root %q (use /unlock to disable)", path, s.Root)
|
||||
return fmt.Errorf("jailed: path %q is outside sandbox root %q (use /unjail to disable)", path, s.Root)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckCommand applies a lightweight sanity check to a bash command
|
||||
// when locked. We cannot fully sandbox a shell, but we can reject the
|
||||
// when jailed. We cannot fully sandbox a shell, but we can reject the
|
||||
// most obvious escapes so the model does not accidentally touch files
|
||||
// outside root via absolute paths.
|
||||
func (s *Sandbox) CheckCommand(cmd string) error {
|
||||
|
|
@ -78,7 +78,7 @@ func (s *Sandbox) CheckCommand(cmd string) error {
|
|||
lower := strings.ToLower(cmd)
|
||||
for _, b := range banned {
|
||||
if strings.Contains(lower, strings.ToLower(b)) {
|
||||
return fmt.Errorf("locked: command contains banned pattern %q (use /unlock to disable)", b)
|
||||
return fmt.Errorf("jailed: command contains banned pattern %q (use /unjail to disable)", b)
|
||||
}
|
||||
}
|
||||
// Heuristic: reject a leading `cd /` or `cd ~` that tries to move
|
||||
|
|
@ -89,7 +89,7 @@ func (s *Sandbox) CheckCommand(cmd string) error {
|
|||
first = strings.TrimSpace(strings.SplitN(first, "&&", 2)[0])
|
||||
if strings.HasPrefix(first, "cd /") || strings.HasPrefix(first, "cd ~") ||
|
||||
strings.HasPrefix(first, "cd $HOME") || strings.HasPrefix(first, "cd ..") {
|
||||
return fmt.Errorf("locked: cd outside sandbox root is not allowed (use /unlock to disable)")
|
||||
return fmt.Errorf("jailed: cd outside sandbox root is not allowed (use /unjail to disable)")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1085,7 +1085,7 @@ func StatusBar(p StatusBarParams) []string {
|
|||
|
||||
cwd := shortenHome(p.CWD)
|
||||
if p.Locked && cwd != "" {
|
||||
cwd = "· locked · " + cwd
|
||||
cwd = "· jailed · " + cwd
|
||||
}
|
||||
|
||||
primary := leftBuilder.String()
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ type Config struct {
|
|||
// NoTools disables every tool. Useful for chat-only embeddings.
|
||||
NoTools bool
|
||||
|
||||
// Lock confines tools to CWD. Same effect as the /lock command.
|
||||
// Lock confines tools to CWD. Same effect as the /jail command.
|
||||
Lock bool
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue