dsh-model-switch 0.0.0-stage → 0.4.17

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 (63) hide show
  1. package/CHANGELOG.md +173 -0
  2. package/IMPLEMENTATION_PLAN.md +40 -0
  3. package/LICENSE +21 -0
  4. package/PRODUCT.md +33 -0
  5. package/README.md +196 -3
  6. package/README.zh.md +194 -0
  7. package/cordis.patch.yml +18 -0
  8. package/docs/screenshots/composer-picker.png +0 -0
  9. package/docs/screenshots/plan-review.png +0 -0
  10. package/docs/screenshots/settings-subagent.png +0 -0
  11. package/lib/adapter-registry.js +59 -0
  12. package/lib/capabilities.js +73 -0
  13. package/lib/client.js +3956 -0
  14. package/lib/index.js +1030 -0
  15. package/lib/main-settings.js +30 -0
  16. package/lib/subagent-route-policy.js +55 -0
  17. package/lib/subagent-runtime.js +186 -0
  18. package/lib/types/adapter-registry.d.ts +60 -0
  19. package/lib/types/capabilities-rpc.d.ts +12 -0
  20. package/lib/types/capabilities.d.ts +22 -0
  21. package/lib/types/client/ModelSwitchSettings.d.ts +32 -0
  22. package/lib/types/client/antigravity-catalog.d.ts +30 -0
  23. package/lib/types/client/derived-config-form.d.ts +10 -0
  24. package/lib/types/client/derived-settings-scope.d.ts +4 -0
  25. package/lib/types/client/index.d.ts +10 -0
  26. package/lib/types/client/locales.d.ts +45 -0
  27. package/lib/types/client/main-row-controller.d.ts +49 -0
  28. package/lib/types/client/nav-icon.d.ts +3 -0
  29. package/lib/types/client/picker/ComposerPicker.d.ts +47 -0
  30. package/lib/types/client/picker/PickerDirectory.d.ts +31 -0
  31. package/lib/types/client/picker/PickerSeatBoundary.d.ts +7 -0
  32. package/lib/types/client/picker/PlanReviewCard.d.ts +37 -0
  33. package/lib/types/client/picker/RetryBoundary.d.ts +20 -0
  34. package/lib/types/client/picker/external-catalog.d.ts +14 -0
  35. package/lib/types/client/picker/install.d.ts +23 -0
  36. package/lib/types/client/picker/locales.d.ts +104 -0
  37. package/lib/types/client/picker/main-default-restore.d.ts +56 -0
  38. package/lib/types/client/picker/popup-dismissal.d.ts +22 -0
  39. package/lib/types/client/picker/useComposerPickerSurface.d.ts +25 -0
  40. package/lib/types/client/plugin-rpc.d.ts +10 -0
  41. package/lib/types/client/provider-directory.d.ts +23 -0
  42. package/lib/types/client/remote-settings-scope.d.ts +45 -0
  43. package/lib/types/client/runtime-lock.d.ts +68 -0
  44. package/lib/types/client/search-capabilities.d.ts +21 -0
  45. package/lib/types/client-contract.d.ts +51 -0
  46. package/lib/types/compatibility.d.ts +38 -0
  47. package/lib/types/deepseek-search-adapter.d.ts +14 -0
  48. package/lib/types/host-runtime.d.ts +78 -0
  49. package/lib/types/host-settings.d.ts +49 -0
  50. package/lib/types/image-tool.d.ts +17 -0
  51. package/lib/types/index.d.ts +7 -0
  52. package/lib/types/main-settings.d.ts +9 -0
  53. package/lib/types/picker/family.d.ts +108 -0
  54. package/lib/types/picker/pending-dismissal.d.ts +34 -0
  55. package/lib/types/picker/plan-review.d.ts +57 -0
  56. package/lib/types/picker/public.d.ts +5 -0
  57. package/lib/types/picker/selection-feedback.d.ts +2 -0
  58. package/lib/types/runtime-capabilities.d.ts +50 -0
  59. package/lib/types/search-provider.d.ts +23 -0
  60. package/lib/types/subagent-route-policy.d.ts +26 -0
  61. package/lib/types/subagent-runtime.d.ts +51 -0
  62. package/lib/types/switch-compact.d.ts +72 -0
  63. package/package.json +342 -6
