Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,698 changes: 1,407 additions & 291 deletions apps/controller/src/console/agents/sandbox-policy.mts

Large diffs are not rendered by default.

836 changes: 791 additions & 45 deletions apps/controller/src/console/console.css

Large diffs are not rendered by default.

53 changes: 47 additions & 6 deletions docs/flows/agent-sandbox-policy.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
created: 2026-10-15
updated: 2026-10-25
last_updated_session: authoring-run/b7994562-b460-4667-9017-4c62c3f56eb0
updated: "2026-10-26"
last_updated_session: "authoring-run/0f3c367f-75d3-40f0-bcfa-3b649b78721f"
---

# Agent sandbox policy flow
Expand Down Expand Up @@ -150,9 +150,33 @@ renders schema fields and JSON fallback, with safe field/root diagnostics.
String controls retain the complete draft. Length validation stays with the
policy owner: JSON Schema counts Unicode code points, while HTML length
attributes count UTF-16 code units and can truncate a valid executable path.
`console/agents/sandbox-policy.mts:renderPolicy` keeps proposed rules local until
preview. Review shows the rules the controller validated, and Save sends those
rules with the reviewed policy version. Superseded preview and read responses
`console/agents/sandbox-policy.mts:renderLogs` snapshots the displayed log window,
filters it, and renders five denials per page initially. Selection spans pages;
Select page changes only eligible visible rows. Follow retains up to 1,000 new
log records without replacing inspected rows. Show newest accepts the retained
window and removes selections for records no longer retained.

`stageGrants` builds exact executable/host/port candidates, deduplicates requests,
and skips requests already covered by saved or proposed grants. It retains the
complete additions array and opens the schema editor or batch preview. Missing
metadata disables prefilling. An incomplete existing draft must be corrected
before staging more grants. `ruleList` searches and pages explicit rules five at
a time; row controls edit or remove local additions.

`console/agents/sandbox-policy.mts:renderPolicy` places policy status and suggestions
above the log. It shows three suggestions initially and expands the remaining
candidates on request, independently of mutation authority. `openNetworkLimits`
uses the policy view's ceiling in a separate read-only dialog. Defaults and
version details stay in the main-page disclosure. The Network policy card also enables or disables Agent policy editing
through the separately authorized editing operation. Mutation feedback appears in
the card while the dialog is closed; a failed policy read retains its error and
read recovery action. `renderEditor` manages rules without an editing toggle and
keeps proposed rules local until preview. Its footer compares the draft with saved
additions, including removals and restored values, to offer review, application,
or Done. Saving a confirmed response closes the dialog and clears the completed
selection. Review shows additions, updates, and removals from the controller-validated rules,
with the previously saved policy available for comparison. Save sends the complete
validated array with the reviewed policy version. Superseded preview and read responses
cannot overwrite a newer draft. Policy reads use a separate sequence: an earlier response
cannot invalidate a newer request. A pending application re-arms polling after
the latest read, including a response discarded after a draft edit.
Expand All @@ -164,7 +188,10 @@ unconfirmed save, only an explicit refresh started after the outcome can reopen
Save alone leaves running enforcement unchanged. Applying a
previously saved policy is disabled while local edits are unsaved. If application
fails after saving, the alert identifies the saved version rather than implying
that the entire operation was rejected.
that the entire operation was rejected. `loadPolicy(true)` records explicit
inspection of a failed application. Refresh issues only the policy GET; a separate
Retry application action invokes the existing apply descriptor after inspection.
The action remains subject to current permissions, lifecycle, and draft guards.

