Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
af31d78
feat(task): add opt-in sub-agent model autorouting
Jul 31, 2026
ae35aef
feat(task): auto-generate autorouting tiers from declared providers
Aug 2, 2026
c29badf
fix(task): reconcile autorouting with current artifact authority
Aug 6, 2026
836b270
fix(task): reconcile autorouting with current dev
Aug 11, 2026
9b3cb04
feat(routing): add a /routing entry point to smart-routing setup
Aug 11, 2026
54123f1
fix(task): unbreak tool-catalog generation and document the tier input
Aug 11, 2026
4e16051
fix(routing): sanitize routing status output and guard the toggle
Aug 11, 2026
8ad73ff
fix(routing): sanitize routing surfaces before terminal rendering
Aug 12, 2026
1e904ca
fix(session): preserve artifacts when a managed publish commits witho…
Aug 12, 2026
47e6fe1
fix(routing): keep untrusted routing text on a single rendered row
Aug 12, 2026
e278db9
test(session): cover the durable preflight path for unproven publicat…
Aug 12, 2026
082553d
fix(session): latch unproven publications against later staged cleanup
Aug 12, 2026
e03a547
fix(routing): guard the non-TUI /routing on|off toggle against scoped…
Aug 12, 2026
c9d1588
fix(task): stop live routed selectors from being sanitized and keep u…
Aug 12, 2026
b3b2142
fix(routing): skip-list the catalog additions dev added since the las…
Aug 13, 2026
9f178d3
feat(routing): derive the smart-routing seed from provider priority
Aug 13, 2026
74a5176
feat(routing): add the single inactive-autorouting warning constant
Aug 13, 2026
874be28
feat(routing)!: cut the preset layer and collapse the routing source …
Aug 13, 2026
a6f8b24
feat(routing): warn once when autorouting is enabled but cannot route
Aug 13, 2026
ed1ae50
fix(routing): drop the unreachable ACP path for the inactive-autorout…
Aug 13, 2026
b6e80d2
feat(routing): deliver the inactive-autorouting notice through the re…
Aug 13, 2026
af014c6
test(routing): prove the inactive-autorouting notice reaches ACP once
Aug 13, 2026
7fa0c75
fix(sdk): drop the unused warning import from the runtime seam
Aug 13, 2026
33855e2
fix(acp): stop consuming the autorouting notice before it is published
Aug 13, 2026
b931988
fix(sdk): keep the autorouting diagnostic off the public host surface
Aug 13, 2026
5b8cb0c
test(sdk): lock the autorouting diagnostic inside the package boundary
Aug 13, 2026
49648c3
test(routing): add the policy-derived golden the plan requires
Aug 13, 2026
2170761
test(routing): close the terminal critic's coverage blockers
Aug 13, 2026
c76787f
test(routing): bind the golden to the shipped projection and isolate …
Aug 13, 2026
af04197
fix(routing): skip-list the catalog additions dev added since the las…
Aug 15, 2026
dc35f85
style(session): drop the stray blank line left by the breadcrumb reso…
Aug 15, 2026
7fd4058
fix(ci): preserve managed replay snapshots
Aug 15, 2026
da875c8
test(acp): await fixture session teardown
Aug 15, 2026
c3c8776
fix(routing): defer staged session breadcrumbs
Aug 15, 2026
c3c31a3
fix(routing): validate autorouting mutation ingress
Aug 15, 2026
8958029
fix(routing): preserve credential session in preflight
Aug 15, 2026
7b394a3
fix(routing): validate SDK tier patches
Aug 15, 2026
0158a4c
fix(routing): preserve typed credential candidate skips
Aug 15, 2026
4fbaaee
fix(routing): retain credential lookup fault evidence
Aug 15, 2026
3682c0b
fix(routing): normalize credential session absence
Aug 15, 2026
9748e9d
fix(routing): preserve first credential lookup failure
Aug 15, 2026
bee5a8b
test(routing): cover one-shot credential faults
Aug 15, 2026
1b79719
fix(routing): preserve undefined credential faults
Aug 15, 2026
aa080f9
fix(routing): match setup provider casing against catalog keys case-i…
Aug 20, 2026
1fb1c6b
fix(routing): cap selector grammar at the routing-evidence bound
Aug 20, 2026
d50b471
fix(session): keep exactly one attempt root through staged commit/dis…
Aug 20, 2026
2cc599e
fix(routing): validate tier-map skip entries and prune dead baseline …
Aug 20, 2026
103fb26
test(routing): pin the grammar-level selector length cap in red-team …
Aug 20, 2026
a77d003
fix(routing): share the selector bound and tighten staged-root invari…
Aug 20, 2026
8ad7a45
fix(routing): close exact-head autorouting review blockers
Aug 21, 2026
07c84fa
fix(routing): require staged-session authority
Aug 21, 2026
b9a6992
fix(routing): honor all thinking suffixes
Aug 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion docs/tools/task.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `agent` | `string` | Yes | Exact agent name for every task item. Resolved at execution time through `discoverAgents(...)`. |
| `tasks` | `Array<{ id: string; description: string; assignment: string }>` | Yes | Batch of small, self-contained task items. `id` max length 48 in schema; duplicate ids are rejected case-insensitively at runtime. |
| `tasks` | `Array<{ id: string; description: string; assignment: string; tier?: "fast" \| "balanced" \| "strong" }>` | Yes | Batch of small, self-contained task items. `id` max length 48 in schema; duplicate ids are rejected case-insensitively at runtime. `tier` is advisory unless autorouting is enabled. |
| `context` | `string` | No | Shared background prepended to every subagent system prompt. Trimmed before use. |
| `schema` | `string` | No | JSON-encoded JTD schema. Overrides agent/session output schema when this mode allows task-level schemas. |
| `isolated` | `boolean` | No | Only present when the tool is created with isolation enabled. Requests isolated execution for the whole batch. |

