docks-kit 0.16.6 → 0.16.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -35,7 +35,7 @@ launcher can fall back to Bun source.
35
35
  | `SoT/models.json` | Kit-verified Claude and Codex model catalog |
36
36
  | `SoT/toolchain.json` | Toolchain floors manifest (verified pins consumed by EngineNative) |
37
37
  | `SoT/.claude/bin/` | Dependency-free Bun runtime programs for Claude's statusline, SessionStart, and Notification |
38
- | `SoT/.omp/` | Kit-owned omp SoT: `AGENTS.md`, `config.yml`, `mcp.json`, and `intercom.json` |
38
+ | `SoT/.omp/` | Kit-owned omp SoT: `AGENTS.md`, `config.yml`, `models.yml`, `mcp.json`, and `intercom.json` |
39
39
  | `install.sh` / `install.ps1` | POSIX and Windows global installers |
40
40
  | `.github/workflows/release-cli.yml` | `cli-v*` release: six binaries for Linux, macOS, and Windows on x64 and arm64, plus `SHA256SUMS` and npm publish |
41
41
  | `README.md` | Front door |
@@ -61,12 +61,12 @@ Codex SoT notes:
61
61
  - Claude permission rules are pruned, not host-scoped. The kit deploys its `PowerShell(...)` deny and ask rules on every host, because Claude Code leaves the PowerShell tool opt-in off Windows rather than unavailable, and a host that enables it must already carry the guards. `claudeRetired.ts RETIRED_PERMISSION_RULES, exact retired-rule inventory` lists the rules the kit no longer ships. `claudeSync.ts syncRemovals, retired-permission pass` force-prunes those exact strings from the kit-managed `~/.claude/settings.json` on every sync, because `settings.ts mergeSettings, permission-array union` would otherwise keep a dropped rule forever. A different user-authored rule survives. To restore an exact retired rule for one checkout, put it in that checkout's `.claude/settings.local.json`. Claude Code resolves that file against the working directory and merges it over user settings. Sync never reads or writes the checkout-local file. Claude Code has no user-scope local settings file. For a machine-wide restoration, add the rule to `SoT/.claude/settings.json`. At the same time, remove the exact string from `claudeRetired.ts RETIRED_PERMISSION_RULES, exact retired-rule inventory`. `permissions.allow` holds four read-only entries (`Read`, `Glob`, `Grep`, and `WebSearch`) plus the working-directory edit rule `Edit(./)`. Broad shell allow rules are not restored, because an allow rule resolves before Claude Code's read-only command analyzer and the auto-mode classifier. `autoMemoryEnabled` and `autoDreamEnabled` are both `false`.
62
62
 
63
63
  omp SoT notes:
64
- - `SoT/.omp/AGENTS.md`, `config.yml`, and `mcp.json` deploy to `~/.omp/agent/`.
64
+ - `SoT/.omp/AGENTS.md`, `config.yml`, `models.yml`, and `mcp.json` deploy to `~/.omp/agent/`.
65
65
  - `SoT/.omp/intercom.json` deploys to `$PI_CODING_AGENT_DIR/intercom/config.json`. The default root is `~/.pi/agent`.
66
- - `ompSync.ts syncConfig` deep-merges `config.yml` through `mergeOmpConfig`.
66
+ - `ompSync.ts syncMergedYaml` deep-merges `config.yml` through `ompYaml.ts mergeOmpConfig` and `models.yml` through `mergeOmpModels`. Both wrap one generic mapping merge; only the config wrapper prunes stale `retry.fallbackChains` wildcards.
67
67
  - `cycleOrder` ends with the `fable` role (`modelRoles.fable` = `anthropic/claude-fable-5-1:medium`, `modelTags.fable` visible, `retry.fallbackChains.fable` empty), so the model switcher reaches Fable 5.1 as its fourth stop and never falls back off it. The hidden `switch_fable` role points at the same model and level.
68
- - `modelRoles.task` is `openai-codex/gpt-6-astra:low`, chosen for 2.60 s TTFT and 4k output tokens per task at cost parity with the previous `gpt-5.6-sol:high`. The four reviewer agents in `task.agentModelOverrides` inherit `@task`, and Artificial Analysis publishes no per-level Astra Coding Agent Index score, so reviewer output is the signal to watch.
69
- - `task.maxEffort` is `low`. The bundled `task` agent runs `thinking-level: auto`, which rated many prompts `high`, and a caller's `effort: hi` also reached Astra `high`; the ceiling clamps both. Verified for the five bundled agents: `task`, `reviewer`, and `security-reviewer` resolve Astra `low`; `scout` and `sonic` (bundled `@smol`, Luna) drop to Luna `low`, the accepted cost. `cli/docs/omp-models.md` records the verification.
68
+ - `modelRoles.task` is `openai-codex/gpt-6-astra:low`, chosen for 2.60 s TTFT and 4k output tokens per task at cost parity with the previous `gpt-5.6-sol:high`. `task.agentModelOverrides` points four reviewer names at `@task`; only the bundled `reviewer` and `security-reviewer` are discoverable OMP agents, so those two inherit `task`, and the `code-reviewer` and `plan-reviewer` entries are dormant. Artificial Analysis publishes no per-level Astra Coding Agent Index score, so reviewer output is the signal to watch.
69
+ - `SoT/.omp/models.yml` deploys to `~/.omp/agent/models.yml` through `mergeOmpModels`, which keeps every deployed-only key. A user file may carry provider credentials, so whole-file replacement is wrong. It restricts the `gpt-6-astra` effort ladder to `low`, so every Astra subagent runs `low` while `task.maxEffort: high` lets `scout` and `sonic` reach Luna `high` with `effort: hi`. `cli/docs/omp-models.md` records the verification.
70
70
  - `SoT/.omp/AGENTS.md` carries the rule `Please remove all mannered prose.` Anthropic's Fable 5.1 prompting guide documents mannered prose as a Fable 5.1 behavior and gives that sentence as its short-version fix: https://platform.claude.com/docs/en/build-with-claude/prompt-engineering/prompting-claude-fable-5-1
71
71
  - `cli/docs/omp-models.md` (topic `omp-models`) records the role map rationale and the Artificial Analysis snapshot behind it. Model choices change with published benchmarks, so update that topic in the same commit as a role change.
72
72
  - Sync registers the `docks` marketplace. It installs or upgrades `docks@docks` and `plan-lifecycle@docks` at user scope.
@@ -79,7 +79,7 @@ For per-tool SoT layouts (`SoT/.claude/`, `SoT/.codex/`, `SoT/.omp/`), see the m
79
79
 
