OpenClaw EnterpriseDOCSGitHub

Agent Presets flow

Overview

The Console renders a Namespace Preset and saves an independent Configuration and Agent through existing APIs. This flow stops at the draft; deployment follows version admission.

Entry Points

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"]
  D -->|Default quick-start without variables| F
  E --> F["Renderer copies launch settings"]
  F --> G["Chooser closes; user edits and saves ordinary draft"]
  U["User selects Start without Preset"] --> G
  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 version 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-presets.ts:loadInstallationPresets

The loader validates the opt-in boolean and file list, reads bundled JSON, and resolves explicit paths beside the startup YAML. It validates names/templates and rejects missing, malformed, invalid, or duplicate-name files; a file named like a bundled default replaces it, and API composition warns presets.bundled-default-shadowed. API and worker share the startup snapshot and source path (files are not watched), but only the API applies defaults and logs Preset warnings. Production composition and development composition pass generic definitions into ControllerOptions.defaultPresets and initialize defaults after selecting Configuration and IAM Drivers. They try each persisted Installation administrator in turn, moving on when one cannot create a Namespace's Presets (say, its grant stops at the Installation); startup fails only when none can seed. Native template contents remain in the application bundle; OCC owns generic Preset lifecycle. Seeding and rendering copy the native policy of the standard Codex artifact (build hosts); the deployed Codex plugin enforces it. loadBundledPresetVersions also loads every shipped version listed in deploy/presets/archive/versions.json, whether or not defaults are enabled, into ControllerOptions.bundledPresetVersions. A conformance test in tests/conformance/presets.test.ts fails when a bundled file changes without its previous version archived there.

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. A deny Restriction on a creation leaves the name missing with one presets.default-create-skipped warning; a missing grant still needs another administrator. While includeDefaults is enabled, an existing copy of a bundled default that still equals a superseded shipped version requires Preset update permission; its template is replaced in place and audited with source: installation-defaults-refresh. A refresh the policy refuses keeps the copy and logs one presets.default-refresh-skipped warning naming it and the reason. Startup first skips only refusals from a deny Restriction, which binds every administrator; only when no single administrator can then complete initialization does it skip every refused refresh. Removing the Restriction, or granting preset:update to an Installation administrator, lets the next startup refresh the copy. Other existing copies are untouched. Any other 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

apps/controller/src/index.ts:createFastifyApp gives Preset POST/PATCH a transport budget for 1 MiB templates, JSON escapes and the envelope; overflow returns 413. Their onRequest hook runs authorizePresetWrite: callers without the grant get 403 before any body is read.

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. validatePresetLaunchFields uses the shared tokenizer to distinguish declared, unescaped variable tokens from literal launch fields. Tokens wait for launch, including tokens with defaults; other text receives Agent create's rules. When native values exist, OCC and the selected Configuration Driver's validateValues check their credential rules. They defer a Gateway password only when the partial renderer identifies an unresolved whole-string variable at that exact field. The completed Configuration must contain an accepted credential reference. A missing Driver validation capability fails closed.

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.mts:createPresetFields

createPresetFields lists readable Preset summaries, requiring Namespace read, then reads the selection once. Start with default Preset reads the listed default-codex resource. Variable-free templates apply immediately; customized variables keep the chooser and its shortcut origin. Missing defaults or failed reads do not open a hardcoded starter; other readable Presets remain selectable. Start without Preset opens the form without a Preset read, not as a fallback for a denied selection. Normal creation authorization applies.

console-assets.ts:readConsoleAsset serves the public default-codex as /console/default-codex-preset.mjs. configurationTemplate uses this base for empty templates, reset and Harness transitions, adding model routing. Installed Namespace templates are never exposed by that module.

The chooser prefills typed defaults. String controls accept multiple lines; untouched defaults and restored drafts retain exact line endings, while edits use browser LF. Empty strings override defaults. Referenced variables without defaults are required; defaulted or unused variables are optional. The password variable offers a masked new token or an existing same-Namespace Secret. The chooser reads Secret metadata, validates the original template, then substitutes the selected reference in a temporary copy. Changing mode discards 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.