`console/agents/sandbox-policy.mts:mutate` displays rejected saves as focused
alerts and scrolls the reason into view after rendering. Ceiling rejection
Expand Down Expand Up @@ -194,6 +221,12 @@ the policy read, only the network policy card states it; the suggestions card is
- [PostgreSQL admission integration](../../tests/integration/postgres-agent-sandbox-policy.test.mjs)
exercises HTTP/IAM/storage; the [OpenShell runtime lane](../../tests/integration/sandbox-driver-openshell-k3d-real.test.mjs)
owns real enforcement proof. Storybook proves simulated presentation only.
- [Console batch browser tests](../../tests/browser/console-policy-denials.test.ts)
cover selection across pages, stable Follow, deduplication, preservation of saved
rules, and ceiling rejection through real controller routes. They use in-memory
storage and test Drivers with simulated log input. A simulated failed-application
view covers explicit inspection and retry against the real API refusal; neither
case proves provider enforcement.

## Related docs

Expand All @@ -206,6 +239,8 @@ the policy read, only the network policy card states it; the suggestions card is

## Changelog

- 2026-10-26 10:49: Combine the accompanying policy UX controls with upstream permission hints, unavailable Sandbox log handling, and application failure explanations. (authoring-run/0f3c367f-75d3-40f0-bcfa-3b649b78721f - e89a4e37a0b3dff75739cf6988028f81cd96f5ac)

- 2026-10-25: Hide the empty denial rows and state a refused policy read once. (finding 2522)
- 2026-10-25: Hide the denial window's controls on the draft. (finding 2509)
- 2026-10-25: Skip the policy and suggestion reads that the permission answer refuses. (finding 2454)
Expand All @@ -216,6 +251,12 @@ the policy read, only the network policy card states it; the suggestions card is

- 2026-10-22 23:45: Preserve CLI output guidance alongside upstream grant admission rules after conflict resolution. (authoring-run/01a10a81-070c-7aa3-97d0-fbce8f285d4d - c574fc743b21599c51d3809365039010a7e077d2)

- 2026-10-23 16:20: Trace the accompanying compact policy controls, suggestion disclosure, independent ceiling dialog, contextual footer, and explicit retry after status inspection. (authoring-run/2d64e0f2-543e-4fee-bc9d-0ed8f2d48f5b - a8575405cf9fbd1779b2daa1fab96a88d584cc7c)

- 2026-10-23 14:55: Trace the accompanying move of the policy-editing control to the page, visible mutation feedback, and read recovery. (authoring-run/71e6c3c8-7b57-42ff-bc6c-37f2506f2da5 - a8575405cf9fbd1779b2daa1fab96a88d584cc7c)

- 2026-10-22 16:59: Trace the accompanying console changes for paged denials, batch proposals, rule management, and review before saving. (authoring-run/cd2ce6a3-4507-46cc-b936-68c9862c794b - 0c73a86a6cdd92f0129c8e52e0f230517490bd46)

- 2026-10-22 13:40: Run the shared grant rules before a Driver contribution, at every admission (preview, save, apply, deploy) and the worker recheck; empty or reversed ranges never satisfy the ceiling. (bughunt-1022c)

- 2026-10-21 04:25: Declare structured policy CLI output before requests. (authoring-run/a66234ce-95fd-4d64-9fe8-3ce3d143e572 - e8185dac805b737363c7178d3068b310a53a7c24)
Expand Down
65 changes: 41 additions & 24 deletions docs/guides/topics/agent-sandbox-policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,47 @@ observed denials. A version that runs without a Sandbox records none, and the ta
says so. Policy reads and changes have independent permissions; see the
[policy reference](../../reference/sandbox-policies.md).

