14 KiB
Public foreground Shell admission for Hosted Workspace Sessions
Status and problem
Implementation design for #13271, based on main bb66a52c. The Harness already
supports persisted hosted-workspace-shell/1 and mandatory Shell approval.
Public REST and WebShell creation still pin hosted-workspace-files/1; callers
cannot opt into the foreground Shell path. This change exposes that existing
path through deployment admission, without completing G3 Step 3 or Shell L4.
Scope and decisions
The deployment flag qwen.managed-agent.harness.workspace-shell-enabled
(QWEN_MANAGED_AGENT_WORKSPACE_SHELL_ENABLED) defaults to false. Enabling it
requires Workspace files and all their existing trusted local-process,
Session-isolated Broker, mount, Session Store and Harness prerequisites.
Approval mode must be default or auto-edit; yolo, missing, blank and
unknown modes fail startup. This deployment mode is persisted for built-in
qwen-code Workspace Sessions.
New Workspace-bound Sessions of the built-in qwen-code agent, including empty
creation and creation with input, pin hosted-workspace-shell/1 when enabled,
otherwise files/1. Unbound Sessions
and existing files Sessions retain their behavior. No caller profile selector,
metadata override, schema migration, public /2 search profile, background Shell,
Monitor or new credential mechanism is added.
Stored AgentDefinition Sessions retain the profile and approval mode compiled
from their pinned revision and the deployment allowlist. The Shell flag changes
only the built-in agent's default profile selection; it does not override a
stored definition. Every Shell Session still requires the Shell flag and valid
asking approval for fresh execution. Harness load requests retain both the
AgentDefinition identity and suppressChildAgents through child-Workspace,
lifecycle and stopped-message copies.
The inherited Shell/1 declaration includes Monitor, but public creation does not configure capture bytes or the H3 publication lane. Existing admission rejects Monitor and background commands before approval or Broker execution. Real public tests exercise both refusals and assert zero side effects; the private H3 tool family is unchanged.
Public Shell create and load set suppressChildAgents. The Harness then does
not attach the child-agent orchestrator, so the public tool list stays the
file tools plus foreground run_shell_command and Monitor. Session messages
share that orchestrator's write chain, so the same lane has no message funnel.
A Shell lane that omits the flag keeps the child agent tool.
PR #13674 also separates the existing internal TeamLead rig with the reserved
hosted-workspace-team-lead/1 profile and a trusted Java-only empty-creation
entry. Public create and AgentDefinition cannot select this identity, including
through idempotency replay. These public Shell rules still apply only to
the unified Shell/1 profile. Authorized input, Actions and protocol-0 close on an internally
created Session retain their private policy; opening public foreground Shell
does not expose team tools. See the internal admission design
for the persisted boundary and pending Linux/MySQL process acceptance.
Admission and persistence
Profile and approval mode are written in the existing creation transaction. They are server decisions, not inputs to the client request digest. Creation replay returns the original Session across a deployment flag change.
The V53 actor-role storage merged from main fbde5cf0 initializes the Session's
creator and owner from the same authenticated actor key in that transaction,
for Shell and files creation. Shell profile selection preserves both identity
columns and the upstream role-based grants; this slice adds no handover or
role-management operation.
With Shell disabled, existing Shell Sessions reject fresh Turns with
409 workspace_unavailable. Authorization still runs first. Both the service
replay probe and the locked Store command probe precede the new Shell gate;
original idempotency keys replay without accepting new work. The Store gate
is authoritative. Disabling only the Shell flag preserves accepted work,
approval decisions, cancellation and settlement while Workspace files remain
enabled and persisted approval remains default or auto-edit. Files-off or
invalid approval can refuse attachment; the shared Connector does not bypass
those guards. Controlled W2 cwd changes do not execute Shell and retain their
own files, actor, path and context-revision admission. The Shell flag is not a
general Session freeze.
Persisted approval and recovery
Before returning a cached attachment or making a create/load/recovery request,
the Connector validates that a persisted Shell profile has default or
auto-edit approval mode. Invalid modes never silently become YOLO. Harness
confirmation still must match the stored mode. Existing file YOLO behavior is
unchanged. The unreleased Shell /2 identifier has been removed; public
creation emits only the unified /1 profile.
Recovery reuses current G3 behavior. Requested approvals remain requested on a
plain owner reattach. Durable allow/deny remains durable. Recovery may refuse
at model_start or shell_in_flight with the existing recovery-blocked reason;
no Shell is automatically replayed and no physical Workspace fence is cleared
without verified recovery. This change does not promise full owner takeover.
Capabilities and lifecycle
Add optional public foreground_shell and WebShell foregroundShell. Files
and unbound responses omit them, without a schema default. Stored Shell Sessions return true only when
mandatory approval and deployment admission are valid; disabled Shell Sessions
return false. This explicit false is necessary to distinguish an existing Shell
Session from a files Session without exposing a caller-selectable profile.
workspaceTurns expresses the OPERATOR submit and rename capability under the
Session's creator-keyed execution facts. It can remain true when Shell
admission is off and is not sufficient Turn admission. Shell fresh sending
also requires foregroundShell=true. Disabling only Shell admission retains
accepted cancellation under the prerequisites above; false caused by files-off
or invalid approval does not guarantee cancellation. The actions capability
likewise does not override attachment or response guards. Cancellation admits
an OPERATOR caller on the Session's executable shape and remains independent
of fresh-creation grants. Its dedicated cached attachment path performs the
same persisted Shell approval validation. Older clients remain protected by
the transactional admission gate.
The canonical Shell profile uses the existing lifecycle v2 path, advertised by default. An explicit QWEN_HOSTED_LIFECYCLE_V2_PROFILES value replaces the list; an empty value disables v2. Public close and active delete additionally require reliable original-Worker stop support, Session ownership and current read access. A completed close enables archive/unarchive; unarchive restores CLOSED. Foreground work and pending approvals return turn_active: cancel the original Turn and wait for settlement before requesting close again. Background Shell, Monitor and child Sessions use the existing drain and cascade. Missing execution, output or stop proof remains blocked for recovery. Turn cancellation and Session lifecycle retain their separate authorization and recovery guards. See the current lifecycle design.
Changes by component
| Component | Change |
|---|---|
| ManagedAgentProperties and application.yml | Default-off flag and startup prerequisites |
| ManagedAgentStore / AgentStateStore | Creation profile, fresh Turn gate, lifecycle exclusion, flag accessor |
| ManagedAgentService | Persisted-profile capability projection and Shell lifecycle exclusion |
| QwenHostedHarnessConnector | Mandatory approval checks before cache or RPC use |
| ApiModels / OpenAPI / generated WebShell API | Additive optional Shell capability |
| Java WebShell provider | Separate fresh send from accepted cancellation |
| Unit and Hosted public integration tests | Admission, replay, approval, lifecycle and real-process coverage |
Validation and acceptance
Configuration tests cover default-off, missing prerequisites and all approval modes. SQL-backed admission tests cover both surfaces, empty/initial creation, frozen metadata and profile, flag changes, replay, fresh rejection, ACLs and zero lifecycle operations. Empty and initial Shell creation in both approval modes, plus flag-off files creation, must persist the exact actor key in both creator and owner columns. Connector tests exercise create/load/cached/recovery validation with zero Harness calls for invalid modes and file YOLO controls. WebShell adapter tests prove disabled sending retains active cancellation. Both wire surfaces pin explicit false for flag-off Shell and omission for files and unbound Sessions. Lifecycle negatives use close-capable Runtime support and a completed files close receipt before switching the stored profile to Shell, so a missing Shell exclusion cannot pass through an unavailable Runtime fixture. Connector coverage includes cold and cached action responses with Shell disabled and rejects invalid persisted approval before any Harness call. Cancellation asserts its durable CANCELLING state and coordinator dispatch. A requested Action on a bound Shell/default Session with files enabled and Shell disabled must commit a matching decision receipt, complete the response operation and replay across surfaces after a lost answer with one delivery. This H2/MockMvc test uses a simulated Harness decision and does not prove real Shell execution or a deployment restart.
Defensive projection tests cover invalid non-null persisted approval and a manually constructed files-off store; SQL forbids null approval, and a validated deployment forbids Shell-on/files-off. Synthetic persisted Shell/2 rows exercise the existing exclusion predicate, without adding public Shell/2 creation. Lifecycle projections also retain the close-capable positive control with Shell enabled. Cached stream/rename reject invalid approval before Harness access; Shell/1 retains the non-executing cwd admission and replay contract.
The public integration fixture must fail immediately on one-shot Action response assertions. Receipt evidence waits for the attached Harness prompt to become inactive and recovery-blocked, and for all three receipt handlers to finish, before checking exact attempts and asynchronous failure channels. Public prefix checks retain their first failure and rethrow it. Allow stdout must reach the model, and Allow/Deny side effects are rechecked after replay. Teardown runs all cleanup steps even when the output probe fails, retaining the original failure.
Real Java/Broker/Session Store/Harness tests must prove public Shell Allow has
one side effect, Deny has none, and lost response/retry does not execute twice.
Cold attachment must preserve profile and approval. FG6f publisher/receipt
failure tests require real worker and SQL evidence; macOS substitutes or H2
cannot certify Linux physical quiescence. Build, bundle, typecheck, focused
package tests, Java packaging and Checkstyle, two clean self-audits and an
independent review precede completion. Results live in
.qwen/e2e-tests/public-hosted-shell-admission.md.
Historical validation at 9c5e2817, after merging main bb66a52c, passed 181 focused Java tests, 30
WebShell adapter tests, 341 CLI Harness/recovery tests and six real
MySQL/Broker/Harness public integration cases. The receipt failure and
committed-receipt/lost-reply cases each retain one dispatch and one side effect,
with byte-identical receipt retries and verified capture resources. An independent
flag-off/cold probe verifies Store/Service admission and the live Connector,
including accepted approval/cancellation and a new Harness after natural writer
lease expiry. This probe does not restart the complete Java deployment. Linux
publisher/worker-kill physical gates remain unverified and keep rollout gated.
Rollout, limits and open questions
Keep the flag off until the deployment has evidence for #12904, #13010 and the
public FG6f gates. This feature does not fix those independent issues, add full
Shell lifecycle, or bypass a recovery refusal. Deploy all Session readers before
creating Shell Sessions; an older binary must not reinterpret the stored
profile as files. Upgrade the Harness before enabling the flag. An older
Harness ignores suppressChildAgents and would advertise the child agent
tool. Shell Action responses admit only the Session's recorded owner, not
every Workspace operator, because the answer runs a command. The implementation choices for this slice are set; maintainer
review of the public contract and rollout sequencing remains required. Missing
physical test infrastructure is reported as an unverified gate, never a pass.
W1c offline Workspace migration remains files/1-only. A persisted Shell
definition causes migration_profile_unsupported for its storage, so plan any
W1c migration before creating Shell Sessions there. This slice does not widen
the migration guard or add Shell migration support.