80
80
  - **Idempotent operations.** Every EngineNative sync step must be safe to re-run. Settings merges, plugin installs, and marketplace adds are all idempotent — re-running with no SoT changes is a no-op.
81
81
  - **Removed bash engine.** The bash engine was removed after the `bash-engine-final` tag. `DOCKS_KIT_ENGINE=bash` must fail with the removed-engine message; engine bugs are fixed forward in EngineNative.
82
- - **Effect 4 CLI stack.** The CLI pins `effect@4.0.0-rc.109` (including `effect/unstable/cli`), `@effect/platform-bun@4.0.0-rc.109` (`BunServices.layer`, `BunRuntime.runMain`), `@effect/vitest@4.0.0-rc.109`, and `vitest@4.1.10` (required by the `@effect/vitest` peer range). `@effect/cli` and `@effect/platform` are removed and must not be reintroduced.
82
+ - **Effect 4 CLI stack.** The CLI pins `effect@4.0.0-rc.109` (including `effect/unstable/cli`), `@effect/platform-bun@4.0.0-rc.109` (`BunServices.layer`, `BunRuntime.runMain`), `@effect/vitest@4.0.0-rc.109`, and `vitest@4.1.11` (inside the `@effect/vitest` peer range `>=4.1.0 <5.0.0`; 4.1.11 fixes GHSA-82fw-gwwq-j7x9). `@effect/cli` and `@effect/platform` are removed and must not be reintroduced.
83
83
  - **Effect skill routing.** Effect work in this checkout must verify migration and API call shapes against the installed declarations under `node_modules/effect/dist/unstable/cli/`, never from memory or a mutable dist-tag. The `effect-ts-setup`, `effect-ts-port`, and `effect-ts-specialist` skills target Effect 3.x and do not apply.
84
84
  - **Targeted syncs.** `./docks-kit sync` accepts positional targets: `claude`, `codex`, `agents`, and `omp`. Use the narrowest target that matches the SoT change (for example, `./docks-kit sync omp` for omp-only config edits); targets can be combined with `--dry-run`, `--skip-bubblewrap` (skip optional bubblewrap bootstrap for the Codex Linux sandbox), `--skip-plugin-refresh` (install missing plugins without refreshing existing caches; used by `docks-kit update`), `--reconcile`, `--prune`, and the deploy-time modifiers `--claude-compact-window=<tokens>` / `--claude-permissive` / `--claude-model=<m>` / `--claude-effort=<level>` / `--claude-advisor=<on|off|default>` / `--codex-model=<m>` / `--codex-effort=<level>` (see `CLAUDE.md` § Deploy-time modifiers).
85
85
  - **Per-machine harness selection.** `~/.docks-kit/state.json` drives a flag-less sync. A missing file selects `claude`, `codex`, and `agents`; it never selects `omp` implicitly. `sync` never prompts and never writes the selection file. `docks-kit harnesses` is the only command that writes the selection.
package/README.md CHANGED
@@ -61,8 +61,8 @@ EngineNative in `cli/src/engine-native/`, with golden-regression coverage for
61
61
  dry-run output, mutation snapshots, and argv logs. Its dependency graph is
62
62
  `effect@4.0.0-rc.109` (including `effect/unstable/cli`),
63
63
  `@effect/platform-bun@4.0.0-rc.109` (`BunServices.layer` and
64
- `BunRuntime.runMain`), `@effect/vitest@4.0.0-rc.109`, and `vitest@4.1.10`
65
- (required by the `@effect/vitest` peer range). `@effect/cli` and
64
+ `BunRuntime.runMain`), `@effect/vitest@4.0.0-rc.109`, and `vitest@4.1.11`
65
+ (inside the `@effect/vitest` peer range `>=4.1.0 <5.0.0`). `@effect/cli` and
66
66
  `@effect/platform` are removed: neither has a 4.x release, the CLI API moved
67
67
  into core, and the standalone platform package was unused here.
68
68
 
@@ -112,7 +112,7 @@ and a later flag-less sync reverts them. Full reference: `docks-kit docs flags`
112
112
  |------|---------|
113
113
  | `SoT/.claude/` | Claude Code SoT (settings template, Bun runtime programs, CLAUDE.md) |
114
114
  | `SoT/.codex/` | Codex SoT (config.toml, rules, AGENTS.md, marketplace) |
115
- | `SoT/.omp/` | omp SoT (AGENTS.md, config.yml, mcp.json, intercom.json) |
115
+ | `SoT/.omp/` | omp SoT (AGENTS.md, config.yml, models.yml, mcp.json, intercom.json) |
116
116
  | `SoT/.agents/` | Universal-skill manifest |
117
117
  | `SoT/models.json` | Kit-verified Claude and Codex model catalog |
118
118
  | `SoT/toolchain.json` | Verified-version floors |
@@ -36,9 +36,13 @@ What omp's settings catalog establishes about these roles:
36
36
  keeps `switch_fable` out of the switcher list.
37
37
 
38
38
  `task.agentModelOverrides` maps `reviewer`, `security-reviewer`,
39
- `code-reviewer`, and `plan-reviewer` to `@task`, so all four inherit whatever
40
- `task` resolves to. `retry.fallbackChains.task` keeps
41
- `anthropic/claude-opus-5:high` as a cross-vendor fallback.
39
+ `code-reviewer`, and `plan-reviewer` to `@task`. Only the bundled `reviewer`
40
+ and `security-reviewer` exist as OMP agents (omp's task tool lists `scout`,
41
+ `reviewer`, `security-reviewer`, `task`, and `sonic`), so those two inherit
42
+ whatever `task` resolves to. The `code-reviewer` and `plan-reviewer` entries
43
+ are dormant until an OMP agent with that name exists.
44
+ `retry.fallbackChains.task` keeps `anthropic/claude-opus-5:high` as a
45
+ cross-vendor fallback.
42
46
 
43
47
  ## Artificial Analysis snapshot
44
48
 
@@ -162,15 +166,18 @@ published Astra coding-agent score at that level.
162
166
 
163
167
  Two risks come with it. Astra low loses AA-Briefcase, the eval closest to this
164
168
  kit's agent workload, and AA publishes no Coding Agent Index score for any Astra
165
- level except max. Watch reviewer output, because the four reviewer agents
166
- inherit `@task`. If review quality drops, pin those four agents to
167
- `anthropic/claude-opus-5:high` rather than reverting the whole role.
169
+ level except max. Watch reviewer output, because `reviewer` and
170
+ `security-reviewer` inherit `@task`. If review quality drops, pin those agents
171
+ to `anthropic/claude-opus-5:high` rather than reverting the whole role.
168
172
 
169
173
  ## What resolves to Astra low in practice
170
174
 
