@crouter/api 0.3.387 → 0.3.388

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.
Files changed (130) hide show
  1. package/dist/api/__tests__/integration/client.test.js +97 -0
  2. package/dist/api/client.d.ts +7 -0
  3. package/dist/api/client.js +40 -21
  4. package/dist/core/asset-root.d.ts +7 -0
  5. package/dist/core/asset-root.js +18 -0
  6. package/dist/core/canvas/boot-id.d.ts +6 -0
  7. package/dist/core/canvas/boot-id.js +26 -0
  8. package/dist/core/canvas/paths.d.ts +72 -0
  9. package/dist/core/canvas/paths.js +163 -0
  10. package/dist/core/canvas/pid.d.ts +391 -0
  11. package/dist/core/canvas/pid.js +948 -0
  12. package/dist/core/command-plugins/bundle.d.ts +149 -0
  13. package/dist/core/command-plugins/bundle.js +588 -0
  14. package/dist/core/command-plugins/endpoint.d.ts +24 -0
  15. package/dist/core/command-plugins/endpoint.js +51 -0
  16. package/dist/core/config.d.ts +233 -0
  17. package/dist/core/config.js +1120 -0
  18. package/dist/core/env-name.d.ts +6 -0
  19. package/dist/core/env-name.js +9 -0
  20. package/dist/core/errors.d.ts +38 -0
  21. package/dist/core/errors.js +90 -0
  22. package/dist/core/events/emit.d.ts +6 -0
  23. package/dist/core/events/emit.js +42 -0
  24. package/dist/core/events/envelope.d.ts +2 -0
  25. package/dist/core/events/envelope.js +84 -0
  26. package/dist/core/events/errors.d.ts +4 -0
  27. package/dist/core/events/errors.js +69 -0
  28. package/dist/core/events/operation-id.d.ts +4 -0
  29. package/dist/core/events/operation-id.js +24 -0
  30. package/dist/core/events/serialize.d.ts +4 -0
  31. package/dist/core/events/serialize.js +199 -0
  32. package/dist/core/events/source.d.ts +16 -0
  33. package/dist/core/events/source.js +31 -0
  34. package/dist/core/events/types.d.ts +68 -0
  35. package/dist/core/events/types.js +11 -0
  36. package/dist/core/exclusive-lock.d.ts +34 -0
  37. package/dist/core/exclusive-lock.js +197 -0
  38. package/dist/core/fs-utils.d.ts +44 -0
  39. package/dist/core/fs-utils.js +208 -0
  40. package/dist/core/help.d.ts +309 -0
  41. package/dist/core/help.js +406 -0
  42. package/dist/core/human/page-catalog.d.ts +57 -0
  43. package/dist/core/human/page-catalog.js +172 -0
  44. package/dist/core/installed-plugins.d.ts +2 -0
  45. package/dist/core/installed-plugins.js +79 -0
  46. package/dist/core/io.d.ts +122 -0
  47. package/dist/core/io.js +373 -0
  48. package/dist/core/keybindings/attach-control.d.ts +49 -0
  49. package/dist/core/keybindings/attach-control.js +42 -0
  50. package/dist/core/keybindings/catalog.d.ts +18 -0
  51. package/dist/core/keybindings/catalog.js +257 -0
  52. package/dist/core/keybindings/types.d.ts +42 -0
  53. package/dist/core/keybindings/types.js +1 -0
  54. package/dist/core/layout.d.ts +26 -0
  55. package/dist/core/layout.js +94 -0
  56. package/dist/core/locked-file.d.ts +27 -0
  57. package/dist/core/locked-file.js +118 -0
  58. package/dist/core/log.d.ts +9 -0
  59. package/dist/core/log.js +89 -0
  60. package/dist/core/manifest.d.ts +5 -0
  61. package/dist/core/manifest.js +15 -0
  62. package/dist/core/plugin-env.d.ts +8 -0
  63. package/dist/core/plugin-env.js +31 -0
  64. package/dist/core/plugin-extensions.d.ts +29 -0
  65. package/dist/core/plugin-extensions.js +191 -0
  66. package/dist/core/plugin-swap-lock.d.ts +9 -0
  67. package/dist/core/plugin-swap-lock.js +31 -0
  68. package/dist/core/preview-result-path.d.ts +4 -0
  69. package/dist/core/preview-result-path.js +26 -0
  70. package/dist/core/profiles/env-store.d.ts +22 -0
  71. package/dist/core/profiles/env-store.js +163 -0
  72. package/dist/core/profiles/fuzzy-match.d.ts +19 -0
  73. package/dist/core/profiles/fuzzy-match.js +92 -0
  74. package/dist/core/profiles/manifest.d.ts +120 -0
  75. package/dist/core/profiles/manifest.js +529 -0
  76. package/dist/core/rate-limit-scope.d.ts +25 -0
  77. package/dist/core/rate-limit-scope.js +64 -0
  78. package/dist/core/render.d.ts +12 -0
  79. package/dist/core/render.js +138 -0
  80. package/dist/core/resolver.d.ts +14 -0
  81. package/dist/core/resolver.js +111 -0
  82. package/dist/core/runtime/branded-host.d.ts +25 -0
  83. package/dist/core/runtime/branded-host.js +264 -0
  84. package/dist/core/runtime/broker/daemon-ops.d.ts +65 -0
  85. package/dist/core/runtime/broker/daemon-ops.js +177 -0
  86. package/dist/core/runtime/broker/signal-stream.d.ts +30 -0
  87. package/dist/core/runtime/broker/signal-stream.js +149 -0
  88. package/dist/core/scope.d.ts +32 -0
  89. package/dist/core/scope.js +184 -0
  90. package/dist/core/scoped-state/db.d.ts +17 -0
  91. package/dist/core/scoped-state/db.js +247 -0
  92. package/dist/core/scoped-state/migrate.d.ts +8 -0
  93. package/dist/core/scoped-state/migrate.js +187 -0
  94. package/dist/core/scoped-state/paths.d.ts +9 -0
  95. package/dist/core/scoped-state/paths.js +27 -0
  96. package/dist/core/scoped-state/profiles.d.ts +27 -0
  97. package/dist/core/scoped-state/profiles.js +93 -0
  98. package/dist/core/scoped-state/providers.d.ts +24 -0
  99. package/dist/core/scoped-state/providers.js +19 -0
  100. package/dist/core/scoped-state/schema.d.ts +6 -0
  101. package/dist/core/scoped-state/schema.js +43 -0
  102. package/dist/core/scoped-state/settings.d.ts +28 -0
  103. package/dist/core/scoped-state/settings.js +83 -0
  104. package/dist/core/spaces/open-beneath.d.ts +71 -0
  105. package/dist/core/spaces/open-beneath.js +581 -0
  106. package/dist/core/sqlite-statements.d.ts +4 -0
  107. package/dist/core/sqlite-statements.js +17 -0
  108. package/dist/core/subscription-state.d.ts +121 -0
  109. package/dist/core/subscription-state.js +287 -0
  110. package/dist/core/user-settings.d.ts +377 -0
  111. package/dist/core/user-settings.js +458 -0
  112. package/dist/daemon/broker-signals/bus.d.ts +30 -0
  113. package/dist/daemon/broker-signals/bus.js +87 -0
  114. package/dist/daemon/manage.d.ts +176 -0
  115. package/dist/daemon/manage.js +664 -0
  116. package/dist/daemon/pidfile.d.ts +8 -0
  117. package/dist/daemon/pidfile.js +37 -0
  118. package/dist/daemon/startup-policy.d.ts +1 -0
  119. package/dist/daemon/startup-policy.js +1 -0
  120. package/dist/native/linux.d.ts +29 -0
  121. package/dist/native/linux.js +20 -0
  122. package/dist/shared/env.d.ts +116 -0
  123. package/dist/shared/env.js +271 -0
  124. package/dist/shared/inbox-entry-body.d.ts +22 -0
  125. package/dist/shared/inbox-entry-body.js +116 -0
  126. package/dist/shared/working-activity.d.ts +9 -0
  127. package/dist/shared/working-activity.js +27 -0
  128. package/dist/types.d.ts +562 -0
  129. package/dist/types.js +186 -0
  130. package/package.json +1 -1