@@ -0,0 +1,78 @@
1
+ import { Context, Service } from '@deepseek-ai/cordis';
2
+ import type { ModelSelection } from '@deepseek-ai/dsh-agent';
3
+ import type { AgentDefaultModelConfig } from '@deepseek-ai/dsh-agent-default-model';
4
+ import { type Config as ModelSwitchSettingsConfig, type ModelSwitchSettings } from './host-settings.js';
5
+ import { ModelSwitchAdapterRegistry } from './adapter-registry.js';
6
+ declare module '@deepseek-ai/cordis' {
7
+ interface Context {
8
+ modelSwitch: ModelSwitchRuntime;
9
+ }
10
+ }
11
+ /** Host owner for Model Switch settings and the released Main-default adapter. */
12
+ export declare class ModelSwitchRuntime extends Service {
13
+ static inject: string[];
14
+ static Config: import("@deepseek-ai/schemastery").default<Schemastery.ObjectS<NoInfer<{
15
+ subagentMode: import("@deepseek-ai/schemastery").default<"follow-main" | "fixed", "follow-main" | "fixed", "volatile-defined">;
16
+ subagentProvider: import("@deepseek-ai/schemastery").default<string, string, "volatile">;
17
+ subagentModel: import("@deepseek-ai/schemastery").default<string, string, "volatile">;
18
+ subagentReasoningEffort: import("@deepseek-ai/schemastery").default<string, string, "volatile">;
19
+ searchProvider: import("@deepseek-ai/schemastery").default<string, string, "volatile">;
20
+ searchModel: import("@deepseek-ai/schemastery").default<string, string, "volatile">;
21
+ imageProvider: import("@deepseek-ai/schemastery").default<string, string, "volatile">;
22
+ imageModel: import("@deepseek-ai/schemastery").default<string, string, "volatile">;
23
+ compactOnSwitch: import("@deepseek-ai/schemastery").default<boolean, boolean, "volatile-defined">;
24
+ }>>, Schemastery.ObjectT<NoInfer<{
25
+ subagentMode: import("@deepseek-ai/schemastery").default<"follow-main" | "fixed", "follow-main" | "fixed", "volatile-defined">;
26
+ subagentProvider: import("@deepseek-ai/schemastery").default<string, string, "volatile">;
27
+ subagentModel: import("@deepseek-ai/schemastery").default<string, string, "volatile">;
28
+ subagentReasoningEffort: import("@deepseek-ai/schemastery").default<string, string, "volatile">;
29
+ searchProvider: import("@deepseek-ai/schemastery").default<string, string, "volatile">;
30
+ searchModel: import("@deepseek-ai/schemastery").default<string, string, "volatile">;
31
+ imageProvider: import("@deepseek-ai/schemastery").default<string, string, "volatile">;
32
+ imageModel: import("@deepseek-ai/schemastery").default<string, string, "volatile">;
33
+ compactOnSwitch: import("@deepseek-ai/schemastery").default<boolean, boolean, "volatile-defined">;
34
+ }>>, "plain">;
35
+ get capabilities(): {
36
+ searchProviderAdapters: {
37
+ available: boolean;
38
+ providers: string[];
39
+ catalog: readonly import("./adapter-registry.js").SearchProviderMetadata[];
40
+ };
41
+ mainDefaults: Readonly<{
42
+ available: true;
43
+ }>;
44
+ settings: Readonly<{
45
+ available: true;
46
+ }>;
47
+ centralSubagentRouting: Readonly<{
48
+ available: true;
49
+ }>;
50
+ packagedPresetRoots: Readonly<{
51
+ available: false;
52
+ reason: "packaged-preset-roots";
53
+ }>;
54
+ toolOwnerSuppression: Readonly<{
55
+ available: false;
56
+ reason: "tool-owner-suppression";
57
+ }>;
58
+ visionProviderAdapters: Readonly<{
59
+ available: false;
60
+ reason: "vision-provider-adapters";
61
+ }>;
62
+ imageProviderAdapters: Readonly<{
63
+ available: true;
64
+ providers: readonly string[];
65
+ }>;
66
+ };
67
+ readonly adapters: ModelSwitchAdapterRegistry;
68
+ private source;
69
+ constructor(ctx: Context, entry: ModelSwitchSettingsConfig);
70
+ currentSettings(): ModelSwitchSettings;
71
+ currentMainSelection(): ModelSelection;
72
+ saveMainSelection(selection: ModelSelection): Promise<void>;
73
+ }
74
+ export interface MainDefaultPort {
75
+ currentSelection(): ModelSelection;
76
+ saveSelection(selection: ModelSelection): Promise<void>;
77
+ }
78
+ export declare function mainDefaultPort(service: AgentDefaultModelConfig): MainDefaultPort;
@@ -0,0 +1,49 @@
1
+ import type { Volatile } from '@deepseek-ai/cordis';
2
+ import z from '@deepseek-ai/schemastery';
3
+ export type SubagentMode = 'follow-main' | 'fixed';
4
+ /** Resolved live values consumed by Model Switch. */
5
+ export interface ModelSwitchSettings {
6
+ subagentMode: SubagentMode;
7
+ subagentProvider?: string;
8
+ subagentModel?: string;
9
+ subagentReasoningEffort?: string;
10
+ searchProvider?: string;
11
+ searchModel?: string;
12
+ imageProvider?: string;
13
+ imageModel?: string;
14
+ compactOnSwitch: boolean;
15
+ }
16
+ /** Loader-owned references; every field is directly editable in the profile form. */
17
+ export interface Config {
18
+ subagentMode: Volatile<SubagentMode>;
19
+ subagentProvider: Volatile<string | undefined>;
20
+ subagentModel: Volatile<string | undefined>;
21
+ subagentReasoningEffort: Volatile<string | undefined>;
22
+ searchProvider: Volatile<string | undefined>;
23
+ searchModel: Volatile<string | undefined>;
24
+ imageProvider: Volatile<string | undefined>;
25
+ imageModel: Volatile<string | undefined>;
26
+ compactOnSwitch: Volatile<boolean>;
27
+ }
28
+ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
29
+ subagentMode: z<"follow-main" | "fixed", "follow-main" | "fixed", "volatile-defined">;
30
+ subagentProvider: z<string, string, "volatile">;
31
+ subagentModel: z<string, string, "volatile">;
32
+ subagentReasoningEffort: z<string, string, "volatile">;
33
+ searchProvider: z<string, string, "volatile">;
34
+ searchModel: z<string, string, "volatile">;
35
+ imageProvider: z<string, string, "volatile">;
36
+ imageModel: z<string, string, "volatile">;
37
+ compactOnSwitch: z<boolean, boolean, "volatile-defined">;
38
+ }>>, Schemastery.ObjectT<NoInfer<{
39
+ subagentMode: z<"follow-main" | "fixed", "follow-main" | "fixed", "volatile-defined">;
40
+ subagentProvider: z<string, string, "volatile">;
41
+ subagentModel: z<string, string, "volatile">;
42
+ subagentReasoningEffort: z<string, string, "volatile">;
43
+ searchProvider: z<string, string, "volatile">;
44
+ searchModel: z<string, string, "volatile">;
45
+ imageProvider: z<string, string, "volatile">;
46
+ imageModel: z<string, string, "volatile">;
47
+ compactOnSwitch: z<boolean, boolean, "volatile-defined">;
48
+ }>>, "plain">;
49
+ export declare function readConfig(config: Config): ModelSwitchSettings;
@@ -0,0 +1,17 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import type { ModelSwitchAdapterRegistry } from './adapter-registry.js';
3
+ export declare const GENERATE_IMAGE_TOOL_NAME = "generate_image";
4
+ interface ImageSettings {
5
+ readonly imageProvider?: string;
6
+ readonly imageModel?: string;
7
+ }
8
+ export interface ImageToolRuntime {
9
+ currentSettings(): ImageSettings;
10
+ readonly adapters: ModelSwitchAdapterRegistry;
11
+ }
12
+ export interface ImageToolController {
13
+ reconcile(): Promise<void>;
14
+ }
15
+ /** Registers one provider-specific schema and replaces it transactionally on Settings changes. */
16
+ export declare function installGenerateImageTool(ctx: Context, runtime: ImageToolRuntime): ImageToolController;
17
+ export {};
@@ -0,0 +1,7 @@
1
+ export { Config, readConfig, type ModelSwitchSettings } from './host-settings.js';
2
+ export { mainDefaultPort, ModelSwitchRuntime, type MainDefaultPort } from './host-runtime.js';
3
+ export { ModelSwitchAdapterRegistry, type ModelSwitchProviderAdapters, type ModelSwitchSearchAdapter, type ModelSwitchImageAdapter, type ModelSwitchImageRequest, type ModelSwitchGeneratedImage } from './adapter-registry.js';
4
+ export { RUNTIME_CAPABILITIES, type RuntimeCapabilities, type RuntimeCapability } from './runtime-capabilities.js';
5
+ export { parsePickerId, groupFamilies, planReviewOf, selectPlanReview, approvePlanReview, } from './picker/public.ts';
6
+ export declare const name = "dsh-model-switch";
7
+ export { ModelSwitchRuntime as default } from './host-runtime.js';
@@ -0,0 +1,9 @@
1
+ import { type CapabilityCatalog, type ModelSelection } from './capabilities.js';
2
+ export interface MainSettingsDocument {
3
+ version: 1;
4
+ defaultRoute: ModelSelection;
5
+ }
6
+ /** Decode storage without resolving availability, so invalid/uninstalled choices remain visible. */
7
+ export declare function parseMainSettingsDocument(input: unknown): MainSettingsDocument;
8
+ /** Validate only when a caller creates a new session; existing sessions never re-read this document. */
9
+ export declare function routeForNewSession(catalog: CapabilityCatalog, settings: MainSettingsDocument): ModelSelection;
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Suffix grammar for composer picker grouping. Provider plugins own the wire
3
+ * peel; this module only reads catalog ids.
4
+ */
5
+ /** Trailing Fast row. */
6
+ export declare const FAST_SUFFIX = "-fast";
7
+ /** Parsed picker id after stripping Fast and a numeric context tier. */
8
+ export interface ParsedPickerId {
9
+ /** Family key after peeling Fast and `-<n>k` / `-<n>m`. */
10
+ base: string;
11
+ /** Whether this row is the Fast sibling. */
12
+ fast: boolean;
13
+ /** Context suffix without the leading dash, or null for the standard row. */
14
+ contextTier: string | null;
15
+ /** Compaction budget implied by the suffix. */
16
+ contextTokens?: number;
17
+ }
18
+ /** One catalog model as the Host directory projects it. */
19
+ export interface CatalogModelView {
20
+ id: string;
21
+ name: string;
22
+ description?: string;
23
+ reasoning?: {
24
+ defaultEffort?: string;
25
+ efforts: readonly {
26
+ id: string;
27
+ name: string;
28
+ description?: string;
29
+ }[];
30
+ };
31
+ }
32
+ /** One provider group from the Host directory. */
33
+ export interface CatalogGroupView {
34
+ id: string;
35
+ name: string;
36
+ models: readonly CatalogModelView[];
37
+ }
38
+ /** One catalog row inside a suffix family. */
39
+ export interface FamilyMember {
40
+ model: CatalogModelView;
41
+ fast: boolean;
42
+ contextTier: string | null;
43
+ contextTokens?: number;
44
+ thinking: boolean;
45
+ }
46
+ /** Same-base rows across Fast / context / thinking siblings. */
47
+ export interface ModelFamily {
48
+ provider: string;
49
+ providerName: string;
50
+ base: string;
51
+ name: string;
52
+ members: FamilyMember[];
53
+ }
54
+ /** Peel Fast and `-<n>k` / `-<n>m` in either order. Product names like `-max` stay. */
55
+ export declare function parsePickerId(id: string): ParsedPickerId;
56
+ /** Catalog id for a standard-row window that the Host did not publish. UI localizes this. */
57
+ export declare const STANDARD_CONTEXT_LABEL = "standard";
58
+ /** Human label for a context tier: 1M, 272K, or STANDARD_CONTEXT_LABEL when its window is unknown. */
59
+ export declare function contextTierLabel(tier: string | null, tokens?: number): string;
60
+ /** Compact token window for trigger / context-cell copy. */
61
+ export declare function formatWindow(tokens: number): string;
62
+ /** Standard-row window when the Host directory omits contextWindow. */
63
+ export declare function impliedStandardTokens(base: string): number | undefined;
64
+ /** Label a selected variant from catalog identity, never from stale session pressure. */
65
+ export declare function contextLabelForMember(family: ModelFamily, member: FamilyMember): string;
66
+ /** Group directory rows by provider + peeled base. */
67
+ export declare function groupFamilies(groups: readonly CatalogGroupView[]): ModelFamily[];
68
+ /** Locate the family that owns a provider/model pair. */
69
+ export declare function findFamily(families: readonly ModelFamily[], provider: string, modelId: string): ModelFamily | undefined;
70
+ /** Locate one family member by catalog id. */
71
+ export declare function findMember(family: ModelFamily, modelId: string): FamilyMember | undefined;
72
+ export interface VariantPatch {
73
+ fast?: boolean;
74
+ contextTier?: string | null;
75
+ thinking?: boolean;
76
+ }
77
+ /** Pick a sibling after toggling Fast / context / thinking, keeping the other axes. */
78
+ export declare function pickVariant(family: ModelFamily, current: FamilyMember, patch: VariantPatch): FamilyMember;
79
+ /** Fast row appears only when both a Fast and a non-Fast sibling exist. */
80
+ export declare function familyHasFast(family: ModelFamily): boolean;
81
+ /** Provider sections in catalog order, for the model pane. */
82
+ export declare function sectionFamilies(families: readonly ModelFamily[]): Array<{
83
+ provider: string;
84
+ providerName: string;
85
+ families: ModelFamily[];
86
+ }>;
87
+ /** Unique context tiers in catalog order. */
88
+ export declare function contextTiers(family: ModelFamily, standardTokens?: number): Array<{
89
+ tier: string | null;
90
+ label: string;
91
+ tokens?: number;
92
+ }>;
93
+ /** Context row appears only when the family has more than one tier. */
94
+ export declare function familyHasContextChoices(family: ModelFamily): boolean;
95
+ /** Thinking on/off siblings at the current Fast + context axes, or null. */
96
+ export declare function thinkingSiblings(family: ModelFamily, current: FamilyMember): {
97
+ on: FamilyMember;
98
+ off: FamilyMember;
99
+ } | null;
100
+ /** Case-insensitive local search over family name, base, and provider. */
101
+ export declare function filterFamilies(families: readonly ModelFamily[], query: string): ModelFamily[];
102
+ export interface ModelSelectionView {
103
+ provider: string;
104
+ model: string;
105
+ reasoningEffort?: string;
106
+ }
107
+ /** Build a Host selection from a member, preserving or defaulting effort. */
108
+ export declare function selectionOf(family: ModelFamily, member: FamilyMember, reasoningEffort?: string): ModelSelectionView;
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Dismissal seam for one pending question card.
3
+ *
4
+ * The published `PendingQuestion` renamed its "user closed this card" verb:
5
+ * the 0.1.x generation rejected the Host waterfall through `cancel()`
6
+ * (documented as *Reject the Host waterfall because the user closed the
7
+ * question*), while the 0.2.x generation splits hiding from cancelling and
8
+ * exposes only `dismiss()`. Both mean the same product action here — the user
9
+ * takes the turn back to speak — because plan mode maps `ASK_CANCELLED` to
10
+ * "the user dismissed the plan review to speak instead; stay in plan mode,
11
+ * stop here, and wait for their message".
12
+ *
13
+ * Probe the method instead of the version: a Host may ship either generation,
14
+ * and neither seam means the takeover must fail loudly rather than answer.
15
+ */
16
+ export type PendingDismissal = 'dismiss' | 'cancel';
17
+ /** The card surface this module needs; every generation satisfies a subset. */
18
+ export interface PendingDismissible {
19
+ /** 0.2.x: withdraw the card (and end an unnamed request); request stays live for a named card. */
20
+ dismiss?: () => Promise<void>;
21
+ /** 0.1.x: reject the Host waterfall so the user can speak instead. */
22
+ cancel?: () => Promise<void>;
23
+ }
24
+ /** Raised when the running Host exposes neither withdrawal verb. */
25
+ export declare class PlanDismissalUnsupportedError extends Error {
26
+ constructor(message: string);
27
+ }
28
+ /**
29
+ * Withdraw a pending card so the human can talk instead, on either generation.
30
+ * @param wait - the matched pending question carrier.
31
+ * @returns Which verb withdrew the card.
32
+ * @throws {PlanDismissalUnsupportedError} when the carrier exposes neither verb.
33
+ */
34
+ export declare function dismissPendingQuestion(wait: PendingDismissible): Promise<PendingDismissal>;
@@ -0,0 +1,57 @@
1
+ import type { ModelSelection } from '@deepseek-ai/dsh-api-session-controller/types';
2
+ import type { ComposerChainProps } from '@deepseek-ai/dsh-client-ui-conversation/client';
3
+ import type { PendingQuestion, PlanReview } from '@deepseek-ai/dsh-client-ui-user-questions/client';
4
+ export type { PlanReview } from '@deepseek-ai/dsh-client-ui-user-questions/client';
5
+ export type PlanReviewOption = PlanReview['approve'];
6
+ type QuestionItem = PendingQuestion['questions'][number];
7
+ export declare function planReviewOf(questions: readonly QuestionItem[]): PlanReview | undefined;
8
+ export declare function selectPlanReview(owner: ComposerChainProps): PendingQuestion | null;
9
+ export declare class PlanApprovalResponseError extends Error {
10
+ constructor(message: string, options?: {
11
+ cause?: unknown;
12
+ });
13
+ }
14
+ /**
15
+ * Result of committing the draft execution model before answering a review.
16
+ *
17
+ * A commit and the compensation of the Host's own deployment-default write are
18
+ * separate facts: only a failed commit may hold the answer back, because the
19
+ * Host persists a Session selection as the deployment default on several
20
+ * releases and this plugin restores the captured Main default afterwards. A
21
+ * failed restore is announced, never fatal — the Session selection the human
22
+ * asked for is already installed.
23
+ */
24
+ export interface PlanCommitOutcome {
25
+ /** The Session now runs the requested provider/model/effort. */
26
+ committed: boolean;
27
+ /** The captured Main default is the effective one again. */
28
+ mainDefaultRestored: boolean;
29
+ }
30
+ /**
31
+ * Diagnostic text for one failed action, keeping the wire code visible.
32
+ * Official Remote failures carry a code that names the owning domain
33
+ * (`session/writer-held`, `session/model-unavailable`, …); dropping it leaves
34
+ * the human with copy they cannot act on.
35
+ * @param cause - the thrown value.
36
+ * @returns The message, prefixed with the nearest available code.
37
+ */
38
+ export declare function planErrorText(cause: unknown): string;
39
+ export declare function approvePlanReview(args: {
40
+ select: (selection: ModelSelection) => Promise<PlanCommitOutcome>;
41
+ selection: ModelSelection;
42
+ current?: ModelSelection | null;
43
+ answer: () => Promise<void>;
44
+ /** Announced after a successful answer when the Main default could not be restored. */
45
+ onNotRestored?: (notice: string) => void;
46
+ }): Promise<boolean>;
47
+ export interface PlanActionState {
48
+ busy: boolean;
49
+ blocked: boolean;
50
+ error: string | null;
51
+ }
52
+ export interface PlanActionView {
53
+ approveDisabled: boolean;
54
+ error: string | null;
55
+ }
56
+ export declare function planActionView(state: PlanActionState, available: boolean, hasExecution: boolean): PlanActionView;
57
+ export declare function settlePlanAction(send: () => Promise<void>, update: (state: PlanActionState) => void): Promise<boolean>;
@@ -0,0 +1,5 @@
1
+ /** Host-facing re-exports for tests and typed consumers. */
2
+ export { parsePickerId, groupFamilies, findFamily, pickVariant, familyHasFast, familyHasContextChoices, thinkingSiblings, filterFamilies, selectionOf, contextTierLabel, formatWindow, } from './family.ts';
3
+ export type { ParsedPickerId, CatalogGroupView, CatalogModelView, FamilyMember, ModelFamily, ModelSelectionView, } from './family.ts';
4
+ export { planReviewOf, selectPlanReview, approvePlanReview } from './plan-review.ts';
5
+ export type { PlanReview, PlanReviewOption } from './plan-review.ts';
@@ -0,0 +1,2 @@
1
+ /** Immediate picker feedback around an asynchronous Host model selection. */
2
+ export declare function beginSelection<T>(select: () => Promise<T>, showFeedback: () => void, settle: (outcome: T) => void): Promise<void>;
@@ -0,0 +1,50 @@
1
+ export type RuntimeCapabilityReason = 'central-subagent-routing' | 'packaged-preset-roots' | 'tool-owner-suppression' | 'search-provider-adapters' | 'vision-provider-adapters' | 'image-provider-adapters';
2
+ import type { SearchProviderMetadata } from './adapter-registry.js';
3
+ export interface RuntimeCapability {
4
+ readonly available: boolean;
5
+ readonly reason?: RuntimeCapabilityReason;
6
+ readonly providers?: readonly string[];
7
+ readonly catalog?: readonly SearchProviderMetadata[];
8
+ }
9
+ export interface RuntimeCapabilities {
10
+ readonly mainDefaults: RuntimeCapability;
11
+ readonly settings: RuntimeCapability;
12
+ readonly centralSubagentRouting: RuntimeCapability;
13
+ readonly packagedPresetRoots: RuntimeCapability;
14
+ readonly toolOwnerSuppression: RuntimeCapability;
15
+ readonly searchProviderAdapters: RuntimeCapability;
16
+ readonly visionProviderAdapters: RuntimeCapability;
17
+ readonly imageProviderAdapters: RuntimeCapability;
18
+ }
19
+ export declare const RUNTIME_CAPABILITIES: Readonly<{
20
+ mainDefaults: Readonly<{
21
+ available: true;
22
+ }>;
23
+ settings: Readonly<{
24
+ available: true;
25
+ }>;
26
+ centralSubagentRouting: Readonly<{
27
+ available: true;
28
+ }>;
29
+ packagedPresetRoots: Readonly<{
30
+ available: false;
31
+ reason: "packaged-preset-roots";
32
+ }>;
33
+ toolOwnerSuppression: Readonly<{
34
+ available: false;
35
+ reason: "tool-owner-suppression";
36
+ }>;
37
+ searchProviderAdapters: Readonly<{
38
+ available: false;
39
+ reason: "search-provider-adapters";
40
+ providers: readonly never[];
41
+ }>;
42
+ visionProviderAdapters: Readonly<{
43
+ available: false;
44
+ reason: "vision-provider-adapters";
45
+ }>;
46
+ imageProviderAdapters: Readonly<{
47
+ available: true;
48
+ providers: readonly string[];
49
+ }>;
50
+ }>;
@@ -0,0 +1,23 @@
1
+ import type { Context } from '@deepseek-ai/cordis';
2
+ import type { WebSearchProvider, WebSearchRequest, WebSearchResult } from '@deepseek-ai/dsh-web';
3
+ import type { ModelSwitchAdapterRegistry } from './adapter-registry.js';
4
+ export declare const MODEL_SWITCH_SEARCH_PROVIDER_ID = "model-switch";
5
+ interface SearchSettings {
6
+ readonly searchProvider?: string;
7
+ readonly searchModel?: string;
8
+ }
9
+ /** Thin official WebSearchProvider that resolves Model Switch routing at execution time. */
10
+ export declare class ModelSwitchSearchProvider implements WebSearchProvider {
11
+ private readonly settings;
12
+ private readonly adapters;
13
+ readonly id = "model-switch";
14
+ constructor(settings: () => SearchSettings, adapters: ModelSwitchAdapterRegistry);
15
+ available(): boolean;
16
+ search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult>;
17
+ }
18
+ export interface SearchProviderRuntime {
19
+ currentSettings(): SearchSettings;
20
+ readonly adapters: ModelSwitchAdapterRegistry;
21
+ }
22
+ export declare function installModelSwitchSearchProvider(ctx: Context, runtime: SearchProviderRuntime): void;
23
+ export {};
@@ -0,0 +1,26 @@
1
+ import { type CapabilityCatalog, type ModelSelection, type ReasoningEffortId } from './capabilities.js';
2
+ export type SubagentRoutePolicy = {
3
+ mode: 'follow-main';
4
+ } | {
5
+ mode: 'fixed';
6
+ route: ModelSelection;
7
+ };
8
+ export interface WorkflowRouteOverride {
9
+ provider: string;
10
+ model: string;
11
+ effort?: ReasoningEffortId;
12
+ }
13
+ export type SubagentRouteSnapshot = {
14
+ version: 1;
15
+ source: 'fixed-policy' | 'workflow-override';
16
+ selection: ModelSelection;
17
+ } | {
18
+ version: 1;
19
+ source: 'official-inherit';
20
+ };
21
+ export interface CreateSubagentRouteInput {
22
+ policy: SubagentRoutePolicy;
23
+ workflowOverride?: WorkflowRouteOverride;
24
+ }
25
+ export declare function createSubagentRouteSnapshot(catalog: CapabilityCatalog, input: CreateSubagentRouteInput): SubagentRouteSnapshot;
26
+ export declare function restoreSubagentRouteSnapshot(catalog: CapabilityCatalog, input: unknown): SubagentRouteSnapshot;
@@ -0,0 +1,51 @@
1
+ import { Context } from '@deepseek-ai/cordis';
2
+ import OfficialSubagentRuntime, { type ContinuableStart, type ContinuableStartSpec, type SubagentRun, type SubagentStartRequest } from '@deepseek-ai/dsh-subagent';
3
+ import type { ModelSwitchSettings } from './host-settings.js';
4
+ /** Raised only when an explicit startup check finds an unsupported public surface. */
5
+ export declare class StartupIncompatibilityError extends Error {
6
+ readonly name = "StartupIncompatibilityError";
7
+ readonly surface: string;
8
+ /**
9
+ * @param surfaceOrMessage - the incompatible public surface, or the error message.
10
+ * @param message - the missing or incompatible requirement.
11
+ */
12
+ constructor(surfaceOrMessage: string, message?: string);
13
+ }
14
+ /** One idempotent cleanup operation tracked during runtime startup. */
15
+ export type StartupDisposer = () => void | PromiseLike<void>;
16
+ /** Register one cleanup operation for a startup attempt. */
17
+ export type StartupCleanupTracker = (dispose: StartupDisposer) => void;
18
+ /** A successfully mounted runtime and its idempotent cleanup operation. */
19
+ export interface MountedStartup<T> {
20
+ readonly value: T;
21
+ readonly dispose: () => Promise<void>;
22
+ }
23
+ type RoutableSubagentRequest = Pick<SubagentStartRequest, 'parent' | 'agentOptions'>;
24
+ /** Inject a fixed Default Subagent route, or leave the request for Official inherit. */
25
+ export declare function routeSubagentRequest<T extends RoutableSubagentRequest>(request: T, settings: ModelSwitchSettings): T;
26
+ /**
27
+ * Mount a candidate and use the fallback only for typed startup incompatibility.
28
+ *
29
+ * Every resource registered by an attempt is disposed in reverse order before a
30
+ * fallback starts and again when the mounted result is disposed. Cleanup keeps
31
+ * running after failures, and a startup error stays first in any aggregate.
32
+ *
33
+ * @param candidate - startup callback for the routed candidate.
34
+ * @param fallback - startup callback for the untouched official runtime.
35
+ * @returns the selected value and an idempotent disposer.
36
+ * @throws the candidate error unless it is a StartupIncompatibilityError.
37
+ * @throws the fallback startup error when fallback startup fails.
38
+ */
39
+ export declare function mountWithStartupFallback<T>(candidate: (track: StartupCleanupTracker) => Promise<T>, fallback: (track: StartupCleanupTracker) => Promise<T>): Promise<MountedStartup<T>>;
40
+ /** Profile replacement that selects the routed runtime or the untouched official runtime. */
41
+ export declare const profileSubagentRuntime: ((ctx: Context) => Promise<() => Promise<void>>) & {
42
+ inject: string[];
43
+ };
44
+ /** Official runtime with only a pre-descriptor route-selection adapter. */
45
+ export declare class ModelSwitchSubagentRuntime extends OfficialSubagentRuntime {
46
+ static inject: string[];
47
+ private routed;
48
+ start(name: string, request: SubagentStartRequest): Promise<SubagentRun>;
49
+ startContinuable(spec: ContinuableStartSpec): Promise<ContinuableStart>;
50
+ }
51
+ export default profileSubagentRuntime;
@@ -0,0 +1,72 @@
1
+ /** Send-time context check and optional old-model compaction before a switched request. */
2
+ import type { Context } from '@deepseek-ai/cordis';
3
+ import { type UserMessage } from '@deepseek-ai/dsh-llm';
4
+ declare module '@deepseek-ai/dsh-llm' {
5
+ interface MessageSourceMap {
6
+ 'model-switch': {
7
+ kind: 'model-switch';
8
+ form: 'notice';
9
+ summary: string;
10
+ };
11
+ }
12
+ }
13
+ /** Provider and model selected for one model-routed request. */
14
+ export interface SwitchRoute {
15
+ readonly provider: string;
16
+ readonly model: string;
17
+ }
18
+ /**
19
+ * Compare two fully specified model routes.
20
+ * @param left - first route, if one was resolved.
21
+ * @param right - second route, if one was resolved.
22
+ * @returns Whether both routes identify the same provider and model.
23
+ */
24
+ export declare function sameSwitchRoute(left: SwitchRoute | undefined, right: SwitchRoute | undefined): boolean;
25
+ /**
26
+ * Decide whether a changed, complete route needs send-time context protection.
27
+ * @param enabled - whether the user enabled send-time protection.
28
+ * @param previous - route used by the preceding request.
29
+ * @param selected - route assembled for the pending request.
30
+ * @returns Whether the pending request changes its provider or model.
31
+ */
32
+ export declare function shouldProtectSwitch(enabled: boolean, previous: SwitchRoute | undefined, selected: SwitchRoute | undefined): boolean;
33
+ /**
34
+ * Input budget for the target window. Reserves output (maxTokens or 20%) plus a
35
+ * fixed estimator margin. `undefined` means the window cannot be used.
36
+ * @param contextWindow - adapter-owned combined request/response capacity.
37
+ * @param maxTokens - optional generation cap from the live agent options.
38
+ * @returns Tokens available to the request input, or undefined for an invalid window.
39
+ */
40
+ export declare function switchInputBudget(contextWindow: number, maxTokens?: number): number | undefined;
41
+ /**
42
+ * Determine whether an estimated input exceeds its target route budget.
43
+ * @param tokens - estimated input tokens.
44
+ * @param budget - input budget for the target window.
45
+ * @returns Whether the budget is unusable or the estimate is too large.
46
+ */
47
+ export declare function overSwitchBudget(tokens: number, budget: number): boolean;
48
+ /**
49
+ * Build one transcript-visible Model Switch progress notice.
50
+ * @param text - localized notice text shown in the transcript.
51
+ * @param summary - bounded event summary for durable session metadata.
52
+ * @returns A plugin-authored user message for the session log.
53
+ */
54
+ export declare function switchNotice(text: string, summary: string): UserMessage;
55
+ /**
56
+ * Extract a complete route from partial request configuration.
57
+ * @param value - candidate provider and model values.
58
+ * @returns A complete route, or undefined when either identifier is absent.
59
+ */
60
+ export declare function routeOf(value: {
61
+ provider?: string;
62
+ model?: string;
63
+ } | undefined): SwitchRoute | undefined;
64
+ /**
65
+ * Install send-time switch protection on the host context.
66
+ * @param ctx - host context that owns agent-loop, llm, and tokenMeter.
67
+ * @param settings - live Model Switch settings; `compactOnSwitch !== false` is on.
68
+ * @param roleOf - provider-role lookup used to exclude native Agent runtimes.
69
+ */
70
+ export declare function installSwitchCompaction(ctx: Context, settings: () => {
71
+ compactOnSwitch?: boolean;
72
+ }, roleOf?: (provider: string) => 'llm' | 'agent' | undefined): void;