An older stored Preset can contain a non-null, non-text primary model that current Controller admission refuses. agents/create.mts:renderAgentForm rejects that selected primary before opening the form. The chooser explains that it must be text and keeps entered variables. Select another Preset or ask a Preset editor to fix the template. A null primary remains unset so the user can choose a model. Partial Presets and valid roster-only selections still open.

Rendering makes no requests. Success opens the Agent form with rendered settings and optional ephemeral Secret metadata for grants. The alphabetic chooser moves password values into the masked credential input and clears detached controls; later Preset changes cannot alter the form. Before saving, Start over confirms discard and opens a fresh chooser. A successful or uncertain save disables restart and uses ordinary creation recovery.

The form keeps the chooser state and the configuration.values path each variable filled. A Create refusal that starts Configuration setting <path> and covers such a path names those variables and offers Change with their labels, unless the submitted value at that path differs from the one the Preset rendered. While restart is enabled, that action reopens the chooser from the kept state, marks those fields with the server's sentence and focuses the first. The added text names variables, never their values. A quoted path segment, such as models["openai/gpt 5"], is one key. If the Preset then fails to load, focus moves to the chooser heading.

apps/controller/src/console/console.mjs:loadPage

loadPage captures JSON, model controls, workspace files, repositories and staged Secret references per user/Namespace before resetting views. Explicit Preset, default-shortcut and bare forms all return from this copy with fresh capability and repository discovery. A confirmed Cancel removes creation and channel snapshots and the retained view; re-entry opens the chooser and preserves saved resources. beforeunload asks first while any capture holds changes. Invalid JSON stays text; passwords and discovery results are excluded. Start over, session loss, logout, user change or page exit clears captures, not a failed /namespaces read. A known Create refusal keeps the capture and its saved resources, unless repository policy refused it or an Agent, provisioning job or credential registration exists.

agents/create.mts:updateModelConfiguration synchronizes qualified shared primaries and existing local policies. Empty maps and inherited selections remain. Invalid rosters stay for admission validation. JSON selection edits update controls and clear pending state. Clearing/navigation retain policies, fallbacks and referenced metadata.

4. Save an independent draft

apps/controller/src/console/agents/create.mts: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. The approver finder uses the staged Slack bot binding or a separately selected same-Namespace Secret, held only for search. OCC authorizes Agent creation and operate on the exact Secret per lookup. Results add workspace-qualified IDs. 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. Model Secret references survive draft navigation; passwords do not. Provider or authentication-method changes clear the selection. Existing or Preset-bound references are reused. Ordinary creation grants the Agent service principal exact Secret operate access; Agent-conflict and grant retries retain the reference. The caller needs grant-management permission. A failed grant keeps the saved Agent and offers retry. Provisioning derives it from harnessAuth.source. Password inputs first create a same-Namespace Secret, clear the input, and retain the reference. Save then creates Configuration and Agent resources and grants access; dedicated provisioning follows its existing flow. Password bytes reach only Secret creation. Each API request performs schema, native credential and authorization admission before persistence.

A confirmed duplicate-name Secret response offers the existing permission-aware picker or an independent new Secret name in the same form. Selection clears the entered key; neither path deletes or reads the existing value. Other draft fields remain. Other known refusals keep the masked input for correction or retry; uncertain writes clear it and lock creation. Agent-name conflicts do not enable Secret recovery. Credential-context changes clear the notice, alternate name, and required state. Returning to a password method does not revive that recovery. The notice and choices share a focused block under the model credential field. The notice belongs to the attempted Secret name. Changing that name or submitting a new attempt clears the old feedback without discarding an edited alternate name. Submit trims and validates alternate names before any write. Choosing an existing Secret restores the normal optional service-account plugin discovery token field. 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 version admission read those resources, not the Preset. Later Preset changes cannot change a draft or an immutable saved version.

Debugging and Verification

Search documentation