Skip to content

GLM workspace defaults and model migration

The model picker uses the existing glm model (GLM-5.3-Flash). Its terminal capability is enabled, while every other declared model inherits terminal: false. GLM declares glm-terminal-pilot as its default terminal; native Open WebUI selects it only when the current user can access that connection. The former glm-terminal preset is removed from models.yaml and disabled in place when found live. Its model record and saved conversations are not deleted. To continue a conversation that used the retired alias, select the existing GLM model; the migration does not rewrite any chat's history or selections.

Focused migration

Run scripts/sync_terminal_pilot.py to inspect the plan before applying it. The script keeps its original filename so the existing operational entry point works. Use --api-key-file for a local token file, or --snapshot-file for an offline snapshot. --json prints a summary without model prompts or metadata; --plan-file writes complete request bodies to a new mode-0600 file.

The ordered plan performs these changes only:

  1. Preserve global model metadata and its unset terminalId, and set its terminal capability to false. A nonempty global terminal default is refused instead of spreading an owner's connection choice to other models. The deployed API uses a one-key partial import of models.default_metadata after verifying the flat config namespace. Model order, pinned models, and other config keys stay intact.
  2. Set terminal to false on other observed model records, including undeclared records and inactive records returned by export. For glm-terminal, also set is_active: false, preserving its prompt, tools, and sharing grants.
  3. Set terminal to true on the existing glm record and apply the explicitly declared default_terminal_id and workspace_layout to meta.terminalId and meta.workspaceLayout. Preserve the name, route, tool IDs, prompt, sharing grants, suggestions, and all other capabilities. Keep existing parameters; only an explicit function_calling: legacy is changed to native, which native terminal tools require. Missing function-calling configuration already uses the native path and is preserved.

The script never creates or deletes a model, and never writes a chat. It refuses to replace a missing/inactive GLM model or overwrite a record that changed after planning. Before enabling GLM and after applying, it reads the effective model list to catch non-GLM terminal access, including synthetic models. Model export can omit other owners' records, so visible record IDs are also fetched individually. Each write must return an object, then a fresh read must match every written field before the action counts as applied. A 200 response containing null is a failure. On partial failure the script reports how many actions were verified, the uncertain action, and those remaining. Re-fetch and review a new plan before retrying; a failed request may still have reached the server.

For a read-only policy audit, run python3 scripts/sync_terminal_pilot.py --check with the same credentials. It exits 1 on drift and never writes. The nightly live-goals workflow runs this check to detect terminal access enabled through later UI edits, including on undeclared models.

The broader scripts/sync_models.py --apply writes declared parameters, including function_calling, and the allowlisted terminal: false default across every declared model. Inspect its dry run first. Use the focused script above when only the GLM terminal migration is intended.

Measured read-only after the focused migration on 2026-09-05: 49 live records passed terminal policy (48 denied, GLM enabled). The full model-sync dry run planned 0 creates and 0 updates, with 38 declared presets already current and 6 unresolvable existing GPT-5/mini/o3/o4-mini aliases. It separately reported 7 missing public grants (those 6 aliases and Inkling). That broader apply was not run. Resolve route/grant work separately rather than using a terminal migration to change unrelated model exposure.

The pinned Open WebUI version really does merge global defaults into record-less entries: see utils/models.py at the reviewed upstream commit. The audit still inspects the effective /api/models metadata and fails closed if it does not show denial; it does not assume that an absent response flag is safe. Each models-default import also re-reads the entire models namespace, checking pins, ordering and other keys alongside the changed metadata.

Model-specific defaults and user access

Open WebUI's default selection code uses meta.terminalId to auto-select a terminal. Capability alone exposes the selector. GLM deliberately declares the trial connection as its default; the frontend checks that connection is available to the current user before attaching. Users can disconnect it from the composer. The migration preserves saved chat choices and never changes connection grants. The request middleware adds terminal tools only when a chat has selected a terminal and the model allows it. The terminal connection's user/group grants remain the actual access control; the existing public GLM model does not grant other users access to a private terminal.

default_terminal_id and workspace_layout are accepted only on individual model declarations, never in global defaults or bundles. An absent key preserves any hand-set value; the general model sync accepts explicit null to clear it. The focused trial sync requires GLM's declared trial connection. Layout fields are validated against the frontend schema: absolute POSIX filePath without traversal, boolean terminalExpanded, terminalHeight from 80–800 px, and panelWidth from 300–1200 px. An optional terminalHeightRatio from 0.2–0.8 takes precedence over the pixel height until the user drags the terminal divider. The service does not require layout metadata or a patched frontend. Layout defaults have no effect until the separate frontend support is deployed; terminal auto-selection already exists natively.

Keep browser instructions on the terminal, not in GLM's system prompt. The content of docker/open-terminal/terminal-info.md is a terminal-only appendix for OPEN_TERMINAL_INFO: Open Terminal adds it to its generated /system prompt, and Open WebUI fetches that prompt when the terminal is selected. No model system_prompt_file is declared for this migration.

For rollback, keep the private before-snapshot/plan and restore only the fields this migration changed after checking for later edits. Re-enabling the old alias does not require recovering a deleted model or reconstructing conversations.