Agent Presets flow
Overview
The console reads a Namespace-owned Preset, renders its variables, and saves an independent Configuration and Agent through the existing APIs. This flow starts with Preset CRUD or selection and stops at a saved Agent draft. Deployment continues through revision admission.
Entry Points
-
Installation loader:
loadInstallationConfigurationreadspresets.includeDefaultsandpresets.files. Bundled defaults are Standard Codex and Standard OpenClaw; custom DevDay files are loaded only when explicitly listed. Production and PostgreSQL development composition pass generic name/template definitions to OCC and callinitializeDefaultPresets. -
Source:
packages/contracts/src/api/routes.ts:occApiRoutes. -
Preset routes: authenticated collection and exact-resource operations in a Namespace.
-
OCC Preset methods:
createPreset,updatePreset,listPresets,getPreset, anddeletePresetown lifecycle. -
Console selector:
createPresetFieldsrequires a selected Namespace and an authenticated user. Saving also needs the existing Configuration, Agent, and credential grants.
Flow
graph TD
S0["API startup with defaults enabled"] --> S1["Authorize and lock eligible Namespaces"]
S1 --> S2["Create missing names; preserve existing copies"]
S2 --> C
N["Authorized Namespace creation"] --> S2
A["Operator writes Preset"] --> B["OCC authorizes and validates template"]
B --> C["Store Namespace-owned Preset"]
C --> D["Console reads selected Preset once"]
D --> E["User supplies variables and selects Use Preset"]
E --> F["Renderer copies launch settings"]
F --> G["Chooser closes; user edits and saves ordinary draft"]
G --> S["Password input: create Secret in current Namespace"]
S --> H["Configuration API admits and saves"]
G -->|Existing credential binding| H
H --> I["Agent API admits and saves"]
I -->|Model Secret selected| O["Grant Agent access to the selected model Secret"]
I -->|No model Secret| J
O --> J["Independent Agent draft"]
O -->|Grant fails| R["Retain Agent and retry credential access"]
F -->|Invalid variable| E
I -->|Agent save fails| K["Keep Configuration ID for safe retry"]
J --> L["Credential preparation and revision admission"]Execution Trace
Before serving requests, migration 0024 creates Preset storage and adds Preset CRUD grants to the unchanged built-in administrator Role. Its guarded update preserves customized Roles; the exact eligibility rules belong to the Preset contract.
1. Include configured defaults
apps/controller/src/composition/installation-config.ts:loadInstallationConfiguration
The loader validates the opt-in boolean and file list. It loads bundled JSON
when enabled, resolves explicit JSON paths beside the startup YAML, validates
each name/template definition, and rejects missing, malformed, invalid, or
duplicate-name definitions before composition. API and worker share the startup
snapshot and its source path; files are not watched. Production composition
and development composition
pass generic definitions into ControllerOptions.defaultPresets, select an
authorized persisted administrator through IAM, and initialize defaults after
selecting Configuration and IAM Drivers. Native template contents
remain in the application bundle; OCC owns generic Preset lifecycle. The
standard Codex artifact requests
on-request approvals with the user as reviewer, cached hosted search, and the exact build hosts in the
standard Preset guide.
Seeding and rendering copy that native policy; the deployed Codex plugin owns
its enforcement. Updating the bundle does not replace already installed copies.
packages/occ/src/index.ts:OpenClawController.initializeDefaultPresets
Initialization authorizes Installation administration, locks Namespaces in ID order in one transaction, and skips failed/deleting Namespaces. Missing names require Preset create permission and ordinary template/Driver validation before storage and mutation audit. Existing names are untouched. Any failure rolls back the transaction and prevents API startup. Namespace creation uses the same helper before queuing provisioning, so denied or invalid defaults also roll back the new Namespace. Disabling defaults leaves persisted copies alone.
2. Admit and store a template
packages/occ/src/index.ts:OpenClawController.createPreset
OpenClawController.createPreset and admitPresetTemplate
lock the Namespace, check the exact collection grant and ready state, then call
normalizePresetTemplate.
The normalizer copies the template and fills omitted namespaceId fields in
Harness authentication and Configuration Secret binding sources from the locked
Namespace. Explicit scopes remain unchanged for same-Namespace validation.
Template structure, variable declarations, default types, and credential
references are checked without requiring unfilled variables. Password definitions
have no defaults and can appear only as whole tokens at agent.harnessAuth.secret;
literal credentials and substitution into ordinary settings are rejected. Ordinary Agent
field validation is deferred to the creation APIs. When native values exist,
the selected Configuration Driver's
validateValues checks their native credential rules; missing capability fails
closed. Core owns no native configuration interpretation.
The repository stores the whole
template in occ.presets. PATCH locks the resource and replaces an included
template atomically. DELETE removes its exact IAM bindings in the transaction.
Presets prevent Namespace deletion while present. The controller's normal audit
path records mutations and denials without template or variable contents.
3. Read and render the selected copy
apps/controller/src/console/agents/presets.mjs:createPresetFields
createPresetFields
lists only readable Presets, then reads the selected resource once. The user
reviews prefilled scalar defaults and fills typed inputs. The bound password
variable offers a new masked token or an existing same-Namespace Secret. The
chooser fetches only Secret metadata, validates the original template, and replaces
the password token with the selected reference in a temporary copy. Mode changes
clear discarded tokens; stale catalog responses cannot replace a later selection.
The user then selects Use Preset. The shared
renderPresetTemplate
walks JSON once, rejects missing or mistyped inputs and duplicate rendered native
keys, and preserves runtime placeholders and unresolved SecretRefs.
Rendering makes no requests and fetches no credentials. On success, the chooser is replaced by the ordinary Agent form; the form keeps only the rendered settings and, when selected, ephemeral existing-Secret metadata for access grants. The chooser lists Presets alphabetically by display name. Password values move into the ordinary masked credential input; the chooser clears its detached password controls. Preset updates or deletion cannot alter them. Before saving, Start over discards the unsaved draft after confirmation and opens a fresh chooser. After a save succeeds or its outcome becomes uncertain, restart is disabled so the user follows ordinary creation recovery.
apps/controller/src/console/console.mjs:loadPage
Before resetting the view, Console captures the unsaved form's raw editor text,
model controls, workspace files, repository selections, and staged Secret
references. The in-memory map is scoped to the signed-in user and Namespace.
Returning to a Preset form through navigation or browser history reconstructs it
from that copy; capability and repository discovery run again against current
access. A form started without a Preset registers for discard on exit. After
flushing captures, loadPage removes its creation and channel snapshots and its
retained view when navigation leaves creation or changes Namespace. Re-entry
opens the initial choices; resources already saved through the API remain.
Invalid JSON survives as text. Password controls and plugin discovery results
are excluded. Start over removes the copy; session loss, logout, a different
signed-in user, and page exit clear the map. Starting a save removes its capture
before any mutation, so a later route return cannot replay a pre-save copy as a
new Agent. Existing partial-save recovery remains local to its form.
4. Save an independent draft
apps/controller/src/console/agents/create.mjs:renderCreateAgent
The creation form copies
rendered settings into editable fields and checks their form representation.
A method-only Preset authentication default selects API key or Service Accounts
without binding a Secret. The shared Secret picker requires a same-Namespace
selection. Create new Secret... saves immediately and stages the reference;
the browser never reads existing Secret bytes. Final Agent admission still
requires a complete authentication binding.
Rendered agent.pluginApprovers remains ordinary Agent draft data. Omission
inherits the form default, an empty array keeps the explicit no-approver default,
and selected channel identities are submitted through the normal Agent create
body. The Agent API and selected Plugin Driver validate the concrete approvers
after variable rendering.
Preset agent.initialWorkspaceFiles override matching workspace defaults,
including explicit empty strings. The shared Preset validator checks supported
filenames, Unicode, NUL, and byte limits before and after expansion; password
variables remain confined to the credential field. User-edited workspace bytes
follow the existing private workspace setup path in both regular and provisioning
creation. The form keeps Secret bindings internally and exposes channel-specific
Secret controls rather than a raw bindings editor.
Selected model Secret metadata and references survive draft navigation; raw
passwords do not. Provider or authentication-method changes clear the selection.
For an existing selection or a Secret reference already bound in the Preset,
Save uses the reference without creating another Secret. Ordinary creation grants
the new Agent's service principal exact Secret operate access and retains the
reference through Agent-conflict and grant retries. The caller needs permission to
manage the grant; if it fails, the saved Agent remains and the form offers a retry.
Provisioning derives the grant from harnessAuth.source.
For a password input, Save first creates a same-Namespace Secret, clears the
credential input, and retains the returned reference. It then creates a
Configuration and an Agent that refers to the Configuration and Secret, and
grants the Agent access. Dedicated provisioning uses the existing provisioning
flow after Secret creation. Password bytes are sent only to the Secret creation
endpoint, never as Agent or Configuration fields. Each server
request owns full schema, native credential, and authorization admission before
its persistence boundary; browser validation is not that boundary.
If Secret creation fails, the masked input remains for correction or retry. If a later save fails, its saved Secret reference is reused. If Configuration creation succeeds but Agent creation fails, the form retains the Configuration ID and locks Configuration-affecting controls. A safe retry reuses the saved Configuration. An uncertain response requires inspection before another creation attempt. See creation recovery.
5. Hand off to deployment
packages/occ/src/index.ts:OpenClawController.deployAgent
The saved Agent has a new identity and no Preset reference. Variable inputs are not stored as a separate map. Rendered values are ordinary Agent/Configuration settings. Credential preparation and revision admission read those resources, not the Preset. Later Preset changes cannot change a draft or an immutable admitted revision.
Debugging and Verification
- A missing selector entry can mean missing exact Preset
readpermission; compare the Namespace and the authenticated list response. - Chooser errors identify variable or form-field problems before any save. Save errors come from existing Configuration or Agent admission; retain request and saved Configuration IDs when investigating partial or uncertain outcomes.
- Controller integration coverage exercises the HTTP workflow, admission, isolation, and copy independence. PostgreSQL coverage exercises persistence; browser coverage exercises the real selection form. Coverage names are not proof of a live model response.
- The standard Preset's clean-build network trace covers source builds with cold dependency caches behind an enforcing HTTPS proxy. It establishes required destinations for those targets, not native Codex enforcement or cached-search model behavior.
- The local cluster attempt for this implementation stopped at a cgroup v2 startup failure. A Kubernetes deployment and real model response remain unverified; use the Kubernetes testing guide on a supported host for that proof.