1. Read **Denials in the loaded window** and any gap notices. Expand a row for
decision metadata and its sanitized log. Missing binary, destination, reason,
or generation information is shown as unknown.
2. If available, inspect **Policy suggestions** separately. Their observation
counts are aggregated provider data. Choose **Review suggestion**, or enter a
grant manually under **Agent network policy**. A denial with an exact path and
destination can also prefill a grant.
3. In the discovered configuration section, use **Add Network rules** and
**Remove Network rules** to edit additions. These changes stay local. Review the executable path, hostname, and complete
port scope against the **Installation delegation ceiling**, then select
**Review changes**. The preview shows field errors without saving or applying.
Review the rules the controller validated. If the Console cannot read them,
your proposal stays editable. **Back to editing** also preserves your proposal.
4. Choose **Save** to persist the policy without changing the current revision,
or **Save and apply to current revision** to save and request live application
to the selected running revision. Wait for `applied` and a provider version
before retrying the original tool action. A successful tool result provides
the final execution evidence.
If the Console cannot confirm which rules were saved, select **Refresh policy**
and inspect the saved version before deciding whether to apply it.
5. To apply a policy saved earlier, select **Apply saved policy to current
revision**. This control is unavailable while your proposal has unsaved edits.
When live application is unsupported, use **Deploy new version** explicitly.
Deployment requires deploy permission and can interrupt the Agent.
1. Check the **Network policy** card above the log. **Policy details** shows saved
and confirmed live versions and read-only Installation defaults. **View network
limits** opens the Installation ceiling, which grants no access by itself.
Suggestions are untrusted candidates. The card shows three at a time; **Show all**
expands the list. Expand **Full executable path** before reviewing a suggestion.
With editing off, **View suggestions** still lets you inspect candidates.
2. Read the denial count and any gap notices. The log starts with five rows per
page. Search the loaded window or use **Next** to inspect more records. Expand
a row for decision metadata and its sanitized log. Missing fields stay unknown.
**Follow** collects updates without moving the rows you are inspecting; select
**Show newest** to display them.
3. Choose **Allow this** for one denial, or select several and choose
**Allow selected**. **Select page** selects eligible rows on the visible page.
Selection carries across pages; the count includes selections outside the
current page. Exact executable, host, and port requests become proposed rules.
Repeated requests and requests covered by saved or proposed rules do not add
duplicate rules. Incomplete metadata cannot prefill a rule.
4. Administrators can use the **Policy editing** switch beside **Manage rules**.
Its **On** or **Off** label states the current setting. Disabling editing preserves
saved rules. **View rules** remains available with editing off. To change rules,
enable editing and choose **Manage rules**. Search or page through five rules at
a time. Use **Add rule** or a row's menu to edit or remove a rule. These changes
stay local. The dialog footer offers **Review changes** when the draft changes;
**Done** closes without saving and retains the proposal on this page.
5. Select **Review changes** after editing a rule. A batch opens this review
directly. Check the rules to add, update, or remove. The preview shows field
errors without saving or applying. **Back to editing** retains the proposal.
6. Choose **Save for later** to persist the policy without changing the current
revision, or **Save and apply to current revision** to save and request live
application. Wait for **Applied** before retrying the original tool action.
A successful tool result provides the final execution evidence.
If the Console cannot confirm which rules were saved, open **Manage rules**,
select **Refresh policy**, and inspect the saved version before applying it.
7. To apply a policy saved earlier, choose **Apply saved rules** on the card or
in the **Manage rules** footer. This action is unavailable while the proposal
has unsaved edits. **Applying…** waits for provider confirmation. If application
is unconfirmed, use **Refresh status** to read the outcome without submitting
another request. If the refreshed application still failed, **Retry application**
offers a separate explicit request to authorized users. When live application
is unsupported, use **Deploy new version** explicitly. Deployment requires deploy
permission and can interrupt the Agent.

The schema supplies field descriptions and constraints. Shapes without a form
use a schema-aware JSON editor. A preview for an older draft or policy version
Expand Down
28 changes: 26 additions & 2 deletions docs/reference/sandbox-policies.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,34 @@ The console's **Policy denials** tab uses the existing bounded, exact-revision
Sandbox log path and its `read_logs`/administration rules. Typed metadata is
optional. Recognized OpenShell records are parsed by the Driver; OCC validates,
redacts, and applies the normal output and retention limits. Unknown fields stay
unknown. **Denials in the loaded window** counts retained records, not requests
or lifetime denials. Buffer gaps, withheld records, permission denial, and missing
unknown. The denial count describes retained records, not unique requests or
lifetime denials. The console retains up to 1,000 log records and initially shows
five denials per page, newest first. Search filters the loaded window. Follow
collects new records without moving inspected rows; **Show newest** displays them. Buffer gaps, withheld records, permission denial, and missing
metadata remain visible. An empty successful read says **No denials in this window.**