171
- `modelRoles.task` sets the model. The `:low` suffix is the role's own level;
172
- it does not by itself decide the level of every spawn, and the role does not
173
- cover every agent.
175
+ `modelRoles.task` sets the model. The `:low` suffix alone does not cap every
176
+ spawn. `SoT/.omp/models.yml` restricts the `openai-codex/gpt-6-astra` effort
177
+ ladder to `[low]` through
178
+ `providers.openai-codex.modelOverrides.gpt-6-astra.thinking`. omp clamps any
179
+ requested effort to the model ladder, and `auto` has only one choice. Every
180
+ Astra spawn therefore runs `low`, regardless of its `effort` hint.
174
181
 
175
182
  - The bundled `scout` and `sonic` agents carry `model: "@smol"` and
176
183
  `thinking-level: medium` in their embedded frontmatter, so they run Luna,
@@ -178,26 +185,31 @@ cover every agent.
178
185
  `task.agentModelOverrides` entry for the agent name.
179
186
  - The bundled `task` agent carries `model: "@task"` and
180
187
  `thinking-level: auto`. `auto` classifies each prompt and picks a level for
181
- the resolved model. With no ceiling, `task` spawns without an `effort` hint
182
- were observed at both `low` and `high`.
188
+ the resolved model, but Astra's ladder permits only `low`.
183
189
  - `task.enableEffort` is `true`, so a caller can pass `effort: lo`, `med`, or
184
190
  `hi`, which overrides `auto`.
185
- - `task.maxEffort` is `low` in the SoT. This ceiling clamps both the `auto`
186
- classifier and the `effort` hint. Verified on 2026-09-08 for the five
187
- bundled agents: `task`, `reviewer`, and `security-reviewer` resolved
188
- `gpt-6-astra:low` with no hint and with `effort: hi`; `scout` and `sonic`
189
- with `effort: hi` resolved `gpt-5.6-luna:low`. The `plan-lifecycle` plugin
190
- agents `code-reviewer` and `plan-reviewer` carry no `model` or
191
- `thinking-level` frontmatter and inherit `@task`; the cap applies to their
192
- spawns by the same rule, but they were not probed.
193
-
194
- So the bundled subagents run their models at `low`. The Astra low figures
195
- above are the level `task`, `reviewer`, and `security-reviewer` get. The cost
196
- of the cap is that `scout` and `sonic` also drop from Luna `medium` to Luna
197
- `low` (index 22 against 26). Raising `task.maxEffort` lets `auto` and
198
- `effort: hi` reach Astra `high` again ($1.72 per index task, 45.63 s TTFT).
199
-
200
- omp's model catalog lists Astra with a 272k context window, while AA lists 1M.
191
+ - `task.maxEffort` is `high`, so `scout` and `sonic` run Luna `medium` by
192
+ default and Luna `high` with `effort: hi`.
193
+ - The `code-reviewer` and `plan-reviewer` override entries remain dormant.
194
+ omp's task tool rejects both names as unknown agents, so neither can spawn.
195
+
196
+ Fresh `omp -p` runs on 2026-09-09 verified the model ladder with
197
+ `task.maxEffort: high`. Child session logs recorded these results:
198
+
199
+ | Agent | Effort hint | Resolved model and level |
200
+ |---|---|---|
201
+ | `task` | `hi` | `gpt-6-astra:low` |
202
+ | `task` | None, complex prompt | `gpt-6-astra:low` |
203
+ | `reviewer` | `hi` | `gpt-6-astra:low` |
204
+ | `security-reviewer` | None | `gpt-6-astra:low` |
205
+ | `scout` | `hi` | `gpt-5.6-luna:high` |
206
+ | `sonic` | `hi` | `gpt-5.6-luna:high` |
207
+
208
+ The complex `task` run completed with 682 output tokens.
209
+
210
+ omp's 272k context window is the `/extended-context off` window for Astra.
211
+ `/extended-context on` uses the 922k input window, 1.05M total, which matches
212
+ AA's 1M.
201
213
 
202
214
  ## Maintenance
203
215
 
@@ -207,3 +219,5 @@ omp's model catalog lists Astra with a 272k context window, while AA lists 1M.
207
219
  - Record the index version with the numbers. AA changes index composition
208
220
  between versions, so a score from another version is not a comparison.
209
221
  - Update the capture date in the same commit as any number.
222
+ - Verify changes to the Astra ladder in `SoT/.omp/models.yml` and to
223
+ `task.maxEffort` together with a fresh `omp -p` spawn per bundled agent.
@@ -1,12 +1,12 @@
1
1
  /**
2
- * EngineNative `sync omp` pipeline. config.yml merges through mergeOmpConfig
3
- * because omp serialises that file itself. Paths come from ompPaths because
2
+ * EngineNative `sync omp` pipeline. config.yml and models.yml merge through
3
+ * mergeOmpConfig to preserve user-only keys. Paths come from ompPaths because
4
4
  * profiles, PI_CONFIG_DIR, PI_CODING_AGENT_DIR, and XDG roots each move them.
5
5
  * Resolution stays within the environment and filesystem probes so no omp
6
6
  * subcommand runs under ctx.dryRun.
7
7
  */
