Presets & Shortcuts

Applies to: ChengOS v0.1.0+ | Last verified: 2026-08-12 | Source: deploy/config/presets.yaml, deploy/config/shortcuts.yaml, crates/cheng-core/src/domain/preset.rs, crates/cheng-core/src/domain/shortcut.rs, crates/cheng-api/src/services/presets.rs, crates/cheng-api/src/services/shortcuts.rs

Two YAML catalogs let you change a workflow's behaviour without editing the workflow:

PresetsShortcuts
ChangesA node's configurationThe routing of the next turn
Filepresets.yamlshortcuts.yaml
ScopeUntil changed againOne turn, or until cleared
ConsumerThe target nodeai/llm_branch

Both are merged from a global file and a per-workspace file, and both are cached with the file's mtime as the invalidation signal — edit the YAML and the change is picked up without a restart.

<global_dir>/presets.yaml                              ← shared by all workspaces
<workspace-root>/<workspace-id>/config/presets.yaml    ← per workspace, wins on id conflict

Per-workspace entries sharing an id with a global one replace it (case-insensitively); the rest are appended in file order.

Presets

A preset is a named, validated configuration patch for one node type. Selection is two-level: preset type, then preset.

preset_types:
  - id: tool_nodes
    label: Tool nodes
    description: Presets for tool nodes

presets:
  - id: code-safe-mode
    preset_type: tool_nodes
    node_type: tools/code
    label: Code execution safe mode
    description: Restrict file and shell behavior
    llm_visible: true
    risk_level: high
    config_patch:
      allow_write: false
      sandbox: strict
FieldMeaning
idStable, file-unique identity used in commands and approval payloads
preset_typeThe category id it belongs to
node_typeThe node type this patch is valid for — applying it elsewhere is rejected
labelShown in the selection list
descriptionHuman- and LLM-facing explanation of what it changes and when to use it
llm_visibleWhether scoped LLM discovery may surface it — default false
risk_levellow, medium, or high
config_patchJSON object shallow-merged into the target node's config

The patch is applied through the same validation path as a normal node update, so a preset cannot write a configuration the property panel would have rejected.

One source of truth

presets.yaml is the single store on every surface. The editor's property panel "save as preset" writes into it, and the chat preset selector, the CLI, and external channels read it back. There is deliberately no second browser-local preset store to reconcile.

A user-saved preset with no category lands in user_presets ("Saved presets"), created on demand, and defaults to llm_visible: false — a preset you saved for yourself is not automatically offered to an agent, because that would widen what an agent can change without review.

Because persisting one preset means rewriting the whole file, saves are serialised behind a process-local lock; otherwise two concurrent saves would race and the second would drop the first.

LLM discovery is deliberately narrow

An agent does not see the workspace catalog. A preset is discoverable only when it is llm_visible and its node_type is among the node types reachable as tools from the requesting node.

And an LLM may request a preset switch but never apply one. The request becomes an approval carrying the workspace, workflow, requester node, target node, preset id and label, a change_summary diff, and an optional reason. It rides the normal approval infrastructure — ~~y / ~~n, the CLI, web cards — under the action name preset_switch, so every surface can tell it apart from a tool-execution approval. See Approvals & Human Review.

Failure behaviour

Listing fails soft: a missing or malformed presets.yaml logs a warning and yields an empty catalog, so chat ingestion never crashes because of a bad workspace file.

Applying and saving fail hard, with typed errors: a save that did not happen must never look like it did.

Shortcuts

A shortcut attaches structured routing data to a turn:

shortcuts:
  - name: Need Tools
    description: Route to the tool-capable branch
    route_mode: always
    value:
      need_tools: true

  - name: Search
    description: Route to the search action
    value:
      action:
        name: search

  - name: Deep Think
    description: Enable extended reasoning mode
    route_mode: always
    value:
      reasoning:
        mode: deep
        max_steps: 20
FieldMeaning
nameDisplay name and stable identity; non-empty, unique in the file
descriptionOne line shown in the selection list
route_modeonce (default — next turn only) or always (until cleared)
valueJSON object merged into the turn's routing payload

value must be an object, because the consumer is ai/llm_branch, which routes on nested-path lookups. Prefer nested maps (action: { name: search }) over flat dotted keys (action.name: search) — the latter will not resolve.

Why not just type it in the message

A shortcut attaches routing data beside the message rather than injecting JSON into the message text. Two consequences: chat history stays clean, and the model never sees route-control JSON as if it were natural language.

Lifetime

The active shortcut is stored in the conversation context under active_shortcut_v1 and serialised into the turn's route_shortcut input each turn. once clears after one turn; always persists until the user deselects it — the selector is a toggle.

This is distinct from the pending numbered selection, which is a transient snapshot of a list.

Using them

SurfacePresetsShortcuts
EditorProperty panel selector; "save as preset"Chat toolbar
ChatPreset selector above the inputShortcut selector above the input
CLI/presets/shortcuts
External channels~~presets, then a number~~shortcuts, then a number

See Channel Routing for the channel command set and Embedded Chat for the toolbar.

Practical advice

Next steps