dsh-llm-fallbacks 0.1.0-alpha.1

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.
@@ -0,0 +1,19 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
3
+ import type { SnapshotSelectorHook } from '@deepseek-ai/dsh-client-web-react';
4
+ import type { FallbacksSettingsController, FallbacksSettingsState } from './fallbacks-store.ts';
5
+ /** Injected dependencies of {@link GeneralFallbacksRow} (slot `inject`). */
6
+ export interface GeneralFallbacksRowInjected {
7
+ /** The shared controller (loaded on first mount, refreshed on pushed invalidations). */
8
+ controller: FallbacksSettingsController;
9
+ /** uSES subscription hook bound to the store (inject face — advisor pattern). */
10
+ useSnapshot: SnapshotSelectorHook<FallbacksSettingsState>;
11
+ }
12
+ /** Props delivered by the slot outlet: runtime share + locale seat + inject face. */
13
+ export type GeneralFallbacksRowProps = PropsRuntime<'settings.general.item'> & PropsLocale<'fallbacks'> & GeneralFallbacksRowInjected;
14
+ /**
15
+ * Render the Fallbacks status row.
16
+ * @param props - composed slot props.
17
+ * @returns the row element tree.
18
+ */
19
+ export declare function GeneralFallbacksRow({ controller, useSnapshot, t }: GeneralFallbacksRowProps): ReactNode;
@@ -0,0 +1,379 @@
1
+ /**
2
+ * Fallbacks settings controller — the client half's own store (slot owner
3
+ * props are empty; data rides this store, per the `settings.plugin.item`
4
+ * card contract).
5
+ *
6
+ * Read path: the fallbacks config rides the plugin's own gateway channel —
7
+ * `connection.rpc.call('/api', 'fallbacks/get', { args: {} })` — NOT the
8
+ * apiproxy wire: after the settings-exposure patches are gone the
9
+ * `fallbacks` namespace is absent from `settings.describe` on every host
10
+ * (like `advisor` is). `settings.describe({})` is still called, but only
11
+ * for the top-level `writable` flag (host read-only mode) and the namespace
12
+ * directory (the configured-provider join reads model-provider namespaces).
13
+ * A `get` that does not resolve (transport down / gateway not ready / no
14
+ * settings service on the host) is NOT a page error — `state.present` goes
15
+ * false and the section keeps the usable defaults skeleton (KD-G5).
16
+ *
17
+ * Write path: `save(next)` → `rpc.call('/api', 'fallbacks/set', { args: {
18
+ * patch: next } })` (the full edited config is the patch — a merge with all
19
+ * keys present is a full overwrite); `resetToDefaults()` →
20
+ * `rpc.call('/api', 'fallbacks/reset', { args: {} })` (the host clears the
21
+ * user layer via `settings.replace(ns, {})` — the removal path a merge
22
+ * cannot express). The gateway channel has NO revision guard: any
23
+ * `set`/`reset` failure (business rejection or transport) surfaces its
24
+ * message in `state.error` for the section's error banner (KD-G3 — the old
25
+ * `settings-conflict` branch is gone).
26
+ */
27
+ import type { ClientConnectionRpc, ConfigurableProviderView, HistoryEntry, IApiClient, ModelProviderGroup, SessionId, SettingsNamespaceView } from '@deepseek-ai/dsh-client-connection/client';
28
+ import { type SnapshotStore } from '@deepseek-ai/dsh-client-runtime/client';
29
+ import { type FallbackStrategy, type FallbacksConfig, type FallbacksRole, type FallbacksRoleRule, type FallbacksRoles } from '../config.ts';
30
+ import type { FallbacksSwitchEventData } from '../events.ts';
31
+ /** The plugin's settings namespace on the host wire (settings/document-updated ns filter). */
32
+ export declare const FALLBACKS_SETTINGS_NS = "fallbacks";
33
+ /** Single-page history read for the status block (spec §2.5 D-5: `HISTORY_PAGE_MESSAGES`-sized). */
34
+ export declare const SWITCHES_HISTORY_PAGE = 50;
35
+ /** How many recent switches the status block renders (spec §2.5 D-5: N=5). */
36
+ export declare const RECENT_SWITCH_LIMIT = 5;
37
+ /**
38
+ * One recent `fallbacks/switch` event as the status block renders it: the
39
+ * durable payload plus the raw event's ordering key and time (the payload
40
+ * itself carries no seq/time — spec §5 table).
41
+ */
42
+ export interface FallbacksSwitchSnapshot extends FallbacksSwitchEventData {
43
+ /** Event seq within the session (newest-first ordering key). */
44
+ seq: number;
45
+ /** Event time, Unix epoch milliseconds. */
46
+ time: number;
47
+ }
48
+ /**
49
+ * The status block's derived "current effective model" (spec §2.5 D-6) — a
50
+ * **display value** derived from configuration + recent switches, never a
51
+ * live route probe.
52
+ */
53
+ export type EffectiveModelView =
54
+ /** ① `enabled: false` or an empty rootChain. */
55
+ {
56
+ kind: 'unavailable';
57
+ }
58
+ /** ② The most recent switch's target (`to`). */
59
+ | {
60
+ kind: 'switched';
61
+ provider: string;
62
+ model: string;
63
+ }
64
+ /** ③ No switches yet: the config's primary target (first chain entry). */
65
+ | {
66
+ kind: 'config';
67
+ provider: string;
68
+ model: string;
69
+ };
70
+ /** Fallbacks settings-row snapshot. */
71
+ export interface FallbacksSettingsState {
72
+ status: 'idle' | 'loading' | 'ready' | 'saving' | 'error';
73
+ error: string | null;
74
+ /** Whether the provider allows writes at all (describe top-level flag). */
75
+ writable: boolean;
76
+ /** The resolved configuration (last accepted gateway response, or the defaults skeleton). */
77
+ config: FallbacksConfig;
78
+ /**
79
+ * Whether the `fallbacks/get` gateway channel resolved on the last load.
80
+ * `false` = channel unreachable (transport down / gateway not ready / no
81
+ * settings service) → the section keeps the usable skeleton (KD-G5).
82
+ */
83
+ present: boolean;
84
+ /**
85
+ * Legacy (two-block-era) config keys the gateway detected on the composed
86
+ * source (`get().legacyKeys` / the post-write `set`/`reset` response,
87
+ * spec §9): non-empty → the migration banner renders. The wire field is
88
+ * authoritative — the client never guesses legacy status on its own
89
+ * (detectLegacyClientKeys is a test-only fallback). A `set`/`reset`
90
+ * response WITHOUT the field (older gateway) keeps the last accepted
91
+ * value — only a `get` may settle legacy truth (W-1/F-1: a save merges
92
+ * over the user layer, so it cannot delete legacy keys).
93
+ */
94
+ legacyKeys: string[];
95
+ /** Provider/model directory snapshot (spec §2.5 D-4). */
96
+ catalogStatus: 'idle' | 'loading' | 'ready' | 'error';
97
+ /** Catalog read diagnostic: whole-load failure or per-provider lookups. */
98
+ catalogError: string | null;
99
+ /** Configurable-provider directory (`llm.providers`). */
100
+ providers: ConfigurableProviderView[];
101
+ /**
102
+ * The provider dropdown's offer set: catalog providers whose settings
103
+ * profile resolves, with the Models page's `configured` join semantics
104
+ * (spec §2.5 — see {@link configuredProvidersOf}). Unconfigured directory
105
+ * providers never appear as options.
106
+ */
107
+ configuredProviders: ConfigurableProviderView[];
108
+ /** Model catalog groups (`llm.models`). */
109
+ groups: ModelProviderGroup[];
110
+ /** Bumped on every accepted catalog read; drives row re-classification. */
111
+ catalogEpoch: number;
112
+ /** Recent-switch summary (spec §2.4 R-4a / §2.5 D-5). */
113
+ switchesStatus: 'idle' | 'loading' | 'ready' | 'error';
114
+ /** Switch-read diagnostic (wire message); null when none. */
115
+ switchesError: string | null;
116
+ /** Most recent `fallbacks/switch` events of the current session, newest first. */
117
+ switches: FallbacksSwitchSnapshot[];
118
+ }
119
+ /**
120
+ * The provider dropdown's offer set (spec §2.5 D-4): catalog providers whose
121
+ * settings profile resolves in the describe namespaces — the Models page's
122
+ * `configured` predicate (`ui-models` store.ts): a provider is configured
123
+ * when its settings namespace exists AND either it addresses the whole
124
+ * section (`settingsPath` empty) or its profile path resolves in the resolved
125
+ * value. Directory-only (unconfigured) providers never become options; the
126
+ * section still renders existing values for them (read-back + annotation) so
127
+ * nothing is lost on save.
128
+ */
129
+ export declare function configuredProvidersOf(providers: readonly ConfigurableProviderView[], namespaces: ReadonlyMap<string, SettingsNamespaceView>): ConfigurableProviderView[];
130
+ /**
131
+ * Fold the redacted descriptor value into a complete {@link FallbacksConfig}:
132
+ * missing optional fields take spec §4 defaults; gross type mismatches throw
133
+ * so the UI can surface a broken descriptor instead of mis-rendering.
134
+ */
135
+ export declare function parseFallbacksConfig(value: unknown): FallbacksConfig;
136
+ /**
137
+ * Row-level selection state of one provider/model cell (spec §2.5 D-3):
138
+ * a catalog id, an out-of-catalog raw value read back from the server, or
139
+ * nothing (empty / "any"). Serialization always writes the raw string, so an
140
+ * outside value is preserved verbatim — round-trip lossless.
141
+ */
142
+ export type CatalogSelection = {
143
+ kind: 'catalog';
144
+ id: string;
145
+ } | {
146
+ kind: 'outside';
147
+ raw: string;
148
+ } | null;
149
+ /** The catalog faces row conversions classify raw values against (D-4). */
150
+ export interface CatalogLookup {
151
+ providers: readonly ConfigurableProviderView[];
152
+ groups: readonly ModelProviderGroup[];
153
+ }
154
+ /** The raw selector string a selection serializes to ('' when empty). */
155
+ export declare function selectionToRaw(selection: CatalogSelection): string;
156
+ /**
157
+ * Classify a raw provider value against the catalog: a catalog route id is a
158
+ * catalog selection, anything else is an outside value kept verbatim.
159
+ */
160
+ export declare function classifyProvider(raw: string, catalog: CatalogLookup | undefined): CatalogSelection;
161
+ /**
162
+ * Classify a raw model value under its provider against the catalog: a model
163
+ * id advertised by that provider is a catalog selection, anything else is an
164
+ * outside value kept verbatim.
165
+ */
166
+ export declare function classifyModel(provider: string, raw: string, catalog: CatalogLookup | undefined): CatalogSelection;
167
+ /**
168
+ * Extract the most recent `fallbacks/switch` events from one history page
169
+ * (spec §2.5 D-5): filter by event type, order by `seq` descending, take at
170
+ * most `limit`. Single-page read — fewer than `limit` events show as-is; no
171
+ * multi-page backfill (Non-Goal).
172
+ */
173
+ export declare function extractRecentSwitches(entries: readonly HistoryEntry[], limit?: number): FallbacksSwitchSnapshot[];
174
+ /**
175
+ * Derive the status block's "current effective model" (spec §2.5 D-6): ①
176
+ * disabled / empty rootChain → unavailable; ② a recent switch exists → the
177
+ * latest one's `to`; ③ otherwise → the config's primary target. A **display
178
+ * value** — never a live route probe (the section appends the non-probing
179
+ * note inline right after the derived value, available case only; the
180
+ * unavailable 空态 renders its own copy without the note).
181
+ */
182
+ export declare function deriveEffectiveModel(config: FallbacksConfig, switches: readonly FallbacksSwitchSnapshot[]): EffectiveModelView;
183
+ /** One chain selector row: provider + model (or wildcard). */
184
+ export interface ChainSelectorRow {
185
+ /** `provider/*` wildcard entry: the model part is absent. */
186
+ wildcard: boolean;
187
+ provider: CatalogSelection;
188
+ /** Null when wildcard (or the entry carries no model part). */
189
+ model: CatalogSelection;
190
+ }
191
+ /** Serialize one selector row to its wire string (`provider/model` | `provider/*`). */
192
+ export declare function selectorRowToRaw(row: ChainSelectorRow): string;
193
+ /**
194
+ * One rootChain row in the editor: the root agent's single fallback chain
195
+ * (block 1 of the two-block model) as an ordered selector list. There is no
196
+ * key input — the row IS the chain.
197
+ */
198
+ export interface RootChainRow {
199
+ selectors: ChainSelectorRow[];
200
+ }
201
+ /** Project the rootChain entries into editable rows (one flat chain row). */
202
+ export declare function rootChainToRows(rootChain: readonly string[], catalog?: CatalogLookup): RootChainRow[];
203
+ /** Rebuild the rootChain from edited rows; rows with no usable selector drop out. */
204
+ export declare function rowsToRootChain(rows: readonly RootChainRow[]): string[];
205
+ /**
206
+ * One declared-role row in the editor (block 2 `roles.list`): identity
207
+ * fields + the role's own chain selector list + its append strategy.
208
+ * `prompt`/`permissions` are schema-reserved for the next iteration
209
+ * (fallbacks-explicit-role-tool) — they never enter row editing this round.
210
+ */
211
+ export interface RoleRow {
212
+ id: string;
213
+ label: string;
214
+ description: string;
215
+ selectors: ChainSelectorRow[];
216
+ fallback: FallbackStrategy;
217
+ }
218
+ /** Project the declared roles into editable rows (chain selectors classified). */
219
+ export declare function rolesToRows(roles: readonly FallbacksRole[], catalog?: CatalogLookup): RoleRow[];
220
+ /** Rebuild the declared roles from edited rows; empty selectors drop out. */
221
+ export declare function rowsToRoles(rows: readonly RoleRow[]): FallbacksRole[];
222
+ /**
223
+ * Rebuild the declared roles from edited rows, re-attaching the
224
+ * schema-reserved `prompt`/`permissions` fields from the last accepted
225
+ * config by role id — they never round-trip through rows this round, so
226
+ * without the merge a save would silently drop them (T2 reviewer minor
227
+ * #2). The id trim matches {@link rowsToRoles}; a row whose id matches no
228
+ * original role (a freshly added one) keeps no extras. Key order mirrors
229
+ * `parseFallbacksConfig` so a clean draft's JSON dirty comparison never
230
+ * flags it.
231
+ */
232
+ export declare function mergeRoleExtras(rows: readonly RoleRow[], originalRoles: readonly FallbacksRole[]): FallbacksRole[];
233
+ /**
234
+ * The `roles.rules` role dropdown's offer set — the ONLY data source for the
235
+ * rule rows' role selector: the built-in `'inherit'` target plus every
236
+ * declared `roles.list` id, in declaration order (a role added/removed on
237
+ * the same page is reflected immediately).
238
+ */
239
+ export declare function ruleRoleOptions(roles: Pick<FallbacksRoles, 'list'>): string[];
240
+ /**
241
+ * Client-side legacy fallback detection (spec §9). The gateway's
242
+ * `legacyKeys` wire field is authoritative for the migration banner; this is
243
+ * a defensive fallback for configs that already passed wire normalization
244
+ * yet still carry a `roles.rules` reference to an undeclared role id — the
245
+ * only two-block-era leftover that can survive the wire (the removed
246
+ * `chains`/`roles.default` keys never ride it).
247
+ */
248
+ export declare function detectLegacyClientKeys(config: FallbacksConfig): string[];
249
+ /** One role-rule row in the editor; empty origin means "any". */
250
+ export interface RoleRuleRow {
251
+ origin: string;
252
+ provider: CatalogSelection;
253
+ model: CatalogSelection;
254
+ role: string;
255
+ }
256
+ /** Project the role rules into editable rows (provider/model classified). */
257
+ export declare function rulesToRows(rules: readonly FallbacksRoleRule[], catalog?: CatalogLookup): RoleRuleRow[];
258
+ /** Rebuild the role rules from edited rows; empty origin/provider/model drop out. */
259
+ export declare function rowsToRules(rows: readonly RoleRuleRow[]): FallbacksRoleRule[];
260
+ /** Controller joining Settings reads, writes, and pushed invalidations. */
261
+ export declare class FallbacksSettingsController {
262
+ private readonly api;
263
+ private readonly rpc;
264
+ /** Snapshot consumed by the section through `useSyncExternalStore`. */
265
+ readonly store: SnapshotStore<FallbacksSettingsState>;
266
+ private generation;
267
+ private catalogGeneration;
268
+ private switchesGeneration;
269
+ /** Every settings namespace from the last describe, keyed by ns — the configured-provider join's other input. */
270
+ private namespaces;
271
+ private currentSession;
272
+ /**
273
+ * @param api - Settings / Llm / Sessions wire faces (describe `writable` +
274
+ * namespace directory, provider/model catalog, session history).
275
+ * @param rpc - the connection's generic RPC caller for the host gateway
276
+ * channel (`/api`), injected from the connection handle.
277
+ */
278
+ constructor(api: Pick<IApiClient, 'settings' | 'llm' | 'sessions'>, rpc: ClientConnectionRpc);
279
+ /**
280
+ * Refresh the page snapshot. Latest request wins. `settings.describe`
281
+ * still runs — it supplies the top-level `writable` flag (host read-only
282
+ * mode) and the namespace directory (the configured-provider join's other
283
+ * input) — but the fallbacks config itself rides the gateway channel:
284
+ * `rpc.call('/api', 'fallbacks/get', { args: {} })`. The two reads are
285
+ * independent and run in PARALLEL (Promise.all — one round trip per
286
+ * refresh, not two). The `fallbacks` namespace is NOT expected in describe
287
+ * anymore (it is off the apiproxy boundary post-patch); a describe failure
288
+ * remains a hard `error` (the form cannot render provider/model options
289
+ * without the directory), while a get failure is NOT a page error —
290
+ * `present` goes false and the section keeps the usable skeleton (KD-G5).
291
+ * @returns nothing; {@link store} carries success or failure.
292
+ */
293
+ load(): Promise<void>;
294
+ /**
295
+ * Refresh the provider/model catalog (`llm.providers` + `llm.models`), an
296
+ * independent read path with its own generation guard so it can run
297
+ * parallel to {@link load} without clobbering it (spec §2.5 D-4).
298
+ * Per-provider lookup failures ride `catalogError` as a diagnostic without
299
+ * failing the sound groups; a whole-load failure lands `catalogStatus:
300
+ * 'error'` and never blocks the rest of the form.
301
+ * @returns nothing; {@link store} carries success or failure.
302
+ */
303
+ loadCatalog(): Promise<void>;
304
+ /**
305
+ * Record the current session the status block reads (spec §2.5 D-5). Once
306
+ * the block has been read once, its summary follows session switches
307
+ * immediately; an idle block only records the id — the section's mount
308
+ * effect performs the first read.
309
+ * @param sessionId - the session whose history is summarized; undefined
310
+ * (no current session) resolves to the empty state.
311
+ */
312
+ setCurrentSession(sessionId: SessionId | undefined): void;
313
+ /**
314
+ * Read the recent-switch summary for the current session (spec §2.5 D-5):
315
+ * one `sessions.history` page (`maxMessages` = {@link SWITCHES_HISTORY_PAGE}),
316
+ * `fallbacks/switch` events extracted newest-first capped at
317
+ * {@link RECENT_SWITCH_LIMIT}. No current session → honest empty ready
318
+ * state (no RPC); a read failure lands `switchesStatus: 'error'` and never
319
+ * touches the settings state (the form keeps editing/saving normally).
320
+ * @returns nothing; {@link store} carries success or failure.
321
+ */
322
+ loadSwitches(): Promise<void>;
323
+ /**
324
+ * Persist the full edited configuration through the gateway channel
325
+ * (`/api/fallbacks/set`). The full config is sent as a MERGE patch (guide
326
+ * §9) — keys the new schema cannot express (legacy `chains` /
327
+ * `roles.default` in the user layer) survive the write, which is why the
328
+ * gateway returns POST-WRITE `legacyKeys` and the banner stays honest
329
+ * (W-1/F-1). The merge has no revision guard: any failure (business
330
+ * rejection or transport) surfaces its message in `state.error` for the
331
+ * section's error banner and the form stays editable for retry (KD-G3).
332
+ * @param next - the complete edited configuration.
333
+ */
334
+ save(next: FallbacksConfig): Promise<void>;
335
+ /**
336
+ * Reset to composition defaults through the gateway channel
337
+ * (`/api/fallbacks/reset` — the fallbacks-specific third method; the host
338
+ * clears the user layer via `settings.replace(ns, {})`, the removal path a
339
+ * merge cannot express). Same error handling as {@link save} (KD-G3).
340
+ */
341
+ resetToDefaults(): Promise<void>;
342
+ /** Stop in-flight responses from publishing after plugin disposal. */
343
+ dispose(): void;
344
+ /**
345
+ * Publish a settled load: `status` ready, `writable` from describe, and —
346
+ * only when the gateway returned a REAL config — `present` true and
347
+ * `state.config` replaced with the parsed value. A get that did not
348
+ * resolve (`config === undefined`) lands `present` false and keeps the
349
+ * last accepted config (the defaults skeleton on a first load) — the
350
+ * draft seed invariant (I-1): a transient channel-down must never seed
351
+ * the form with defaults over real server truth. `legacyKeys` rides the
352
+ * same publish: the wire field drives the migration banner. save/reset
353
+ * pass the POST-WRITE value (W-1/F-1) — or the previous value when the
354
+ * response omits the field, so a write can never clear the banner
355
+ * against server truth; only a real `get` may.
356
+ */
357
+ private accept;
358
+ private fail;
359
+ }
360
+ /**
361
+ * Refetch after reconnect / settings change only when the section has already
362
+ * opened once.
363
+ * @param controller - the fallbacks settings controller.
364
+ */
365
+ export declare function refreshFallbacksIfLoaded(controller: FallbacksSettingsController): void;
366
+ /**
367
+ * Refetch the catalog after `llm/adapters-updated` only when it has already
368
+ * been opened once (the catalog twin of {@link refreshFallbacksIfLoaded}).
369
+ * @param controller - the fallbacks settings controller.
370
+ */
371
+ export declare function refreshCatalogIfLoaded(controller: FallbacksSettingsController): void;
372
+ /**
373
+ * Refetch the recent-switch summary after `settings/document-updated`
374
+ * (fallbacks ns) / `connection/reset` only when the status block has already
375
+ * been read once
376
+ * (the switches twin of {@link refreshFallbacksIfLoaded}).
377
+ * @param controller - the fallbacks settings controller.
378
+ */
379
+ export declare function refreshSwitchesIfLoaded(controller: FallbacksSettingsController): void;
@@ -0,0 +1,56 @@
1
+ /**
2
+ * dsh-llm-fallbacks client half: registers the Fallbacks card into the
3
+ * plugin-config page's `settings.plugin.item` slot (the official "插件配置"
4
+ * settings page — id `fallbacks`, order 30, alongside the upstream bash /
5
+ * agent-loop / web-search cards and the advisor card).
6
+ *
7
+ * Wiring (mirrors dsh-advisor):
8
+ * - Registers the `fallbacks` locale dictionaries (zh/en).
9
+ * - Constructs the card's own store over the connection: the fallbacks
10
+ * config rides the plugin's gateway channel (`connection.rpc` →
11
+ * `/api/fallbacks/get|set|reset`), while `settings.describe` (writable +
12
+ * namespace directory) and the provider/model catalog stay on
13
+ * `connection.api` (see `fallbacks-store.ts`).
14
+ * - Registers the `settings.plugin.item` card `id: 'fallbacks'` (order 30)
15
+ * with a business-only inject face ({@link FallbacksSettingsController} +
16
+ * the snapshot-selector hook); the old Settings-nav section registration
17
+ * is removed — deleting the section registration deletes the nav entry.
18
+ * - Refreshes the store on pushed invalidations — the forwarded remote
19
+ * events `settings/document-updated` (ns-filtered to the fallbacks
20
+ * namespace; refetches the descriptor + recent-switch summary) and
21
+ * `llm/adapters-updated` (refetches only the provider/model catalog), plus
22
+ * the client `connection/reset` (refetches all three) — and follows the
23
+ * current session (`sessions.list`) so the status block's recent-switch
24
+ * summary tracks the session being viewed (spec §2.5 D-5). `sessions` is
25
+ * an optional reflection read (S-g): a host without the session service
26
+ * leaves the switches face in its empty ready state.
27
+ *
28
+ * @module dsh-llm-fallbacks/client
29
+ */
30
+ import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
31
+ export type { FallbacksCardInjected, FallbacksCardProps } from './FallbacksCard.tsx';
32
+ export type { GeneralFallbacksRowInjected, GeneralFallbacksRowProps } from './GeneralFallbacksRow.tsx';
33
+ export type { ConversationFallbackSwitchProps, FallbacksSwitchChatData, } from './ConversationFallbackSwitch.tsx';
34
+ export type { FallbacksSettingsState } from './fallbacks-store.ts';
35
+ export { FallbacksSettingsController, FALLBACKS_SETTINGS_NS } from './fallbacks-store.ts';
36
+ /**
37
+ * Required services (cordis fiber inject); registrations wait on the slot
38
+ * declaration. `conversationEvents` is declared because the D1 Definition
39
+ * registration reads the service directly (`ctx.conversationEvents.register`
40
+ * at the bottom of `apply` — explicit fiber-ordering parity with the
41
+ * ui-workflow-run precedent, whose inject list includes it for the same
42
+ * direct read). The runtime would still provide the service synchronously
43
+ * on apply, but the declaration makes the dependency honest. `sessions` is
44
+ * deliberately NOT injected (S-g): a non-web host without the dsh-session
45
+ * client service must not hang the fiber waiting for it — the wiring reads
46
+ * it reflectively and degrades to the switches empty state when absent
47
+ * (`setCurrentSession` never called, `loadSwitches` ready with an empty
48
+ * array, which the store already supports).
49
+ */
50
+ export declare const inject: string[];
51
+ /**
52
+ * Register the `fallbacks` dictionaries and the plugin-config card once the
53
+ * `settings.plugin.item` declaration is on the ledger.
54
+ * @param ctx - client root context.
55
+ */
56
+ export declare function apply(ctx: ClientContext): void;