8
8
  import { chmodSync, copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs"
9
- import { isAbsolute, resolve } from "node:path"
9
+ import { basename, isAbsolute, resolve } from "node:path"
10
10
 
11
11
  import { payloadDisplayPath, payloadText, type PayloadPath } from "../payload"
12
12
  import { bunBootstrap } from "./bun"
@@ -14,7 +14,7 @@ import { p, spawnProcess, type AsyncProcessResult } from "./exec"
14
14
  import type { Ctx } from "./index"
15
15
  import { isObject, parseJson } from "./jq"
16
16
  import { ompPaths } from "./ompPaths"
17
- import { mergeOmpConfig } from "./ompYaml"
17
+ import { mergeOmpConfig, mergeOmpModels } from "./ompYaml"
18
18
  import { field } from "./toolchain"
19
19
 
20
20
  const MARKETPLACE_NAME = "docks"
@@ -35,7 +35,8 @@ export async function ompSync(ctx: Ctx): Promise<OmpState> {
35
35
 
36
36
  syncWholeFile(ctx, "SoT/.omp/AGENTS.md", p(agentDir, "AGENTS.md"), "omp AGENTS.md already in sync", "omp AGENTS.md synced")
37
37
  syncWholeFile(ctx, "SoT/.omp/mcp.json", p(agentDir, "mcp.json"), "omp mcp.json already in sync", "omp mcp.json synced")
38
- syncConfig(ctx, p(agentDir, "config.yml"))
38
+ syncMergedYaml(ctx, "SoT/.omp/config.yml", p(agentDir, "config.yml"), mergeOmpConfig)
39
+ syncMergedYaml(ctx, "SoT/.omp/models.yml", p(agentDir, "models.yml"), mergeOmpModels)
39
40
 
40
41
  const intercomRootSetting = process.env["PI_CODING_AGENT_DIR"]
41
42
  const intercomRoot = intercomRootSetting !== undefined && intercomRootSetting !== ""
@@ -111,10 +112,16 @@ function syncWholeFile(
111
112
  ctx.nextStepTriggers.ompRestart = true
112
113
  }
113
114
 
114
- function syncConfig(ctx: Ctx, target: string): void {
115
+ function syncMergedYaml(
116
+ ctx: Ctx,
117
+ sourcePath: OmpTextPayloadPath,
118
+ target: string,
119
+ merge: (sotText: string, deployedText: string) => string
120
+ ): void {
115
121
  const { change, echo, verbose } = ctx.services.logger
116
- const source = payloadDisplayPath("SoT/.omp/config.yml")
117
- const sotText = payloadText("SoT/.omp/config.yml")
122
+ const source = payloadDisplayPath(sourcePath)
123
+ const sotText = payloadText(sourcePath)
124
+ const name = basename(sourcePath)
118
125
 
119
126
  if (!existsSync(target)) {
120
127
  if (ctx.dryRun) {
@@ -122,15 +129,15 @@ function syncConfig(ctx: Ctx, target: string): void {
122
129
  return
123
130
  }
124
131
  writePrivateFile(target, sotText)
125
- change("omp config.yml installed")
132
+ change(`omp ${name} installed`)
126
133
  ctx.nextStepTriggers.ompRestart = true
127
134
  return
128
135
  }
129
136
 
130
137
  const deployedText = readFileSync(target, "utf8")
131
- const merged = mergeOmpConfig(sotText, deployedText)
138
+ const merged = merge(sotText, deployedText)
132
139
  if (merged === deployedText) {
133
- verbose("omp config.yml already in sync")
140
+ verbose(`omp ${name} already in sync`)
134
141
  return
135
142
  }
136
143
  if (ctx.dryRun) {
@@ -142,7 +149,7 @@ function syncConfig(ctx: Ctx, target: string): void {
142
149
  writePrivateFile(`${target}.tmp`, merged)
143
150
  renameSync(`${target}.tmp`, target)
144
151
  chmodSync(target, 0o600)
145
- change("omp config.yml merged (backup at config.yml.bak)")
152
+ change(`omp ${name} merged (backup at ${name}.bak)`)
146
153
  ctx.nextStepTriggers.ompRestart = true
147
154
  }
148
155
 
@@ -1,12 +1,18 @@
1
1
  /**
2
- * YAML merge for omp config.yml. omp serialises this file itself, so the kit
3
- * uses the yaml package rather than a line-based merge. Deployed-only keys are
4
- * retained except for slash-bearing keys directly under retry.fallbackChains:
5
- * omp treats those as model or provider wildcards ahead of role chains, so a
6
- * stale wildcard would silently override the kit-managed role chains.
2
+ * YAML merge for the omp config.yml and models.yml files. omp serialises both
3
+ * files itself, so the kit uses the yaml package rather than a line-based
4
+ * merge. Deployed-only keys are retained, except where the file-specific
5
+ * wrapper drops them: config.yml drops slash-bearing keys directly under
6
+ * retry.fallbackChains, because omp treats those as model or provider
7
+ * wildcards ahead of role chains, so a stale wildcard would silently override
8
+ * the kit-managed role chains. models.yml drops nothing; a user file may carry
9
+ * provider credentials and custom models the kit never declares.
7
10
  */
8
11
  import { isMap, isScalar, parseDocument, type YAMLMap } from "yaml"
9
12
 
13
+ /** Decides whether a deployed-only key at `path` is dropped instead of kept. */
14
+ type DropDeployedKey = (path: ReadonlyArray<string>, key: string) => boolean
15
+
10
16
  const fallbackChainsPath = ["retry", "fallbackChains"] as const
11
17
 
12
18
  function stringKey(key: unknown): string | undefined {
@@ -15,29 +21,31 @@ function stringKey(key: unknown): string | undefined {
15
21
  return undefined
16
22
  }
17
23
 
18
- function isFallbackChains(path: ReadonlyArray<string>): boolean {
19
- return (
20
- path.length === fallbackChainsPath.length &&
21
- path[0] === fallbackChainsPath[0] &&
22
- path[1] === fallbackChainsPath[1]
23
- )
24
+ function keyName(key: unknown): string {
25
+ return isScalar(key) ? String(key.value) : String(key)
24
26
  }
25
27
 
26
- function pruneFallbackWildcards(mapping: YAMLMap, path: ReadonlyArray<string>): void {
27
- const underFallbackChains = isFallbackChains(path)
28
+ const dropFallbackWildcard: DropDeployedKey = (path, key) =>
29
+ path.length === fallbackChainsPath.length &&
30
+ path[0] === fallbackChainsPath[0] &&
31
+ path[1] === fallbackChainsPath[1] &&
32
+ key.includes("/")
33
+
34
+ const keepEveryKey: DropDeployedKey = () => false
35
+
36
+ function pruneMapping(mapping: YAMLMap, path: ReadonlyArray<string>, drop: DropDeployedKey): void {
28
37
  for (let index = mapping.items.length - 1; index >= 0; index--) {
29
38
  const pair = mapping.items[index]
30
39
  if (pair === undefined) continue
31
40
 
32
- const keyName = isScalar(pair.key) ? String(pair.key.value) : String(pair.key)
33
- if (underFallbackChains && keyName.includes("/")) {
41
+ if (drop(path, keyName(pair.key))) {
34
42
  mapping.items.splice(index, 1)
35
43
  continue
36
44
  }
37
45
 
38
46
  const key = stringKey(pair.key)
39
47
  if (key !== undefined && isMap(pair.value)) {
40
- pruneFallbackWildcards(pair.value, [...path, key])
48
+ pruneMapping(pair.value, [...path, key], drop)
41
49
  }
42
50
  }
43
51
  }
@@ -45,7 +53,8 @@ function pruneFallbackWildcards(mapping: YAMLMap, path: ReadonlyArray<string>):
45
53
  function mergeMappings(
46
54
  sotMapping: YAMLMap,
47
55
  deployedMapping: YAMLMap,
48
- path: ReadonlyArray<string>
56
+ path: ReadonlyArray<string>,
57
+ drop: DropDeployedKey
49
58
  ): void {
50
59
  for (const sotPair of sotMapping.items) {
51
60
  const key = stringKey(sotPair.key)
@@ -53,7 +62,7 @@ function mergeMappings(
53
62
 
54
63
  const deployedPair = deployedMapping.items.find((candidate) => stringKey(candidate.key) === key)
55
64
  if (deployedPair !== undefined && isMap(sotPair.value) && isMap(deployedPair.value)) {
56
- mergeMappings(sotPair.value, deployedPair.value, [...path, key])
65
+ mergeMappings(sotPair.value, deployedPair.value, [...path, key], drop)
57
66
  }
58
67
  }
59
68
 
@@ -63,22 +72,24 @@ function mergeMappings(
63
72
  key !== undefined && sotMapping.items.some((candidate) => stringKey(candidate.key) === key)
64
73
  if (isManaged) continue
65
74
 
66
- const keyName = isScalar(deployedPair.key)
67
- ? String(deployedPair.key.value)
68
- : String(deployedPair.key)
69
- if (isFallbackChains(path) && keyName.includes("/")) continue
75
+ if (drop(path, keyName(deployedPair.key))) continue
70
76
  if (key !== undefined && isMap(deployedPair.value)) {
71
- pruneFallbackWildcards(deployedPair.value, [...path, key])
77
+ pruneMapping(deployedPair.value, [...path, key], drop)
72
78
  }
73
79
  sotMapping.items.push(deployedPair)
74
80
  }
75
81
  }
76
82
 
77
- export function mergeOmpConfig(sotText: string, deployedText: string): string {
83
+ function mergeYamlDocuments(
84
+ sotText: string,
85
+ deployedText: string,
86
+ name: string,
87
+ drop: DropDeployedKey
88
+ ): string {
78
89
  const deployedDoc = parseDocument(deployedText)
79
90
  const deployedError = deployedDoc.errors[0]
80
91
  if (deployedError !== undefined) {
81
- throw new Error(`Invalid deployed omp config YAML: ${deployedError.message}`)
92
+ throw new Error(`Invalid deployed omp ${name} YAML: ${deployedError.message}`)
82
93
  }
83
94
 
84
95
  const deployedContents = deployedDoc.contents
@@ -90,18 +101,28 @@ export function mergeOmpConfig(sotText: string, deployedText: string): string {
90
101
  return sotText
91
102
  }
92
103
  if (!isMap(deployedContents)) {
93
- throw new Error("Deployed omp config YAML root must be a mapping")
104
+ throw new Error(`Deployed omp ${name} YAML root must be a mapping`)
94
105
  }
95
106
 
96
107
  const sotDoc = parseDocument(sotText)
97
108
  const sotError = sotDoc.errors[0]
98
109
  if (sotError !== undefined) {
99
- throw new Error(`Invalid SoT omp config YAML: ${sotError.message}`)
110
+ throw new Error(`Invalid SoT omp ${name} YAML: ${sotError.message}`)
100
111
  }
101
112
  if (!isMap(sotDoc.contents)) {
102
- throw new Error("SoT omp config YAML root must be a mapping")
113
+ throw new Error(`SoT omp ${name} YAML root must be a mapping`)
103
114
  }
104
115
 
105
- mergeMappings(sotDoc.contents, deployedContents, [])
116
+ mergeMappings(sotDoc.contents, deployedContents, [], drop)
106
117
  return sotDoc.toString()
107
118
  }
119
+
120
+ /** config.yml merge: keeps user-only keys, drops stale fallback-chain wildcards. */
121
+ export function mergeOmpConfig(sotText: string, deployedText: string): string {
122
+ return mergeYamlDocuments(sotText, deployedText, "config.yml", dropFallbackWildcard)
123
+ }
124
+
125
+ /** models.yml merge: keeps every user-only key, including credentials and custom models. */
126
+ export function mergeOmpModels(sotText: string, deployedText: string): string {
127
+ return mergeYamlDocuments(sotText, deployedText, "models.yml", keepEveryKey)
128
+ }
@@ -1,7 +1,7 @@
1
1
  // Generated by cli/scripts/generate-sot-payload.ts. DO NOT EDIT.
2
2
  // Edit SoT/, notification.mp3, or package.json, then run: bun cli/scripts/generate-sot-payload.ts
3
3
 
4
- export const GENERATED_PACKAGE_VERSION = "0.16.6"
4
+ export const GENERATED_PACKAGE_VERSION = "0.16.8"
5
5
 
6
6
  export const GENERATED_PAYLOAD_TEXT = {
7
7
  "SoT/.agents/skills.txt": "# Universal AI-agent skill manifest intentionally empty.\n# Global skill discovery is opt-in: add one <owner>/<repo> slug per line.\n# EngineNative ignores comments and blank lines.\n",
@@ -18,9 +18,10 @@ export const GENERATED_PAYLOAD_TEXT = {
18
18
  "SoT/.codex/plugins/marketplace.json": "{\n \"name\": \"docks\",\n \"interface\": {\n \"displayName\": \"DocksDocks\"\n },\n \"plugins\": [\n {\n \"name\": \"docks\",\n \"source\": {\n \"source\": \"git-subdir\",\n \"url\": \"https://github.com/DocksDocks/docks.git\",\n \"path\": \"./plugins/docks\",\n \"ref\": \"main\"\n },\n \"policy\": {\n \"installation\": \"AVAILABLE\",\n \"authentication\": \"ON_INSTALL\"\n },\n \"category\": \"Productivity\"\n },\n {\n \"name\": \"plan-lifecycle\",\n \"source\": {\n \"source\": \"git-subdir\",\n \"url\": \"https://github.com/DocksDocks/docks.git\",\n \"path\": \"./plugins/plan-lifecycle\",\n \"ref\": \"main\"\n },\n \"policy\": {\n \"installation\": \"AVAILABLE\",\n \"authentication\": \"ON_INSTALL\"\n },\n \"category\": \"Productivity\"\n }\n ]\n}\n",
19
19
  "SoT/.codex/rules/docks.rules": "prefix_rule(pattern=[\"pwd\"], decision=\"allow\")\nprefix_rule(pattern=[\"ls\"], decision=\"allow\")\nprefix_rule(pattern=[\"cat\"], decision=\"allow\")\nprefix_rule(pattern=[\"head\"], decision=\"allow\")\nprefix_rule(pattern=[\"tail\"], decision=\"allow\")\nprefix_rule(pattern=[\"wc\"], decision=\"allow\")\nprefix_rule(pattern=[\"nl\"], decision=\"allow\")\nprefix_rule(pattern=[\"grep\"], decision=\"allow\")\nprefix_rule(pattern=[\"sort\"], decision=\"allow\")\nprefix_rule(pattern=[\"uniq\"], decision=\"allow\")\nprefix_rule(pattern=[\"diff\"], decision=\"allow\")\nprefix_rule(pattern=[\"which\"], decision=\"allow\")\nprefix_rule(pattern=[\"date\"], decision=\"allow\")\nprefix_rule(pattern=[\"basename\"], decision=\"allow\")\nprefix_rule(pattern=[\"dirname\"], decision=\"allow\")\nprefix_rule(pattern=[\"realpath\"], decision=\"allow\")\nprefix_rule(pattern=[\"readlink\"], decision=\"allow\")\nprefix_rule(pattern=[\"jq\"], decision=\"allow\")\nprefix_rule(pattern=[\"tree\"], decision=\"allow\")\nprefix_rule(pattern=[\"cut\"], decision=\"allow\")\nprefix_rule(pattern=[\"tr\"], decision=\"allow\")\nprefix_rule(pattern=[\"echo\"], decision=\"allow\")\nprefix_rule(pattern=[\"printf\"], decision=\"allow\")\nprefix_rule(pattern=[\"printenv\"], decision=\"allow\")\nprefix_rule(pattern=[\"uname\"], decision=\"allow\")\nprefix_rule(pattern=[\"file\"], decision=\"allow\")\nprefix_rule(pattern=[\"stat\"], decision=\"allow\")\nprefix_rule(pattern=[\"du\"], decision=\"allow\")\nprefix_rule(pattern=[\"id\"], decision=\"allow\")\nprefix_rule(pattern=[\"whoami\"], decision=\"allow\")\n\nprefix_rule(pattern=[\"git\", \"status\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"diff\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"log\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"show\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"blame\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"rev-parse\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"ls-files\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"grep\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"ls-tree\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"branch\", \"--show-current\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"branch\", \"-vv\"], decision=\"allow\")\nprefix_rule(pattern=[\"git\", \"mv\"], decision=\"allow\")\n\nprefix_rule(pattern=[\"gh\", \"pr\", \"view\"], decision=\"allow\")\nprefix_rule(pattern=[\"gh\", \"pr\", \"list\"], decision=\"allow\")\nprefix_rule(pattern=[\"gh\", \"pr\", \"diff\"], decision=\"allow\")\nprefix_rule(pattern=[\"gh\", \"pr\", \"status\"], decision=\"allow\")\nprefix_rule(pattern=[\"gh\", \"pr\", \"checks\"], decision=\"allow\")\n\nprefix_rule(pattern=[\"docker\", \"ps\"], decision=\"allow\")\n\nprefix_rule(pattern=[\"git\", \"push\"], decision=\"prompt\")\nprefix_rule(pattern=[\"git\", \"reset\"], decision=\"prompt\")\nprefix_rule(pattern=[\"git\", \"clean\"], decision=\"prompt\")\nprefix_rule(pattern=[\"git\", \"merge\"], decision=\"prompt\")\nprefix_rule(pattern=[\"git\", \"rebase\"], decision=\"prompt\")\nprefix_rule(pattern=[\"git\", \"checkout\"], decision=\"prompt\")\n\nprefix_rule(pattern=[\"rm\"], decision=\"prompt\")\nprefix_rule(pattern=[\"mv\"], decision=\"prompt\")\nprefix_rule(pattern=[\"chmod\"], decision=\"prompt\")\nprefix_rule(pattern=[\"chown\"], decision=\"prompt\")\nprefix_rule(pattern=[\"kill\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pkill\"], decision=\"prompt\")\n\nprefix_rule(pattern=[\"npm\", \"install\"], decision=\"prompt\")\nprefix_rule(pattern=[\"npm\", \"uninstall\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pnpm\", \"add\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pnpm\", \"remove\"], decision=\"prompt\")\nprefix_rule(pattern=[\"yarn\", \"add\"], decision=\"prompt\")\nprefix_rule(pattern=[\"yarn\", \"remove\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pip\", \"install\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pip\", \"uninstall\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pip3\", \"install\"], decision=\"prompt\")\nprefix_rule(pattern=[\"pip3\", \"uninstall\"], decision=\"prompt\")\n\nprefix_rule(pattern=[\"docker\", \"run\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"rm\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"stop\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"volume\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"system\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"compose\", \"up\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"compose\", \"down\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"compose\", \"rm\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker\", \"compose\", \"stop\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker-compose\", \"up\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker-compose\", \"down\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker-compose\", \"rm\"], decision=\"prompt\")\nprefix_rule(pattern=[\"docker-compose\", \"stop\"], decision=\"prompt\")\n\n# `tail -f`/`--follow` never returns and hangs the agent (bare `tail` stays allowed above).\nprefix_rule(pattern=[\"tail\", \"-f\"], decision=\"prompt\")\nprefix_rule(pattern=[\"tail\", \"--follow\"], decision=\"prompt\")\n# rg and `sed -n` are prompt, NOT allow: argv-prefix matching cannot gate their\n# code-exec forms (rg --pre=CMD or a reordered --pre; sed -n 'e CMD' / -ni) while a\n# shorter allow prefix would auto-approve the whole command.\nprefix_rule(pattern=[\"rg\"], decision=\"prompt\")\nprefix_rule(pattern=[\"sed\", \"-n\"], decision=\"prompt\")\nprefix_rule(pattern=[\"find\"], decision=\"prompt\")\nprefix_rule(pattern=[\"sed\", \"-i\"], decision=\"prompt\")\nprefix_rule(pattern=[\"sed\", \"--in-place\"], decision=\"prompt\")\nprefix_rule(pattern=[\"awk\"], decision=\"prompt\")\nprefix_rule(pattern=[\"xargs\"], decision=\"prompt\")\nprefix_rule(pattern=[\"tee\"], decision=\"prompt\")\nprefix_rule(pattern=[\"curl\"], decision=\"prompt\")\nprefix_rule(pattern=[\"env\"], decision=\"prompt\")\n\nprefix_rule(pattern=[\"sudo\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"eval\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"mkfs\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"dd\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"git\", \"push\", \"--force\", \"origin\", \"main\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"git\", \"push\", \"--force\", \"origin\", \"master\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"git\", \"push\", \"-f\", \"origin\", \"main\"], decision=\"forbidden\")\nprefix_rule(pattern=[\"git\", \"push\", \"-f\", \"origin\", \"master\"], decision=\"forbidden\")\n",
20
20
  "SoT/.omp/AGENTS.md": "# Global OMP guidance\n\n- Verify unfamiliar or version-sensitive APIs and configuration against current official documentation before implementation.\n- When the user asks for an assessment rather than a change, report findings without editing.\n- For Docks plan reviews, cross-company review is standing-authorized; host security policy still applies.\n- Please remove all mannered prose.\n\n## Asking me things\n\nA question typed in prose is just text I may or may not act on. The `ask` tool renders a\nblocking picker, waits indefinitely (`ask.timeout = 0`), and records my answer in the\ntranscript. If you actually need an answer, it MUST go through `ask`.\n\nMUST use `ask` before:\n- Anything irreversible or destructive: deleting/overwriting files or data you did not create,\n force-push, history rewrite, dropping tables, running migrations, mass rename, touching\n secrets/credentials, or publishing outward (release, upstream PR, issue, comment).\n- Two or more viable approaches whose tradeoffs are mine to own: schema/API/protocol shape,\n adding a dependency, or establishing a convention this repo does not already have.\n- A fact only I hold: intended semantics of an ambiguous requirement, which of several\n conflicting existing patterns is canonical, or which environment/account/target to use.\n- A request that contradicts the repo: surface the conflict and let me resolve it; never\n silently pick one side.\n\nIf `ask` is not registered — subagent, headless, or `-p` print runs, where `hasUI` is false —\nthe MUST above cannot be satisfied: do not fabricate the call and do not stall on it. Take the\nconservative reversible option and put the question, plus the assumption you made, in your\nfinal report so whoever spawned you can decide.\n\nNEVER use `ask` for:\n- Permission to begin, or to confirm scope already stated in the request.\n- Anything a tool, grep, or doc can answer — go read it.\n- A cheap reversible choice — take the conservative option and say which you took.\n- Something already answered earlier in the conversation.\n\nBatch every open question into one `ask` call with multiple questions; do not serialize\nround trips. Being overruled ends the discussion — execute my call without relitigating.\n\n## Output Standard\n\nApply Simplified Technical English to all agent text. This includes responses, messages,\ndocumentation, comments, and interface text.\n\nReply in the language I use, and apply every rule below to that language.\n\nA rule that names English grammar applies only to English. The contraction ban is one\nsuch rule. In another language, follow the normal grammar of that language. Portuguese,\nSpanish, French, Italian, and German merge a preposition with an article, and that merge\nis required, not optional.\n\nTreat the word limits as approximate outside English. Some languages need more words to\ncarry the same content.\n\n- Use the simplest precise technical term.\n- Use each term consistently.\n- Expand an abbreviation at its first occurrence.\n- Explain a technical term when I ask for an explanation.\n- Write complete and grammatically correct sentences.\n- Use active voice and identify the actor.\n- Use the imperative form for instructions.\n- Put only one action in each instruction sentence.\n- Put a necessary condition before its instruction.\n- Use simple verb tenses.\n- Do not use contractions, idioms, or slang. Avoid humor and rhetorical questions.\n- Keep procedural sentences to 20 words or fewer.\n- Keep descriptive sentences to 25 words or fewer.\n- Keep each paragraph to one topic and six sentences or fewer.\n- Do not use more than three nouns together.\n- Use vertical lists for complex information.\n- Put a warning or caution before a related hazardous instruction.\n\n### Naming\n\n- Say what the thing does before you name it. Put the technical term after the plain\n description, once, in parentheses.\n- Do not use a technical term as the only name for something you just introduced.\n- Prefer the short common word. Use \"use\", not \"utilize\". Use \"set up\", not \"provision\".\n- Do not explain by metaphor alone. A metaphor may follow a literal statement.\n",
21
- "SoT/.omp/config.yml": "symbolPreset: unicode\n\ntheme:\n dark: titanium\n\nstatusLine:\n preset: default\n compactThinkingLevel: false\n showHookStatus: true\n sessionAccent: true\n transparent: false\n\nterminal:\n showProgress: false\n\ntui:\n textSizing: false\n tight: false\n\ndefaultThinkingLevel: high\n\ncycleOrder:\n - smol\n - default\n - slow\n - fable\n\ntier:\n openai: none\n anthropic: none\n\nadvisor:\n enabled: true\n syncBacklog: \"1\"\n\ngithub:\n enabled: true\n\ntask:\n eager: always\n showResolvedModelBadge: true\n softRequestBudget: 200\n softRequestBudgetNotice: true\n batch: true\n enableLsp: true\n enableEffort: true\n maxEffort: low\n maxConcurrency: 16\n maxRecursionDepth: 2\n maxRuntimeMs: 3600000\n agentModelOverrides:\n reviewer: \"@task\"\n security-reviewer: \"@task\"\n code-reviewer: \"@task\"\n plan-reviewer: \"@task\"\n\nmodelRoles:\n smol: openai-codex/gpt-5.6-luna:medium\n advisor: openai-codex/gpt-5.6-sol:medium\n designer: anthropic/claude-opus-5:high\n plan: anthropic/claude-opus-5:xhigh\n commit: openai-codex/gpt-5.6-luna:medium\n task: openai-codex/gpt-6-astra:low\n vision: anthropic/claude-opus-5:medium\n tiny: openai-codex/gpt-5.6-luna:low\n default: anthropic/claude-opus-5:high\n slow: anthropic/claude-opus-5:xhigh\n fable: anthropic/claude-fable-5-1:medium\n switch_fable: anthropic/claude-fable-5-1:medium\n\nmodelTags:\n fable:\n name: Fable 5.1\n switch_fable:\n name: Fable switch default\n hidden: true\n\ndisplay:\n shimmer: classic\n showTokenUsage: true\n\nproviders:\n anthropic:\n serverSideFallback: false\n fetch: auto\n webSearchTimeoutSeconds: 30\n webSearchOrder:\n - firecrawl\n - exa\n - perplexity\n - gemini\n - codex\n\nretry:\n usageAwareFallback: true\n fallbackChains:\n default:\n - openai-codex/gpt-5.6-sol:high\n advisor:\n - anthropic/claude-opus-5:medium\n task:\n - anthropic/claude-opus-5:high\n vision:\n - openai-codex/gpt-5.6-sol:medium\n smol:\n - anthropic/claude-opus-5:low\n tiny:\n - anthropic/claude-opus-5:low\n commit:\n - anthropic/claude-opus-5:medium\n switch_fable: []\n fable: []\n\nomitThinking: false\nincludeWorkspaceTree: false\nautocompleteMaxVisible: 10\nemojiAutocomplete: true\n\nbranchSummary:\n enabled: true\n\nreadLineNumbers: false\n\ncommands:\n enableOpencodeProject: false\n enableOpencodeUser: false\n\nskills:\n enableClaudeUser: false\n enableCodexUser: false\n enableAgentsUser: true\n\nsteeringMode: all\ninterruptMode: immediate\n\ndev:\n autoqa: false\n autoqaConsent: denied\n\ncompaction:\n thresholdTokens: 231200\n idleEnabled: true\n handoffSaveToDisk: true\n\ndoubleEscapeAction: tree\nhideThinkingBlock: false\nautoResume: false\ntextVerbosity: low\n\nfeatures:\n unexpectedStopDetection: smart\n\ncodexResets:\n autoRedeem: \"no\"\n\nstartup:\n quiet: true\n changelogMode: summary\n\ntools:\n approvalMode: yolo\n",
21
+ "SoT/.omp/config.yml": "symbolPreset: unicode\n\ntheme:\n dark: titanium\n\nstatusLine:\n preset: default\n compactThinkingLevel: false\n showHookStatus: true\n sessionAccent: true\n transparent: false\n\nterminal:\n showProgress: false\n\ntui:\n textSizing: false\n tight: false\n\ndefaultThinkingLevel: high\n\ncycleOrder:\n - smol\n - default\n - slow\n - fable\n\ntier:\n openai: none\n anthropic: none\n\nadvisor:\n enabled: true\n syncBacklog: \"1\"\n\ngithub:\n enabled: true\n\ntask:\n eager: always\n showResolvedModelBadge: true\n softRequestBudget: 200\n softRequestBudgetNotice: true\n batch: true\n enableLsp: true\n enableEffort: true\n maxEffort: high\n maxConcurrency: 16\n maxRecursionDepth: 2\n maxRuntimeMs: 3600000\n agentModelOverrides:\n reviewer: \"@task\"\n security-reviewer: \"@task\"\n code-reviewer: \"@task\"\n plan-reviewer: \"@task\"\n\nmodelRoles:\n smol: openai-codex/gpt-5.6-luna:medium\n advisor: openai-codex/gpt-5.6-sol:medium\n designer: anthropic/claude-opus-5:high\n plan: anthropic/claude-opus-5:xhigh\n commit: openai-codex/gpt-5.6-luna:medium\n task: openai-codex/gpt-6-astra:low\n vision: anthropic/claude-opus-5:medium\n tiny: openai-codex/gpt-5.6-luna:low\n default: anthropic/claude-opus-5:high\n slow: anthropic/claude-opus-5:xhigh\n fable: anthropic/claude-fable-5-1:medium\n switch_fable: anthropic/claude-fable-5-1:medium\n\nmodelTags:\n fable:\n name: Fable 5.1\n switch_fable:\n name: Fable switch default\n hidden: true\n\ndisplay:\n shimmer: classic\n showTokenUsage: true\n\nproviders:\n anthropic:\n serverSideFallback: false\n fetch: auto\n webSearchTimeoutSeconds: 30\n webSearchOrder:\n - firecrawl\n - exa\n - perplexity\n - gemini\n - codex\n\nretry:\n usageAwareFallback: true\n fallbackChains:\n default:\n - openai-codex/gpt-5.6-sol:high\n advisor:\n - anthropic/claude-opus-5:medium\n task:\n - anthropic/claude-opus-5:high\n vision:\n - openai-codex/gpt-5.6-sol:medium\n smol:\n - anthropic/claude-opus-5:low\n tiny:\n - anthropic/claude-opus-5:low\n commit:\n - anthropic/claude-opus-5:medium\n switch_fable: []\n fable: []\n\nomitThinking: false\nincludeWorkspaceTree: false\nautocompleteMaxVisible: 10\nemojiAutocomplete: true\n\nbranchSummary:\n enabled: true\n\nreadLineNumbers: false\n\ncommands:\n enableOpencodeProject: false\n enableOpencodeUser: false\n\nskills:\n enableClaudeUser: false\n enableCodexUser: false\n enableAgentsUser: true\n\nsteeringMode: all\ninterruptMode: immediate\n\ndev:\n autoqa: false\n autoqaConsent: denied\n\ncompaction:\n thresholdTokens: 231200\n idleEnabled: true\n handoffSaveToDisk: true\n\ndoubleEscapeAction: tree\nhideThinkingBlock: false\nautoResume: false\ntextVerbosity: low\n\nfeatures:\n unexpectedStopDetection: smart\n\ncodexResets:\n autoRedeem: \"no\"\n\nstartup:\n quiet: true\n changelogMode: summary\n\ntools:\n approvalMode: yolo\n",
22
22
  "SoT/.omp/intercom.json": "{\n \"brokerCommand\": \"bun\",\n \"brokerArgs\": []\n}\n",
23
- "SoT/.omp/mcp.json": "{\n \"$schema\": \"https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/config/mcp-schema.json\",\n \"disabledServers\": [\n \"chrome-devtools\",\n \"context7:context7\",\n \"openaiDeveloperDocs\"\n ]\n}\n"
23
+ "SoT/.omp/mcp.json": "{\n \"$schema\": \"https://raw.githubusercontent.com/can1357/oh-my-pi/main/packages/coding-agent/src/config/mcp-schema.json\",\n \"disabledServers\": [\n \"chrome-devtools\",\n \"context7:context7\",\n \"openaiDeveloperDocs\"\n ]\n}\n",
24
+ "SoT/.omp/models.yml": "providers:\n openai-codex:\n modelOverrides:\n gpt-6-astra:\n thinking:\n mode: effort\n efforts:\n - low\n defaultLevel: low\n"
24
25
  } as const
25
26
 
26
27
  export const GENERATED_PAYLOAD_BASE64 = {
@@ -45,7 +46,8 @@ export const GENERATED_PAYLOAD_PATHS = [
45
46
  "SoT/.omp/config.yml",
46
47
  "SoT/.omp/intercom.json",
47
48
  "SoT/.omp/mcp.json",
49
+ "SoT/.omp/models.yml",
48
50
  "notification.mp3"
49
51
  ] as const
50
52
 
51
- export const GENERATED_PAYLOAD_HASH = "df37d563d668ab4f09d4b0ab50f9e7ff81612c2032b57870b0f919aacef43855"
53
+ export const GENERATED_PAYLOAD_HASH = "5f9f8b3d00c15f6611af2bd8d92bbfcd12481442ff6e0e249846ac2e320b4e37"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "docks-kit",
3
- "version": "0.16.6",
3
+ "version": "0.16.8",
4
4
  "description": "Portable AI coding agent config kit — SoT sync engine + typed CLI for Claude Code, Codex, and universal agent skills",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -47,6 +47,6 @@
47
47
  "@effect/vitest": "4.0.0-rc.109",
48
48
  "@types/bun": "^1.4.0",
49
49
  "typescript": "7.0.2",
50
- "vitest": "4.1.10"
50
+ "vitest": "4.1.11"
51
51
  }
52
52
  }