The **Network policy** card precedes the denial log and distinguishes saved
policy from confirmed live application. **Allow this** prefills one exact rule;
**Allow selected** stages eligible denials selected across pages. **Select page**
selects only the visible page. Repeated executable/host/port requests and requests
covered by existing rules do not create additional rules. The complete proposed
policy preserves existing rules and passes the normal preview, save, and apply
admission. Selecting denials grants no access until the user reviews and saves.
**Manage rules** searches and pages explicit rules, five at a time, with separate
controls to edit or remove a rule. Installation defaults stay read-only.
The **Policy editing** switch sits beside **Manage rules**. With editing off,
**View rules**, suggestions, and **View network limits** remain available for
inspection; mutation controls are hidden. The ceiling has a separate read-only
dialog. **Policy details** on the page shows versions and Installation defaults.
Suggestions initially show three entries, with an explicit action to show the rest.
Their executable basenames have expandable full paths. Bulk review shows selected
record and new-rule counts, repeated requests, and already-covered requests.
The rule dialog footer offers review for unsaved changes, application for saved
changes, and **Done** when no action remains. Save feedback distinguishes saved,
pending, confirmed, and unconfirmed outcomes. **Refresh status** only reads;
**Retry application** requires an explicit refresh of the failed application and
rechecks application authority. Neither action starts a deployment.

Provider suggestions use a separate authorized and audited read. OpenShell's
`GetDraftPolicy` returns aggregated chunks, including its lifecycle and observation
count. Chunks can be observation-generated or agent-authored; origin is not
Expand Down
4 changes: 4 additions & 0 deletions scripts/ci/test-suites/checks-browser-2.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"tests/browser/console-agents.test.ts",
"tests/integration/ci-impact.test.mjs",
"tests/browser/console-agent-draft-guards.test.mjs",
"tests/browser/console-policy-denials.test.ts",
"tests/browser/console-agent-channels.test.mjs",
"tests/browser/console-dialogs.test.ts",
"tests/browser/console-form-fields.test.ts",
Expand Down Expand Up @@ -51,6 +52,9 @@
{
"path": "tests/browser/console-agent-draft-guards.test.mjs"
},
{
"path": "tests/browser/console-policy-denials.test.ts"
},
{
"path": "tests/browser/console-agent-state.test.mjs"
},
Expand Down
10 changes: 10 additions & 0 deletions scripts/console-storybook/pages-agent-detail.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,8 @@ export const BootstrapContainerStatusAndLogs = story("runtimeBootstrapContainers

export const PolicyDenials = story("policyDenials");
export const PolicyUnicodeExecutable = story("policyUnicodeExecutable");
export const PolicyDenialsMany = story("policyDenialsMany");
export const PolicyRulesPopulated = story("policyRulesPopulated");
export const PolicyDenialsEmpty = story("policyDenialsEmpty");
export const PolicyDenialsDisabled = story("policyDenialsDisabled");
export const PolicyDenialsUnsupported = story("policyDenialsUnsupported");
Expand Down Expand Up @@ -219,3 +221,11 @@ export const ConfigurationRefreshRace = story("policyConfigurationRefresh");
export const ConfigurationJsonFallback = story("policyConfigurationJson");

export const ConfigurationOwnerDenied = story("policyConfigurationHidden");

export const PolicySuggestionsExpanded = story("policySuggestionsExpanded");
export const PolicySavedForLater = story("policySavedForLater");
export const PolicyApplying = story("policyApplying");
export const PolicyApplied = story("policyApplied");
export const PolicyRetryApplication = story("policyRetryApplication");
export const PolicyEditingAdminDenied = story("policyEditingAdminDenied");
export const PolicyNoNetworkLimits = story("policyNoNetworkLimits");
Loading
Loading