A terminal panel beside an agent chat
A real shell on the chat's own connection, opened and closed like the inspector and never beside it. The modes govern the model; what a person types is theirs, since they hold the credential and could open the same shell with an ssh client. The model cannot see the panel -- a button copies the output you choose into the composer. The session outlives the socket: closing the panel leaves a build running, and coming back reattaches with the scrollback. Two tabs share one shell and the smaller window decides the size. It ends on an idle timeout, on deleting the chat, on disabling, moving or deleting the connection, and on a restart -- which says why rather than quietly opening a fresh shell that has lost the working directory. The nginx template's `Connection ""` is right for SSE and fails every WebSocket handshake, so `location /` now uses a `map $http_upgrade`; update.sh grows a drift check for it, because the only symptom on a stale vhost is a panel that cannot connect. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -75,6 +75,19 @@ def _agents_defaults() -> dict[str, Any]:
|
||||
"allow_default": ["file_read", "file_list", "ls *", "pwd", "git status"],
|
||||
"deny_default": ["shutdown *", "reboot *", "mkfs*"],
|
||||
"ask_free_text": True,
|
||||
# The terminal panel: a person's own shell on their own connection.
|
||||
# Separate from `enabled` because the two are different capabilities --
|
||||
# one lets a model run commands, the other lets a human do what they
|
||||
# could already do with an ssh client. Neither implies the other.
|
||||
"terminal_enabled": True,
|
||||
# Seconds with nobody watching *and* nothing typed before the session is
|
||||
# closed. A build running with the panel shut is not idle. Clamped on
|
||||
# read: zero would leave a shell open until the next restart.
|
||||
"terminal_idle_timeout": 1800,
|
||||
# Open shells across the instance, and per person. Each is a PTY and an
|
||||
# SSH connection held open, so this is a real resource, not a scruple.
|
||||
"terminal_max_sessions": 20,
|
||||
"terminal_max_per_user": 3,
|
||||
}
|
||||
|
||||
|
||||
@@ -213,14 +226,22 @@ def search(db: DBSession) -> dict[str, Any]:
|
||||
|
||||
|
||||
def agents(db: DBSession) -> dict[str, Any]:
|
||||
"""Agent settings, with the two numbers that must not be zero clamped.
|
||||
"""Agent settings, with the numbers that must not be zero clamped.
|
||||
|
||||
`approval_timeout` of 0 would park a background task on a question nobody
|
||||
is going to answer, and nothing else prunes a generation that is not
|
||||
finished. Clamped on read rather than on save, so a value already stored by
|
||||
an earlier version cannot bite either.
|
||||
finished. `terminal_idle_timeout` of 0 would keep a PTY and an SSH
|
||||
connection open until the next restart. Clamped on read rather than on save,
|
||||
so a value already stored by an earlier version cannot bite either.
|
||||
"""
|
||||
values = get_group(db, AGENTS)
|
||||
values["approval_timeout"] = min(max(int(values.get("approval_timeout") or 0), 60), 3600)
|
||||
values["max_timeout"] = min(max(int(values.get("max_timeout") or 0), 1), 3600)
|
||||
values["terminal_idle_timeout"] = min(
|
||||
max(int(values.get("terminal_idle_timeout") or 0), 60), 86400
|
||||
)
|
||||
values["terminal_max_sessions"] = min(
|
||||
max(int(values.get("terminal_max_sessions") or 0), 1), 500
|
||||
)
|
||||
values["terminal_max_per_user"] = min(max(int(values.get("terminal_max_per_user") or 0), 1), 50)
|
||||
return values
|
||||
|
||||
Reference in New Issue
Block a user