`tasks[].description` is UI-only. `tasks[].assignment` is the actual per-task instruction.

`tasks[].tier` is inert while `task.autorouting.enabled` is `false`. When autorouting is active it selects the model chain for that item, an omitted `tier` routes as `balanced`, and the routed pin overrides the manual model chain. See [Autorouting](#autorouting).

### Schema-free mode (`task.simple = "schema-free"`)

Same as default, except `schema` is rejected by `validateTaskModeParams(...)` in `packages/coding-agent/src/task/index.ts`.
Expand Down Expand Up @@ -151,6 +153,19 @@ Artifacts and side channels:
- `planner` — read-only sequencing and acceptance criteria.
- `critic` — read-only plan critique and actionability review.

## Autorouting

Off by default. When `task.autorouting.enabled` is `true`, each task item is routed by its `tier` instead of the manual model chain.

- Tier map source: `task.autorouting.tiers` (generated from `task.autorouting.setup`; provider order is seeded from `modelProviderOrder` and the model catalog). The removed `task.autorouting.preset` setting no longer participates in routing; enablement without usable tiers falls back to manual model resolution and reports a diagnostic. Contract in `packages/coding-agent/src/config/autorouting-contract.ts`.
- Selectors are exact `provider/modelId` strings with an optional `:minimal|low|medium|high|xhigh` suffix. Globs, bare model ids, and `pi/<role>` aliases are rejected by the generated config schema.
- An omitted `tier` routes as `balanced`. A tier with no usable chain falls back to manual resolution for that item alone, with a bounded reason recorded.
- Preflight tries at most three unique candidates and only advances on transient failures observed before the run starts; there is no mid-run failover. Failed attempts run in staged sessions and attempt-scoped artifacts, so they leave no durable residue.
- Resolved routing evidence (skips, attempts, terminal outcome) is attached to the task result, receipt, renderer, and task-summary prompt.
- Setup: `/routing` opens the smart-routing panel (declare providers in priority order; chains are generated deterministically from the curated tier map and the model catalog, never from credentials). `/routing on|off|status` manages the toggle and prints effective chains.

With autorouting disabled, model resolution is byte-for-byte unchanged.

## Side Effects
- Filesystem
- Writes `context.md`, `<id>.jsonl`, and `<id>.md` under the session artifacts dir or a temp task dir.
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@
"check:docker-context": "bun scripts/verify-docker-context.ts",
"test:rs": "bun scripts/run-rs-task.ts test:rs",
"check": "bun run --parallel check:ts check:rs",
"check:ts": "bun run check:tools && bun run check:publish-types && bun run check:node20-baseline && bun run check:public-sync && bun run check:schemas && bun run check:sdk-closure && bun run check:docker-context && bun run check:gjc-ui && bun run --workspaces --if-present check",
"check:ts": "bun run check:tools && bun run check:publish-types && bun run check:node20-baseline && bun run check:public-sync && bun run check:schemas && bun run check:sdk-closure && bun run check:docker-context && bun run check:gjc-ui && bun --cwd=packages/coding-agent run check:autorouting-map && bun run --workspaces --if-present check",
"check:tools": "biome check . --no-errors-on-unmatched && tsc -p tsconfig.tools.json --noEmit",
"check:publish-types": "bun scripts/ci-release-publish.ts --check-types",
"check:node20-baseline": "bun scripts/check-node20-baseline.ts",
Expand All @@ -130,7 +130,7 @@
"fix:tools": "biome check --write --unsafe --changed --no-errors-on-unmatched .",
"fix:tools:all": "biome check --write --unsafe --no-errors-on-unmatched .",
"fix:rs": "bun scripts/run-rs-task.ts fix:rs",
"ci:check:full": "bun run check:tools && bun run check:publish-types && bun run check:node20-baseline && bun run check:public-sync && bun run check:schemas && bun run check:sdk-skills && bun run check:docker-context && bun run check:gjc-ui && bun run --workspaces --if-present check",
"ci:check:full": "bun run check:tools && bun run check:publish-types && bun run check:node20-baseline && bun run check:public-sync && bun run check:schemas && bun run check:sdk-skills && bun run check:docker-context && bun run check:gjc-ui && bun --cwd=packages/coding-agent run check:autorouting-map && bun run --workspaces --if-present check",
"ci:build:native": "bun scripts/ci-build-native.ts",
"ci:test:full": "bun run test",
"ci:test:smoke": "bun packages/coding-agent/src/cli.ts --version && bun packages/coding-agent/src/cli.ts --help && bun packages/coding-agent/src/cli.ts stats --help && bun packages/coding-agent/src/cli.ts --smoke-test",
Expand Down
5 changes: 5 additions & 0 deletions packages/coding-agent/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@
### Added

- Added an SDK `automationTools` option for host-owned `browser` and `computer` implementations. External automation retains built-in activation and provenance, receives the normal abort signal, works independently of the default browser/platform gates, and fails closed on custom, extension, or MCP name collisions (#4809).
- Added opt-in Task autorouting that derives deterministic `fast`, `balanced`, and `strong` model tiers from declared provider priority. Tier generation is catalog-scoped and auth-independent, `/routing` opens the smart-routing setup panel, and bounded preflight evidence records candidate skips and acceptance outcomes.

### Changed

- The SDK session index now self-repairs on append when it finds a corrupt suffix, instead of throwing `Cannot append to corrupt session index log` until an operator runs `gjc gc --repair-session-index`. The inline path reuses the same quarantine-backed repair (evidence preserved under `sessions/quarantine`, valid prefix republished), so a single poisoned row — e.g. a stale long-lived broker signing a `lifecycle_terminal` event against an outdated `indexSeq` — no longer wedges every subsequent session launch in that agent dir; the manual `gc` flag remains for non-converging damage.
- Breaking: removed `task.autorouting.preset` and the autorouting preset layer. Preset-only autorouting configuration is inactive until tiers are generated from smart-routing setup; public routing contracts no longer expose preset/source fields, and routing notes contain tier, fallback, and resume components only.
- Autorouting selectors now use one shared provider-qualified grammar and 256-character bound across runtime validation, generated tiers, routing evidence, and JSON Schema. Settings-derived tiers report provenance only when the provenance is valid; malformed provenance fails closed as hand-authored tiers.
- Workflow handoffs out of autoresearch now transit properly: `/skill:deep-interview`, `/skill:ralplan`, and `/skill:ultragoal` chain from any live autoresearch phase (`intake`/`research`/`verdict`), and ralplan's `final` phase chains via its manifest terminal states; ralplan/ultragoal live phases still require the explicit write-to-handoff step. `gjc autoresearch clear` remains the finalize-only exit.
- Breaking: autoresearch spec intake is now the explicit `intake` verb (`gjc autoresearch intake --spec <path>`; the bare `--spec` flag form still works). The ambiguous `handoff` verb token is rejected with disambiguation hints instead of silently becoming a cold-intake goal — workflow handoff lives at `/skill:<callee>` (or `gjc state autoresearch handoff --to <callee>`).
- Subagents now start correctly on Bun 1.4 when extension tools provide custom renderers. `RegisteredToolAdapter` installs its renderer adapters before proxying the definition, avoiding a write through the proxy's getter-only property while preserving the no-renderer fallback path.
Expand All @@ -19,6 +22,8 @@
- SDK `goal.list/get` no longer reports `resource_gone` when an in-flight session's live goal projection is temporarily unavailable. It recovers the latest authoritative goal mode state from the current session branch after runtime recreation or session replacement, while goal-less sessions return an explicit `no_active_goal` diagnostic instead of being confused with snapshot-store loss (#4824).

- `/logout` (and `gjc accounts logout`) now remove stored API-key credentials, not just OAuth rows. The interactive logout and the CLI only enumerated `oauth` inventory, so API-key logins (OpenCode Go/Zen, Cursor, Venice, DeepSeek, …) were rejected with `API-key credentials are not managed here`; both paths now remove stored credentials of either kind.
- Sanitized autorouting selectors and routing-summary attributes before persistence and `noEscape` prompt interpolation, including control characters and Unicode line separators. Generated tier scope now retains multimodal text-capable models while excluding only image-generation-only catalog entries.
- Autorouting preflight now uses a staged publication when durable session/artifact authority exists and an explicit artifact-only acceptance path when it does not, so a valid synchronous Task without a child session file is not rejected at the acceptance fence.
- Queued SDK prompts now retain their dispatch-time ownership across selection fences instead of reclassifying from a contradictory later streaming snapshot. Fresh promotion, earlier follow-up ordering, and terminal-abort cancellation are reachable again, while `/btw` test fixtures now model the user-drainable queue count required by the current empty-submit contract.
- Coordinator stop and idle-reap now initialize canonical namespace state before reading durable deletion recovery. Fresh or upgraded projection-only sessions were misreported as `state_corrupt` before the broker close was attempted, and completed deletion receipts were excluded from the idempotent missing-session lookup; both paths now preserve strict malformed-state rejection while allowing safe cleanup and replay.
- Fixed provider safety-stop classification being lost before session persistence (#4777). The managed provider-envelope boundary now preserves only the allowlisted `provider_safety_stop` kind, typed safety stops remain terminal even with transport facts on a multi-model fallback chain, and the regression e2e test is selected both by the focused affected-path route and exactly one normal coding-agent shard.
Expand Down
3 changes: 3 additions & 0 deletions packages/coding-agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"check": "biome check . && bun run check:types",
"check:runtime": "bun run generate-hotkeys-docs --check && bun run verify:sdk-canonicalization",
"check:types": "tsc -p tsconfig.json --noEmit",
"check:autorouting-map": "bun scripts/check-autorouting-tier-map.ts",
"lint": "biome lint .",
"test": "bun test",
"generate-hotkeys-docs": "bun scripts/generate-hotkeys-docs.ts",
Expand Down Expand Up @@ -149,6 +150,8 @@
"./sdk/host/control/runtime-gate.js": null,
"./sdk/host/control/runtime-gate": null,
"./sdk/host/control/runtime-gate/*": null,
"./sdk/host/internal-autorouting-state.js": null,
"./sdk/host/internal-autorouting-state": null,
"./sdk/lifecycle/broker-client.js": null,
"./sdk/lifecycle/broker-client": null,
"./sdk/lifecycle/broker-client/*": null,
Expand Down
183 changes: 183 additions & 0 deletions packages/coding-agent/scripts/check-autorouting-tier-map.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
#!/usr/bin/env bun

import * as fs from "node:fs/promises";
import * as path from "node:path";
import { RETIRED_MODEL_KEYS } from "../../ai/src/model-retirements";
import { isValidAutoroutingSelector } from "../src/config/autorouting-contract";
import {
CURATED_TIER_LABELS,
type CuratedTierLabels,
TIER_MAP_SKIP_LIST,
type TierMapSkipList,
} from "../src/config/autorouting-tier-map";

export const BASELINE_SKIP_RATIONALE = "pre-feature baseline; not yet curated";
const DISCOVERY_ONLY_PROVIDERS = new Set(["ollama", "vllm"]);
const RETIRED_KEYS = new Set<string>(RETIRED_MODEL_KEYS);

export type CommittedCatalogModel = {
id?: unknown;
provider?: unknown;
output?: unknown;
[key: string]: unknown;
};

export type CommittedCatalog = Record<string, Record<string, CommittedCatalogModel>>;

export type AutoroutingTierMapGateReport = {
inScopeKeys: string[];
labeledKeys: string[];
skippedKeys: string[];
unlabeledKeys: string[];
invalidLabelKeys: string[];
outOfScopeLabelKeys: string[];
invalidSkipKeys: string[];
staleSkipKeys: string[];
baselineSkipCount: number;
};

function modelKey(provider: string, id: string): string {
return `${provider}/${id}`;
}

function isImageGenerationOnly(model: CommittedCatalogModel): boolean {
return Array.isArray(model.output) && model.output.length > 0 && model.output.every(output => output === "image");
}

export function isInAutoroutingGateScope(provider: string, id: string, model: CommittedCatalogModel): boolean {
const key = modelKey(provider, id);
return (
isValidAutoroutingSelector(key) &&
!RETIRED_KEYS.has(key) &&
!DISCOVERY_ONLY_PROVIDERS.has(provider) &&
!isImageGenerationOnly(model)
);
}

export function committedCatalogKeys(catalog: CommittedCatalog): string[] {
const keys: string[] = [];
for (const [provider, models] of Object.entries(catalog)) {
if (models === null || typeof models !== "object" || Array.isArray(models)) continue;
for (const [id, model] of Object.entries(models)) {
if (isInAutoroutingGateScope(provider, id, model)) keys.push(modelKey(provider, id));
}
}
return keys.sort((left, right) => left.localeCompare(right));
}

export function getAutoroutingTierMapGateReport(
catalog: CommittedCatalog,
labels: CuratedTierLabels = CURATED_TIER_LABELS,
skips: TierMapSkipList = TIER_MAP_SKIP_LIST,
): AutoroutingTierMapGateReport {
const catalogKeys = new Set(committedCatalogKeys(catalog));
const labeledKeys = Object.keys(labels).sort((left, right) => left.localeCompare(right));
const skippedKeys = Object.keys(skips).sort((left, right) => left.localeCompare(right));
const invalidLabelKeys = labeledKeys.filter(key => !catalogKeys.has(key));
const outOfScopeLabelKeys = labeledKeys.filter(key => {
const separator = key.indexOf("/");
const provider = separator > 0 ? key.slice(0, separator) : key;
const id = separator > 0 ? key.slice(separator + 1) : "";
const rawModel = catalog[provider]?.[id];
return rawModel !== undefined && !isInAutoroutingGateScope(provider, id, rawModel);
});
const unlabeledKeys = [...catalogKeys].filter(key => !Object.hasOwn(labels, key) && !Object.hasOwn(skips, key));
const inScopeKeys = [...catalogKeys];
const inScopeSkipped = skippedKeys.filter(key => catalogKeys.has(key));
const invalidSkipKeys = skippedKeys.filter(key => {
const entry = (skips as Record<string, { rationale?: unknown }>)[key];
const rationale = entry?.rationale;
return typeof rationale !== "string" || rationale.trim().length === 0 || !isValidAutoroutingSelector(key);
});
const staleSkipKeys = skippedKeys.filter(key => !catalogKeys.has(key) && !invalidSkipKeys.includes(key));
// A key that is both labeled and skipped would let a curated tier assignment hide
// behind a skip rationale; it is an invalid skip, not a stale one.
const bothLabeledAndSkipped = skippedKeys.filter(key => Object.hasOwn(labels, key));
invalidSkipKeys.push(...bothLabeledAndSkipped.filter(key => !invalidSkipKeys.includes(key)));
return {
inScopeKeys,
labeledKeys,
skippedKeys: inScopeSkipped,
unlabeledKeys: unlabeledKeys.sort((left, right) => left.localeCompare(right)),
invalidLabelKeys,
outOfScopeLabelKeys,
invalidSkipKeys,
staleSkipKeys,
baselineSkipCount: inScopeSkipped.filter(
key => (skips as Record<string, { baseline?: true }>)[key]?.baseline === true,
).length,
};
}

export function findUnlabeledAutoroutingKeys(
catalog: CommittedCatalog,
labels: CuratedTierLabels = CURATED_TIER_LABELS,
skips: TierMapSkipList = TIER_MAP_SKIP_LIST,
): string[] {
return getAutoroutingTierMapGateReport(catalog, labels, skips).unlabeledKeys;
}

export type AutoroutingTierMapGateResult = {
ok: boolean;
report: AutoroutingTierMapGateReport;
};

export function checkAutoroutingTierMap(
catalog: CommittedCatalog,
labels: CuratedTierLabels = CURATED_TIER_LABELS,
skips: TierMapSkipList = TIER_MAP_SKIP_LIST,
): AutoroutingTierMapGateResult {
const report = getAutoroutingTierMapGateReport(catalog, labels, skips);
const ok =
report.unlabeledKeys.length === 0 &&
report.invalidLabelKeys.length === 0 &&
report.outOfScopeLabelKeys.length === 0 &&
report.invalidSkipKeys.length === 0 &&
report.staleSkipKeys.length === 0;
return { ok, report };
}

export async function loadCommittedCatalog(
repoRoot = path.resolve(import.meta.dir, "../../.."),
): Promise<CommittedCatalog> {
const catalogPath = path.join(repoRoot, "packages/ai/src/models.json");
return JSON.parse(await fs.readFile(catalogPath, "utf8")) as CommittedCatalog;
}

export async function runAutoroutingTierMapGate(repoRoot?: string): Promise<AutoroutingTierMapGateResult> {
return checkAutoroutingTierMap(await loadCommittedCatalog(repoRoot));
}

export const checkTierMap = checkAutoroutingTierMap;
export const findUnlabeledKeys = findUnlabeledAutoroutingKeys;
export const runTierMapGate = runAutoroutingTierMapGate;

function printFailure(report: AutoroutingTierMapGateReport): void {
const failures = [
...new Set([
...report.unlabeledKeys,
...report.invalidLabelKeys,
...report.outOfScopeLabelKeys,
...report.invalidSkipKeys,
...report.staleSkipKeys,
]),
].sort((a, b) => a.localeCompare(b));
console.error("Autorouting tier-map gate failed.");
console.error("Offending provider/model-id keys:");
for (const key of failures) console.error(`- ${key}`);
console.error(
"Add each new key to CURATED_TIER_LABELS with reviewed tier/rank data, or add it to TIER_MAP_SKIP_LIST with a non-empty rationale; remove skip entries whose key left the catalog or the selector grammar.",
);
}

if (import.meta.main) {
const result = await runAutoroutingTierMapGate();
if (!result.ok) {
printFailure(result.report);
process.exitCode = 1;
} else {
console.log(
`Autorouting tier-map gate passed: ${result.report.inScopeKeys.length} in-scope keys; ${result.report.baselineSkipCount} baseline skips.`,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ const LOCKED_EXCLUSIONS: Readonly<Record<string, string>> = {
"internal terminal-abort bus seam, threaded via terminalAbortSeams; not a user-facing SDK control seam",
"agent_session:abortPromptAndWaitWithTerminal":
"internal terminal-abort fencing seam, threaded via terminalAbortSeams; not a user-facing SDK control seam",
"slash_command:routing":
"visual/local-only autorouting settings toggle and smart-routing panel entry, not a user-facing SDK control seam",
"slash_command:settings": "visual/local-only command, not a user-facing SDK control seam",
"slash_command:theme": "visual/local-only command, not a user-facing SDK control seam",
"slash_command:copy": "visual/local-only command, not a user-facing SDK control seam",
Expand Down
2 changes: 2 additions & 0 deletions packages/coding-agent/scripts/generate-tool-catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ function makeSettings() {
return {};
},
getNotificationSettingsSnapshot: () => ({ enabled: false, telegram: {}, discord: {}, slack: {} }),
// The catalog documents the default configuration, where autorouting is off.
getEffectiveAutorouting: () => ({ active: false }),
};
}

Expand Down
Loading
Loading