@@ -0,0 +1,51 @@
1
+ import { validateEnvVarName } from '../env-name.js';
2
+ /** Hosts `http:` is allowed for: loopback, Docker's host alias, and any
3
+ * `*.localhost` name (RFC 6761 reserves it for local use; the local
4
+ * directory and provider stacks serve `http://<svc>.localhost:<port>`). */
5
+ function isLoopbackHostname(hostname) {
6
+ return hostname === 'localhost' || hostname.endsWith('.localhost') || hostname === '127.0.0.1' || hostname === '[::1]' || hostname === '::1' || hostname === 'host.docker.internal';
7
+ }
8
+ export function validateEndpoint(endpoint) {
9
+ const errors = [];
10
+ if (typeof endpoint !== 'string')
11
+ errors.push('endpoint must be a string');
12
+ else {
13
+ try {
14
+ const url = new URL(endpoint);
15
+ if (url.protocol !== 'https:' && !(url.protocol === 'http:' && isLoopbackHostname(url.hostname)))
16
+ errors.push('endpoint must be an absolute https:// URL (http:// permitted for loopback and *.localhost hosts only; no credentials or fragment)');
17
+ else if (url.username || url.password || url.hash)
18
+ errors.push('endpoint must be an absolute https:// URL (http:// permitted for loopback and *.localhost hosts only; no credentials or fragment)');
19
+ }
20
+ catch {
21
+ errors.push('endpoint must be an absolute https:// URL (http:// permitted for loopback and *.localhost hosts only; no credentials or fragment)');
22
+ }
23
+ }
24
+ return { isValid: errors.length === 0, errors };
25
+ }
26
+ export function normalizeEndpoint(endpoint) {
27
+ try {
28
+ const url = new URL(endpoint);
29
+ const pathname = url.pathname.length > 1 && url.pathname.endsWith('/') ? url.pathname.slice(0, -1) : url.pathname;
30
+ return `${url.protocol}//${url.host}${pathname}${url.search}`;
31
+ }
32
+ catch {
33
+ return endpoint;
34
+ }
35
+ }
36
+ export function validateHttpPluginTransport(raw) {
37
+ const errors = [];
38
+ if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
39
+ return { errors: ['transport must be an object'] };
40
+ const transport = raw;
41
+ const unknown = Object.keys(transport).filter((key) => !['kind', 'endpoint', 'authEnv'].includes(key));
42
+ if (unknown.length)
43
+ errors.push(`transport has unknown keys: ${unknown.join(', ')}`);
44
+ if (transport.kind !== 'http')
45
+ errors.push('transport.kind must be "http"');
46
+ const endpoint = validateEndpoint(transport.endpoint);
47
+ errors.push(...endpoint.errors);
48
+ if (transport.authEnv !== undefined)
49
+ errors.push(...validateEnvVarName(transport.authEnv).errors);
50
+ return errors.length ? { errors } : { transport: { kind: 'http', endpoint: normalizeEndpoint(transport.endpoint), ...(transport.authEnv !== undefined ? { authEnv: transport.authEnv } : {}) }, errors };
51
+ }
@@ -0,0 +1,233 @@
1
+ import type { PageComponentRegistration, Scope, ScopeConfig, ScopeState, HumanActionConfig, KindConfig, ModelLaddersConfig, ModelProvider, ModelStrength, ModelRouteConfig, ModelRoutingConfig, ProviderOptionsConfig } from '../types.js';
2
+ import { type BindingId } from './keybindings/catalog.js';
3
+ import type { RotationConfig } from './subscription-state.js';
4
+ export declare function configPath(scope: Scope): string | null;
5
+ export declare function statePath(scope: Scope): string | null;
6
+ export interface BrokerConfigSnapshot {
7
+ user: ResolvedScopeConfig;
8
+ launch: MergedLaunchConfig;
9
+ rotation: RotationConfig;
10
+ }
11
+ export declare function installBrokerConfigSnapshot(snapshot: BrokerConfigSnapshot): void;
12
+ export declare function brokerConfigSnapshot(): BrokerConfigSnapshot | undefined;
13
+ export declare function readConfig(scope: Scope): ResolvedScopeConfig;
14
+ export declare function readConfigAtRoot(root: string): ResolvedScopeConfig;
15
+ export declare function readState(scope: Scope): ScopeState;
16
+ export type RawSettingsTarget = {
17
+ scope: 'user';
18
+ } | {
19
+ scope: 'profile';
20
+ profileId: string;
21
+ };
22
+ export declare class InvalidRawSettingsError extends Error {
23
+ constructor(cause: unknown);
24
+ }
25
+ export declare function readRawSettingsForTarget(target: RawSettingsTarget): {
26
+ settings: Record<string, unknown> | null;
27
+ revision: string;
28
+ };
29
+ export declare function replaceRawSettingsForTarget(target: RawSettingsTarget, next: Record<string, unknown>, ifRevision: string): {
30
+ revision: string;
31
+ changed: boolean;
32
+ };
33
+ type RawConfig = Record<string, unknown>;
34
+ export declare function updateRawConfigAtomically(scope: Scope, mutate: (latest: RawConfig) => RawConfig): RawConfig;
35
+ export declare function writeConfig(scope: Scope, config: ScopeConfig): void;
36
+ export declare function writeRawConfig(scope: Scope, config: Partial<ScopeConfig>): void;
37
+ export declare function writeState(scope: Scope, state: ScopeState): void;
38
+ export declare function ensureScopeInitialized(scope: Scope, root: string): void;
39
+ /** Normalize the user-owned list of attach actions allowed to bridge through an
40
+ * occupied tmux root binding. Unknown and non-attach ids have no effect. */
41
+ export declare function normalizeTmuxPassthrough(raw: unknown): BindingId[];
42
+ type LegacyModelRouteConfig = Omit<ModelRouteConfig, 'models'> & {
43
+ models: Partial<Record<ModelStrength, string>>;
44
+ };
45
+ /** Read configuration exposes legacy sparse routes separately during the
46
+ * one-release cutover; launch code only treats `modelRoutes` as complete. */
47
+ export type ResolvedScopeConfig = ScopeConfig & {
48
+ legacyModelRoutes?: Record<string, LegacyModelRouteConfig>;
49
+ };
50
+ /** One malformed `bin` declaration. The read path retains these as doctor
51
+ * findings rather than letting malformed config break a node launch. */
52
+ export interface BinDeclarationIssue {
53
+ name: string;
54
+ message: string;
55
+ }
56
+ /** Validate raw `bin` declaration shape without touching the filesystem.
57
+ * Install uses these findings as a hard gate for plugin manifests; scope
58
+ * resolution reports them through `crtr sys doctor` and continues. */
59
+ export declare function binDeclarationIssues(raw: unknown): BinDeclarationIssue[];
60
+ /** Normalize one raw `bin` block (a scope `config.json`'s or a plugin
61
+ * manifest's) to name → root-relative path. Invalid entries never become
62
+ * commands; `binDeclarationIssues` keeps them visible to Doctor. */
63
+ export declare function normalizeBin(raw: unknown): Record<string, string>;
64
+ export interface HumanActionDeclarationIssue {
65
+ name: string;
66
+ reason: string;
67
+ block?: true;
68
+ }
69
+ /** Validate `humanActions` declaration shape without touching the filesystem.
70
+ * Filesystem checks happen only when resolving an action or running Doctor. */
71
+ export declare function humanActionDeclarationIssues(raw: unknown): HumanActionDeclarationIssue[];
72
+ /** Normalize valid action declarations for scope-local config inspection.
73
+ * Invalid entries remain available through raw reads so resolution and Doctor
74
+ * can report them rather than silently treating them as absent. */
75
+ export declare function normalizeHumanActions(raw: unknown): Record<string, HumanActionConfig>;
76
+ /** STRICT install-time validation for a plugin-declared `bin` block. Target
77
+ * filesystem state is checked later by Doctor because it can change after
78
+ * install. */
79
+ export declare function invalidPluginBinReasons(raw: unknown): string[];
80
+ /** Normalize a plugin manifest's advisory executable requirements. Invalid
81
+ * declarations stay out of the read path; source installs reject them through
82
+ * `invalidPluginRequiresReasons`. */
83
+ export declare function normalizeRequires(raw: unknown): Record<string, string>;
84
+ /** STRICT install-time validation for a plugin's advisory `requires` block.
85
+ * A missing executable is advisory; only malformed declarations fail install. */
86
+ export declare function invalidPluginRequiresReasons(raw: unknown): string[];
87
+ /** The ACTIVE page-component catalog every authoring, validation, daemon,
88
+ * inbox, and TUI consumer resolves: the user scope's own `page_components`
89
+ * entries plus the contributions of every ENABLED plugin installed under that
90
+ * same root, plugin name-sorted for determinism (directory listing order is
91
+ * filesystem-dependent). Installing, updating, or removing a plugin therefore
92
+ * adds, changes, or retires its components with no config edit.
93
+ *
94
+ * User scope only, deliberately: this catalog is read from the daemon, the
95
+ * CLI, and the inbox TUI, whose ambient cwds differ, and a page slot whose
96
+ * validity depended on which directory a process happened to start in would
97
+ * validate in one surface and fail in another. `page_components` has always
98
+ * been a user-scope block (`readConfig('user')`); its plugin channel keeps
99
+ * that boundary.
100
+ *
101
+ * Throws on a duplicate kind or derived JSX tag across sources, and on a
102
+ * plugin whose block is malformed — matching the existing catalog validation
103
+ * style, where a bad `page_components` block already fails config read rather
104
+ * than silently dropping a component the product expects to be authorable.
105
+ * Install-time validation (`invalidPageComponentsReasons`) is what keeps a
106
+ * malformed block from ever reaching this path. */
107
+ export declare function resolvePageComponents(): PageComponentRegistration[];
108
+ /** STRICT install-time validation for a plugin-declared `kinds` block —
109
+ * the loud counterpart to `mergeKinds`'s silent read-time dropping. Read
110
+ * paths must never throw on bad config, but an INSTALL delivering a bad
111
+ * block must fail the install, not ship a kind that silently never
112
+ * registers. Returns one human-readable reason per defect; empty = valid.
113
+ * Used by the archive-bundle validator (`command-plugins/bundle.ts`) on
114
+ * `bundle.json`'s optional `kinds` member. */
115
+ export declare function invalidPluginKindsReasons(raw: unknown): string[];
116
+ /** Raw (un-defaulted) partial config for one scope, or null if the scope has
117
+ * no root or no config.json. Used by `readMergedLaunchConfig` to layer
118
+ * scopes onto each other WITHOUT each scope's own default-fill masking a
119
+ * lower-precedence scope's real customization (see that function's comment
120
+ * for why `readConfig(scope)`, which already defaults-fills, is unusable
121
+ * for cross-scope layering). */
122
+ export declare function readRawConfigAtRoot(root: string): Partial<ScopeConfig> | null;
123
+ export declare function readRawScopeConfig(scope: Scope): Partial<ScopeConfig> | null;
124
+ export interface MergedLaunchConfig {
125
+ kinds: Record<string, KindConfig>;
126
+ modelLadders: ModelLaddersConfig;
127
+ /** Per-provider behavior options keyed by runtime provider id, merged
128
+ * per provider across scopes (project stack > profile > user). */
129
+ providerOptions: Record<string, ProviderOptionsConfig>;
130
+ /** Complete declared routes. Every route here has all four strengths. */
131
+ modelRoutes?: Record<string, ModelRouteConfig>;
132
+ /** Read-only compatibility view for sparse routes written before complete
133
+ * routes were required. New route persistence never produces this shape. */
134
+ legacyModelRoutes?: Record<string, LegacyModelRouteConfig>;
135
+ modelRouting?: ModelRoutingConfig;
136
+ /** The merged `spawnEnv.allow` set (project stack > profile > user >
137
+ * builtin, additive) — read by `buildOperationalEnvBase`
138
+ * (`core/runtime/spawn-env.ts`) as source C of the broker spawn-env
139
+ * boundary. */
140
+ spawnEnv: {
141
+ allow: string[];
142
+ };
143
+ }
144
+ /** Merge launch knobs (`kinds`, `modelLadders`) across scopes in
145
+ * project stack > profile > user > builtin precedence — the same precedence
146
+ * order used for memory resolution. For `kinds` only, each plugin-bearing
147
+ * scope (user, each project root) additionally layers its enabled plugins'
148
+ * manifest `kinds` blocks directly BELOW that scope's own raw config — see
149
+ * `layerPluginKinds`. A kind or ladder cell declared at a
150
+ * more-specific scope shadows the same key from a less-specific scope; the
151
+ * project STACK (`findProjectScopeRoots` — every ancestor `.crouter/`,
152
+ * widened by a selected profile's `projects`) layers nearest-root-strongest;
153
+ * a key no scope declares falls through to the builtin default registry
154
+ * (`defaultScopeConfig().kinds` / `.modelLadders`).
155
+ *
156
+ * Layers RAW (un-defaulted) partial config per scope, not `readConfig(scope)`
157
+ * — `readConfig` already fills in every default kind for a scope that
158
+ * customizes even one, so naively overlaying two already-defaulted
159
+ * `ScopeConfig.kinds` objects would let an untouched project-scope kind
160
+ * (silently defaulted) clobber a real user-scope customization of that same
161
+ * kind. Layering the raw partials avoids that.
162
+ *
163
+ * Callers (launch, kind registry) go through this function rather than
164
+ * `readConfig` directly, so `ScopeConfig.kinds`/`modelLadders`/tools/
165
+ * extensions/`availableTo` all honor the same profile + multi-project
166
+ * precedence.
167
+ *
168
+ * `targetCwd`/`targetProfileId` default to this PROCESS's own ambient cwd
169
+ * and `CRTR_PROFILE_ID` — correct for a caller resolving config for itself
170
+ * (front-door commands, kind listing). A caller resolving config on behalf
171
+ * of a DIFFERENT node (the broker spawn-env boundary or cron executor — both
172
+ * run inside the daemon, whose ambient cwd/profile is the daemon's own, not
173
+ * the target node's) MUST pass the target's own cwd/profile explicitly, or
174
+ * `spawnEnv.allow` resolves from the wrong scope entirely (the C-1
175
+ * follow-up fix this parameterization exists for). */
176
+ export declare function readMergedLaunchConfig(targetCwd?: string, targetProfileId?: string | null): MergedLaunchConfig;
177
+ /** The effective `KindConfig` for one full kind string (top-level, e.g.
178
+ * `developer`, or sub-kind, e.g. `audit/security`), across
179
+ * project > user > builtin precedence. Returns `undefined` for a kind no
180
+ * scope registers — existence is deliberately NOT validated here (kind
181
+ * existence/launch-menu enumeration is a caller concern); this only
182
+ * resolves the config for a kind the caller already knows about. */
183
+ export declare function resolveKindConfig(kind: string): KindConfig | undefined;
184
+ /** The sub-kinds available to spawn FROM a given top-level kind — every
185
+ * registered sub-kind (full path contains `/`) whose `availableTo` (default:
186
+ * its own top-level ancestor, e.g. `audit/security` defaults to
187
+ * `['audit']`) includes `kind` or `'*'`. The single source both `sys
188
+ * prompt-review --list`'s `subPersonas` metadata and the live sub-persona
189
+ * spawn-menu splice (`core/substrate/delivery/render-boot.ts`) read, so the two can never
190
+ * drift apart. Sorted by full kind name for stable rendering. */
191
+ export declare function subKindsAvailableTo(kind: string): {
192
+ kind: string;
193
+ whenToUse: string;
194
+ }[];
195
+ /** Persist an EXPLICIT sparse `modelLadders` intent at one config root: the
196
+ * patch's rungs (and `defaultProvider`) are merged over whatever sparse ladder
197
+ * is already on disk, and every untouched rung stays absent so it keeps
198
+ * re-deriving from the running build's registry.
199
+ *
200
+ * Ladder edits must come through here rather than `updateConfig`, because that
201
+ * path infers what to persist by diffing the mutated config against the merged
202
+ * baseline: setting a rung to the value that happens to be today's compiled
203
+ * default produces an empty diff and silently persists nothing, so the choice
204
+ * evaporates as soon as the default moves. Explicit intent is recorded as
205
+ * written. Mirrors `persistDefaultKindModel`. */
206
+ export declare function persistModelLadders(root: string, patch: {
207
+ defaultProvider?: ModelProvider;
208
+ anthropic?: Partial<Record<ModelStrength, string>>;
209
+ openai?: Partial<Record<ModelStrength, string>>;
210
+ }): void;
211
+ /** Persist one complete route at one exact config root. The route is the only
212
+ * raw route entry touched; unrelated routes and local ladder defaults remain
213
+ * absent. Pass `undefined` to remove this scope's entry, which resumes lower
214
+ * scope inheritance rather than introducing a tombstone. */
215
+ export declare function persistModelRoute(root: string, routeId: string, route: ModelRouteConfig | undefined): void;
216
+ /** Mutate one exact config root. Setup uses this for profile and explicit
217
+ * project targets that cannot be represented by the user/project Scope enum. */
218
+ export declare function updateConfigAtRoot(root: string, mutate: (cfg: ScopeConfig) => void): ScopeConfig;
219
+ export declare function updateConfig(scope: Scope, mutate: (cfg: ScopeConfig) => void): ScopeConfig;
220
+ /** Persist one sparse kind-model field at an exact config root. The sparse
221
+ * patch keeps every unrelated kind and launch knob inherited from lower
222
+ * scopes instead of freezing a materialized registry snapshot. */
223
+ export declare function persistDefaultKindModel(opts: {
224
+ root: string;
225
+ kind: string;
226
+ model: string;
227
+ orchestrator?: boolean;
228
+ }): {
229
+ path: string;
230
+ field: 'model' | 'orchestratorModel';
231
+ };
232
+ export declare function updateState(scope: Scope, mutate: (s: ScopeState) => void): ScopeState;
233
+ export {};