@skaile/forge-settings 1.4.0 → 2.0.0

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 (75) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +34 -11
  3. package/dist/module.json +1 -1
  4. package/dist/runtime/ai-sdk/index.d.ts +33 -26
  5. package/dist/runtime/ai-sdk/index.js +34 -16
  6. package/dist/runtime/components/ForgeProvidersPanel.d.vue.ts +2 -2
  7. package/dist/runtime/components/ForgeProvidersPanel.vue.d.ts +2 -2
  8. package/dist/runtime/editor/index.d.ts +55 -16
  9. package/dist/runtime/editor/index.js +82 -40
  10. package/dist/runtime/model/selector.d.ts +16 -0
  11. package/dist/runtime/model/selector.js +12 -0
  12. package/dist/runtime/server/credential-store.d.ts +16 -4
  13. package/dist/runtime/server/credential-store.js +3 -4
  14. package/dist/runtime/server/env-layers.d.ts +1 -1
  15. package/dist/runtime/server/env-layers.js +3 -3
  16. package/dist/runtime/server/index.d.ts +7 -8
  17. package/dist/runtime/server/index.js +1 -3
  18. package/dist/runtime/server/provider-discovery.d.ts +28 -8
  19. package/dist/runtime/server/provider-discovery.js +57 -33
  20. package/dist/runtime/server/provider-layers.d.ts +1 -1
  21. package/dist/runtime/server/provider-layers.js +3 -5
  22. package/dist/runtime/server/provider-service.d.ts +2 -2
  23. package/dist/runtime/server/provider-write.d.ts +1 -1
  24. package/dist/runtime/server/provider-write.js +3 -3
  25. package/dist/runtime/server/voice-provider.d.ts +61 -0
  26. package/dist/runtime/server/voice-provider.js +31 -0
  27. package/dist/runtime/shared/data-zone.d.ts +28 -26
  28. package/dist/runtime/shared/data-zone.js +4 -4
  29. package/dist/runtime/shared/harness-providers.d.ts +7 -7
  30. package/dist/runtime/shared/provider-accounts.d.ts +1 -1
  31. package/dist/runtime/shared/provider-registry.d.ts +19 -220
  32. package/dist/runtime/shared/provider-registry.js +25 -81
  33. package/dist/runtime/vue/providers-panel-model.d.ts +4 -4
  34. package/dist/runtime/{server → workspaces}/harness-settings.d.ts +3 -3
  35. package/dist/runtime/workspaces/index.d.ts +16 -0
  36. package/dist/runtime/workspaces/index.js +5 -0
  37. package/dist/runtime/workspaces/language-model.d.ts +45 -0
  38. package/dist/runtime/workspaces/language-model.js +26 -0
  39. package/dist/runtime/workspaces/model-endpoint.d.ts +38 -0
  40. package/dist/runtime/workspaces/model-endpoint.js +54 -0
  41. package/dist/runtime/workspaces/provider-settings.d.ts +35 -0
  42. package/dist/runtime/{server → workspaces}/provider-settings.js +0 -31
  43. package/dist/runtime/{server → workspaces}/settings-defaults.d.ts +1 -1
  44. package/dist/runtime/{shared → workspaces}/settings-types.d.ts +4 -4
  45. package/package.json +11 -2
  46. package/src/runtime/ai-sdk/index.ts +88 -47
  47. package/src/runtime/components/ForgeProvidersPanel.vue +2 -2
  48. package/src/runtime/editor/index.ts +158 -63
  49. package/src/runtime/model/selector.ts +31 -0
  50. package/src/runtime/server/credential-store.ts +20 -10
  51. package/src/runtime/server/env-layers.ts +4 -4
  52. package/src/runtime/server/index.ts +7 -8
  53. package/src/runtime/server/provider-discovery.ts +114 -48
  54. package/src/runtime/server/provider-layers.ts +6 -6
  55. package/src/runtime/server/provider-service.ts +2 -2
  56. package/src/runtime/server/provider-write.ts +3 -3
  57. package/src/runtime/server/voice-provider.ts +110 -0
  58. package/src/runtime/shared/data-zone.ts +40 -33
  59. package/src/runtime/shared/harness-providers.ts +7 -7
  60. package/src/runtime/shared/provider-accounts.ts +1 -1
  61. package/src/runtime/shared/provider-registry.ts +82 -314
  62. package/src/runtime/vue/providers-panel-model.ts +5 -5
  63. package/src/runtime/vue/useProvidersPanel.ts +5 -2
  64. package/src/runtime/{server → workspaces}/harness-settings.ts +3 -3
  65. package/src/runtime/workspaces/index.ts +17 -0
  66. package/src/runtime/workspaces/language-model.ts +82 -0
  67. package/src/runtime/workspaces/model-endpoint.ts +100 -0
  68. package/src/runtime/workspaces/provider-settings.ts +96 -0
  69. package/src/runtime/{server → workspaces}/settings-defaults.ts +1 -1
  70. package/src/runtime/{shared → workspaces}/settings-types.ts +4 -4
  71. package/dist/runtime/server/provider-settings.d.ts +0 -78
  72. package/src/runtime/server/provider-settings.ts +0 -192
  73. /package/dist/runtime/{server → workspaces}/harness-settings.js +0 -0
  74. /package/dist/runtime/{server → workspaces}/settings-defaults.js +0 -0
  75. /package/dist/runtime/{shared → workspaces}/settings-types.js +0 -0
@@ -13,8 +13,8 @@
13
13
  *
14
14
  * Browser-safe on purpose — no `node:` imports.
15
15
  */
16
- import type { DataZone, DataZoneRule } from "./provider-registry.js";
17
- /** How many registry entries a harness can run at once (`@skaile/workspaces` `HarnessInfo.providerPlurality`). */
16
+ import type { AIProviderDataZone, AIProviderDataZoneRule } from "./provider-registry.js";
17
+ /** How many registry entries a harness can run at once (`HarnessInfo.providerPlurality` of a Workspaces harness). */
18
18
  export type HarnessProviderPlurality = "registry" | "credentials-n" | "single";
19
19
  /** The slice of a harness the rule reads. `GET /api/agents` rows satisfy it. */
20
20
  export interface HarnessProviderInfo {
@@ -36,7 +36,7 @@ export interface HarnessProviderCandidate {
36
36
  /** Catalog preset; gives the zone when {@link dataZone} is absent. */
37
37
  preset?: string;
38
38
  /** Effective data zone (`GET /api/providers` rows carry it). Absent: the preset's, else `transfer`. */
39
- dataZone?: DataZone;
39
+ dataZone?: AIProviderDataZone;
40
40
  /** The account's EU-processing setting is on. */
41
41
  euProcessing?: boolean;
42
42
  }
@@ -47,18 +47,18 @@ export interface HarnessProviderBlock {
47
47
  id: string;
48
48
  reason: HarnessProviderBlockReason;
49
49
  /** The rule in force. */
50
- rule: DataZoneRule;
50
+ rule: AIProviderDataZoneRule;
51
51
  /** The rule's label ("EU only"). */
52
52
  ruleLabel: string;
53
53
  /** The entry's effective zone. */
54
- zone: DataZone;
54
+ zone: AIProviderDataZone;
55
55
  /** What a settings table shows on the box it cannot tick. Secret-free. */
56
56
  message: string;
57
57
  }
58
58
  /** What {@link resolveHarnessProviders} applies beyond the surface rule. */
59
59
  export interface HarnessProviderOptions {
60
60
  /** The host's data-zone rule. Absent: `any`. */
61
- dataZoneRule?: DataZoneRule;
61
+ dataZoneRule?: AIProviderDataZoneRule;
62
62
  }
63
63
  /**
64
64
  * - `chosen`: the stored pick, narrowed to what is still eligible, is used.
@@ -89,7 +89,7 @@ export declare function isProviderEligibleForHarness(harness: HarnessProviderInf
89
89
  * when it does not: the candidate must be otherwise eligible
90
90
  * ({@link isProviderEligibleForHarness}) and outside `rule`.
91
91
  */
92
- export declare function harnessProviderBlock(harness: HarnessProviderInfo, candidate: HarnessProviderCandidate, rule: DataZoneRule | undefined): HarnessProviderBlock | undefined;
92
+ export declare function harnessProviderBlock(harness: HarnessProviderInfo, candidate: HarnessProviderCandidate, rule: AIProviderDataZoneRule | undefined): HarnessProviderBlock | undefined;
93
93
  /**
94
94
  * Resolve the providers a harness runs against.
95
95
  *
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * Pure and browser-safe: the settings panel and the server's write gate read
8
8
  * the same rules. The facts come from the catalog preset
9
- * (`@skaile/workspaces`' `AIProviderPreset`): its `credential.kind`, its
9
+ * (`@skaile/provider-registry/catalog`'s `AIProviderPreset`): its `credential.kind`, its
10
10
  * `transport`, its default `params` and its `required_params`. The catalog
11
11
  * names no list of accepted sign-in kinds, so they are derived here from those
12
12
  * facts.
@@ -1,193 +1,23 @@
1
1
  /**
2
2
  * The AI provider registry vocabulary, as forge-settings speaks it.
3
3
  *
4
- * `@skaile/workspaces` owns this schema (`core/src/provider-registry.ts`): the
5
- * validator, the layer merge, the catalog presets and the model-reference
6
- * resolver live there and are deliberately NOT reimplemented here. What follows
7
- * is a *structural* mirror of the record so browser bundles do not import the
8
- * workspaces core. Field names, union members and optionality match the
9
- * upstream record exactly, so an entry produced by the runtime is assignable to
10
- * these types and vice versa.
4
+ * The schema is `@skaile/provider-registry`'s: every record type, the
5
+ * enumerations and the credential-reference grammar below are re-exported from
6
+ * there, so an entry this package reads is the same type the runtime resolves
7
+ * and there is one validator, not a mirror. What this module adds are Forge's
8
+ * view helpers: reading either registry shape in registry order, filtering by
9
+ * kind, voice selection and the credential editor's wording.
11
10
  *
12
- * Browser-safe on purpose — no `node:` imports — so the settings modal renders
13
- * registry entries with the same types the server resolves them with.
11
+ * Browser-safe on purpose — no `node:` imports, and only the browser-safe root
12
+ * of `@skaile/provider-registry` — so the settings modal renders registry
13
+ * entries with the same types the server resolves them with.
14
14
  *
15
15
  * Vocabulary: these are **AI provider** records. They are unrelated to the
16
16
  * platform's connector `ProviderType`/`ProviderLink` (git, SharePoint, Drive).
17
17
  */
18
- /** What an entry produces. `stt`/`tts` use the reduced voice record. */
19
- export declare const AI_PROVIDER_KINDS: readonly ["llm", "stt", "tts", "embed"];
20
- /** Union of {@link AI_PROVIDER_KINDS}. */
21
- export type AIProviderKind = (typeof AI_PROVIDER_KINDS)[number];
22
- /** Kinds that use the voice subset of the record. */
23
- export declare const AI_PROVIDER_VOICE_KINDS: readonly ["stt", "tts"];
24
- /** Union of {@link AI_PROVIDER_VOICE_KINDS}. */
25
- export type AIProviderVoiceKind = (typeof AI_PROVIDER_VOICE_KINDS)[number];
26
- /** Wire protocol of an `llm`/`embed` entry — omp's `api` values verbatim. */
27
- export declare const AI_PROVIDER_SURFACES: readonly ["anthropic-messages", "openai-completions", "openai-responses", "openai-codex-responses", "azure-openai-responses", "bedrock-converse-stream", "google-generative-ai", "google-vertex"];
28
- /** Union of {@link AI_PROVIDER_SURFACES}. */
29
- export type AIProviderSurface = (typeof AI_PROVIDER_SURFACES)[number];
30
- /** How the request reaches the surface — selects the `params` shape. */
31
- export declare const AI_PROVIDER_TRANSPORTS: readonly ["direct", "bedrock", "vertex", "foundry", "azure-openai"];
32
- /** Union of {@link AI_PROVIDER_TRANSPORTS}. */
33
- export type AIProviderTransport = (typeof AI_PROVIDER_TRANSPORTS)[number];
34
- /** How the credential is presented. */
35
- export declare const AI_PROVIDER_CREDENTIAL_KINDS: readonly ["api_key_header", "bearer", "sigv4", "google_adc", "azure_entra", "oauth", "command", "none"];
36
- /** Union of {@link AI_PROVIDER_CREDENTIAL_KINDS}. */
37
- export type AIProviderCredentialKind = (typeof AI_PROVIDER_CREDENTIAL_KINDS)[number];
38
- /**
39
- * Where an endpoint processes data: `eu` inside the EU, `eu_option` only once
40
- * the account's EU-processing setting is on, `transfer` outside the EU,
41
- * `local` on the operator's own machine or network. Upstream
42
- * `AI_PROVIDER_DATA_ZONES` (`@skaile/workspaces` 9.1).
43
- */
44
- export declare const DATA_ZONES: readonly ["eu", "eu_option", "transfer", "local"];
45
- /** Union of {@link DATA_ZONES}. */
46
- export type DataZone = (typeof DATA_ZONES)[number];
47
- /**
48
- * Which data zones a host allows: `any` all, `eu_options` EU, local and
49
- * `eu_option` entries with `eu_processing: true`, `eu` only EU and local.
50
- * Upstream `AI_PROVIDER_DATA_ZONE_RULES` (`@skaile/workspaces` 9.1).
51
- */
52
- export declare const DATA_ZONE_RULES: readonly ["any", "eu_options", "eu"];
53
- /** Union of {@link DATA_ZONE_RULES}. */
54
- export type DataZoneRule = (typeof DATA_ZONE_RULES)[number];
55
- /** `transport: bedrock` routing parameters. */
56
- export interface AIProviderBedrockParams {
57
- /** AWS region, e.g. `us-east-1`. */
58
- region: string;
59
- }
60
- /** `transport: vertex` routing parameters. */
61
- export interface AIProviderVertexParams {
62
- /** GCP project id. */
63
- project: string;
64
- /** Vertex location, e.g. `europe-west4`. */
65
- location: string;
66
- /** Model publisher; defaults to `google`. */
67
- publisher?: string;
68
- }
69
- /** `transport: foundry` routing parameters. */
70
- export interface AIProviderFoundryParams {
71
- /** Azure AI Foundry resource name. */
72
- resource: string;
73
- }
74
- /** `transport: azure-openai` routing parameters. */
75
- export interface AIProviderAzureOpenAIParams {
76
- /** Azure OpenAI resource name. */
77
- resource: string;
78
- /** Deployment the request is routed to. */
79
- deployment: string;
80
- /** `api-version` query value. */
81
- api_version: string;
82
- }
83
- /** Voice parameters, typed per preset: Deepgram `{model, language}`, ElevenLabs `{voice_id, output_format}`. */
84
- export type AIProviderVoiceParams = Record<string, string | number | boolean>;
85
- /** The `transport` → `params` discriminated union. */
86
- export type AIProviderTransportSlice = {
87
- transport?: "direct";
88
- params?: Record<string, never>;
89
- } | {
90
- transport: "bedrock";
91
- params: AIProviderBedrockParams;
92
- } | {
93
- transport: "vertex";
94
- params: AIProviderVertexParams;
95
- } | {
96
- transport: "foundry";
97
- params: AIProviderFoundryParams;
98
- } | {
99
- transport: "azure-openai";
100
- params: AIProviderAzureOpenAIParams;
101
- };
102
- /** Reference to a secret — never the secret itself. See {@link parseCredentialRef}. */
103
- export interface AIProviderCredential {
104
- kind: AIProviderCredentialKind;
105
- /**
106
- * `env:NAME` · `cmd:<shell>` · `platform:<configId>` · `keyring:<service>/<account>`.
107
- * A literal secret here is a validation error, not a warning.
108
- */
109
- ref?: string;
110
- }
111
- /** Per-model capability facts, keyed by the logical (`model_map`) id. */
112
- export interface AIProviderCapabilities {
113
- context_window?: number;
114
- tools?: boolean;
115
- images?: boolean;
116
- thinking_max?: number;
117
- }
118
- /** omp-compatible discovery descriptor. */
119
- export interface AIProviderDiscovery {
120
- type: string;
121
- /** Path appended to `base_url` when the surface default does not apply. */
122
- path?: string;
123
- }
124
- /** Fields every entry carries, whatever its `kind`. */
125
- export interface AIProviderEntryCommon {
126
- /** Display name. Never load-bearing. */
127
- label?: string;
128
- /** Catalog preset this entry inherits from. */
129
- preset?: string;
130
- /** Literal endpoint; wins over the preset value and the transport derivation. */
131
- base_url?: string;
132
- /** Static, non-secret request headers. */
133
- headers?: Record<string, string>;
134
- credential?: AIProviderCredential;
135
- /** Default `true`; read it through {@link isEntryEnabled}. */
136
- enabled?: boolean;
137
- /** Set by the control plane: lower layers cannot override this entry. */
138
- locked?: boolean;
139
- /** Where the endpoint processes data; overrides the preset's zone. Read it through `entryDataZone`. */
140
- data_zone?: DataZone;
141
- /** The account's EU-processing setting is on; only an `eu_option` entry reads it. */
142
- eu_processing?: boolean;
143
- }
144
- /** An `llm`/`embed` entry — the full record. */
145
- export type AIProviderLlmEntry = AIProviderEntryCommon & AIProviderTransportSlice & {
146
- kind: "llm" | "embed";
147
- /** Required unless a `preset` supplies it. */
148
- surface?: AIProviderSurface;
149
- /** Azure's `api-version` lives here. */
150
- query_params?: Record<string, string>;
151
- /** Logical id → provider-side id. */
152
- model_map?: Record<string, string>;
153
- /** Keyed by the logical id, i.e. the `model_map` key. */
154
- capabilities?: Record<string, AIProviderCapabilities>;
155
- discovery?: AIProviderDiscovery;
156
- /**
157
- * The models this entry may be used with; absent = every model, `[]` =
158
- * none. Logical ids of `model_map`, or provider-side ids (what a
159
- * `discovery` entry lists). Read it through {@link isModelAllowed}.
160
- */
161
- allowed_models?: string[];
162
- };
163
- /**
164
- * An `stt`/`tts` entry — the reduced record. `surface`, `transport`,
165
- * `model_map`, `capabilities` and `discovery` are `llm`-only: a TTS voice is
166
- * not a wire protocol. The voice defaults forge apps used to hardcode at their
167
- * call sites (Deepgram model/language, ElevenLabs voice id/output format) live
168
- * in `params`.
169
- */
170
- export type AIProviderVoiceEntry = AIProviderEntryCommon & {
171
- kind: AIProviderVoiceKind;
172
- params?: AIProviderVoiceParams;
173
- };
174
- /** One registry entry. */
175
- export type AIProviderEntry = AIProviderLlmEntry | AIProviderVoiceEntry;
176
- /** Entry id → entry. Iteration order is registry order. */
177
- export type AIProviderRegistry = Record<string, AIProviderEntry>;
178
- /** Configuration layers, lowest precedence first. */
179
- export type AIProviderLayerId = "catalog" | "user" | "workspace" | "runtime" | "session";
180
- /** The merged, layered view the runtime hands to a session. */
181
- export interface MergedAIProviderRegistry {
182
- version: 1;
183
- providers: AIProviderRegistry;
184
- /** Registry order: highest-precedence layer first, document order within a layer. */
185
- order: string[];
186
- /** Highest-precedence layer that contributed to each entry. */
187
- origin: Record<string, AIProviderLayerId>;
188
- /** Entries frozen by `locked: true`. */
189
- locked: string[];
190
- }
18
+ import type { AIProviderEntry, AIProviderKind, AIProviderLayerId, AIProviderLlmEntry, AIProviderRegistry, AIProviderVoiceEntry, AIProviderVoiceKind, MergedAIProviderRegistry } from "@skaile/provider-registry";
19
+ export type { AIProviderAzureOpenAIParams, AIProviderBedrockParams, AIProviderCapabilities, AIProviderCredential, AIProviderCredentialKind, AIProviderCredentialRef, AIProviderDataZone, AIProviderDataZoneRule, AIProviderDiscovery, AIProviderEntry, AIProviderEntryCommon, AIProviderFoundryParams, AIProviderKind, AIProviderLayerId, AIProviderLlmEntry, AIProviderModelAllowListFields, AIProviderRegistry, AIProviderSurface, AIProviderTransport, AIProviderTransportSlice, AIProviderVertexParams, AIProviderVoiceEntry, AIProviderVoiceKind, AIProviderVoiceParams, MergedAIProviderRegistry, } from "@skaile/provider-registry";
20
+ export { AI_PROVIDER_CREDENTIAL_KINDS, AI_PROVIDER_DATA_ZONE_RULES, AI_PROVIDER_DATA_ZONES, AI_PROVIDER_KINDS, AI_PROVIDER_SURFACES, AI_PROVIDER_TRANSPORTS, AI_PROVIDER_VOICE_KINDS, allowedModelIds, formatCredentialRef, isEntryEnabled, isModelAllowed, parseCredentialRef, } from "@skaile/provider-registry";
191
21
  /** Either shape a consumer may be handed. */
192
22
  export type AIProviderRegistryView = AIProviderRegistry | MergedAIProviderRegistry;
193
23
  /**
@@ -197,8 +27,13 @@ export type AIProviderRegistryView = AIProviderRegistry | MergedAIProviderRegist
197
27
  * reading it) has to put the entries back in the shape it was handed.
198
28
  */
199
29
  export declare function isMergedRegistry(registry: AIProviderRegistryView): registry is MergedAIProviderRegistry;
200
- /** `enabled` defaults to `true`. */
201
- export declare function isEntryEnabled(entry: AIProviderEntry): boolean;
30
+ /**
31
+ * The layered shape of a view. A merged registry is returned as is; a bare
32
+ * entry map becomes one layer in its own iteration order, reported as the
33
+ * `runtime` layer (the host handed it over in memory) with nothing locked.
34
+ * The model constructor resolves against this shape only.
35
+ */
36
+ export declare function asMergedRegistry(registry: AIProviderRegistryView, layer?: AIProviderLayerId): MergedAIProviderRegistry;
202
37
  /** Entries in registry order, as `[id, entry]` pairs. Both registry shapes accepted. */
203
38
  export declare function registryEntries(registry: AIProviderRegistryView): Array<[string, AIProviderEntry]>;
204
39
  /**
@@ -211,17 +46,6 @@ export declare function registryEntries(registry: AIProviderRegistryView): Array
211
46
  export declare function entriesOfKind<K extends AIProviderKind>(registry: AIProviderRegistryView, kind: K): Array<[string, K extends AIProviderVoiceKind ? AIProviderVoiceEntry : AIProviderLlmEntry]>;
212
47
  /** Whether an entry uses the reduced voice record. Narrows `params` into view. */
213
48
  export declare function isVoiceEntry(entry: AIProviderEntry): entry is AIProviderVoiceEntry;
214
- /**
215
- * Whether an entry's `allowed_models` lets it be used with `modelId`. Absent
216
- * allows every model, `[]` none. `modelId` and the list may each hold the
217
- * logical id (a `model_map` key) or the provider-side id; a trailing
218
- * thinking-level suffix (`:high`) is ignored. Mirrors `isModelAllowed` of
219
- * `@skaile/workspaces/core` 9.1, which enforces it at model resolution.
220
- */
221
- export declare function isModelAllowed(entry: {
222
- allowed_models?: readonly string[];
223
- model_map?: Record<string, string>;
224
- }, modelId: string): boolean;
225
49
  /**
226
50
  * The one `params` key a settings override replaces, per voice kind.
227
51
  *
@@ -249,31 +73,6 @@ export declare const VOICE_OVERRIDE_SETTING: {
249
73
  * @returns The chosen id, or `undefined` when no entry of that kind exists.
250
74
  */
251
75
  export declare function selectVoiceEntryId(ids: readonly string[], wanted?: string): string | undefined;
252
- /** A parsed `credential.ref`. */
253
- export type AIProviderCredentialRef = {
254
- form: "env";
255
- name: string;
256
- } | {
257
- form: "cmd";
258
- command: string;
259
- } | {
260
- form: "platform";
261
- configId: string;
262
- } | {
263
- form: "keyring";
264
- service: string;
265
- account: string;
266
- };
267
- /**
268
- * Parse a `credential.ref`.
269
- *
270
- * Returns `undefined` for anything that is not one of the four reference forms
271
- * — which, in a registry entry or a credential editor, means someone typed a
272
- * literal secret.
273
- */
274
- export declare function parseCredentialRef(ref: string): AIProviderCredentialRef | undefined;
275
- /** Render a parsed reference back to its `<form>:<body>` string. */
276
- export declare function formatCredentialRef(ref: AIProviderCredentialRef): string;
277
76
  /** The reference forms, spelled the way a credential editor offers them. */
278
77
  export declare const CREDENTIAL_REF_FORMS: readonly ["env:NAME", "keyring:service/account", "cmd:<shell command>", "platform:<configId>"];
279
78
  /** What a credential editor tells a user who typed something that is not a reference. */
@@ -1,39 +1,31 @@
1
- export const AI_PROVIDER_KINDS = ["llm", "stt", "tts", "embed"];
2
- export const AI_PROVIDER_VOICE_KINDS = ["stt", "tts"];
3
- export const AI_PROVIDER_SURFACES = [
4
- "anthropic-messages",
5
- "openai-completions",
6
- "openai-responses",
7
- "openai-codex-responses",
8
- "azure-openai-responses",
9
- "bedrock-converse-stream",
10
- "google-generative-ai",
11
- "google-vertex"
12
- ];
13
- export const AI_PROVIDER_TRANSPORTS = [
14
- "direct",
15
- "bedrock",
16
- "vertex",
17
- "foundry",
18
- "azure-openai"
19
- ];
20
- export const AI_PROVIDER_CREDENTIAL_KINDS = [
21
- "api_key_header",
22
- "bearer",
23
- "sigv4",
24
- "google_adc",
25
- "azure_entra",
26
- "oauth",
27
- "command",
28
- "none"
29
- ];
30
- export const DATA_ZONES = ["eu", "eu_option", "transfer", "local"];
31
- export const DATA_ZONE_RULES = ["any", "eu_options", "eu"];
1
+ import { isEntryEnabled, parseCredentialRef } from "@skaile/provider-registry";
2
+ export {
3
+ AI_PROVIDER_CREDENTIAL_KINDS,
4
+ AI_PROVIDER_DATA_ZONE_RULES,
5
+ AI_PROVIDER_DATA_ZONES,
6
+ AI_PROVIDER_KINDS,
7
+ AI_PROVIDER_SURFACES,
8
+ AI_PROVIDER_TRANSPORTS,
9
+ AI_PROVIDER_VOICE_KINDS,
10
+ allowedModelIds,
11
+ formatCredentialRef,
12
+ isEntryEnabled,
13
+ isModelAllowed,
14
+ parseCredentialRef
15
+ } from "@skaile/provider-registry";
32
16
  export function isMergedRegistry(registry) {
33
17
  return Array.isArray(registry.order);
34
18
  }
35
- export function isEntryEnabled(entry) {
36
- return entry.enabled !== false;
19
+ export function asMergedRegistry(registry, layer = "runtime") {
20
+ if (isMergedRegistry(registry)) return registry;
21
+ const order = Object.keys(registry);
22
+ return {
23
+ version: 1,
24
+ providers: registry,
25
+ order,
26
+ origin: Object.fromEntries(order.map((id) => [id, layer])),
27
+ locked: []
28
+ };
37
29
  }
38
30
  export function registryEntries(registry) {
39
31
  if (isMergedRegistry(registry)) {
@@ -49,20 +41,6 @@ export function entriesOfKind(registry, kind) {
49
41
  export function isVoiceEntry(entry) {
50
42
  return entry.kind === "stt" || entry.kind === "tts";
51
43
  }
52
- const THINKING_SUFFIX = /:(off|minimal|low|medium|high|xhigh)$/i;
53
- export function isModelAllowed(entry, modelId) {
54
- const list = entry.allowed_models;
55
- if (list === void 0) return true;
56
- const matches = (id) => {
57
- if (list.includes(id)) return true;
58
- const mapped = entry.model_map?.[id];
59
- if (mapped !== void 0 && list.includes(mapped)) return true;
60
- return list.some((allowed) => entry.model_map?.[allowed] === id);
61
- };
62
- if (matches(modelId)) return true;
63
- const base = modelId.replace(THINKING_SUFFIX, "");
64
- return base !== modelId && matches(base);
65
- }
66
44
  export const VOICE_OVERRIDE_PARAM = { stt: "language", tts: "voice_id" };
67
45
  export const VOICE_OVERRIDE_SETTING = { stt: "sttLanguage", tts: "ttsVoice" };
68
46
  export function selectVoiceEntryId(ids, wanted) {
@@ -70,41 +48,7 @@ export function selectVoiceEntryId(ids, wanted) {
70
48
  if (trimmed && ids.includes(trimmed)) return trimmed;
71
49
  return ids[0];
72
50
  }
73
- const ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
74
51
  const CREDENTIAL_REF = /^(env|cmd|platform|keyring):([\s\S]+)$/;
75
- export function parseCredentialRef(ref) {
76
- const match = CREDENTIAL_REF.exec(ref.trim());
77
- if (!match) return void 0;
78
- const body = match[2].trim();
79
- if (body === "") return void 0;
80
- switch (match[1]) {
81
- case "env":
82
- return ENV_NAME.test(body) ? { form: "env", name: body } : void 0;
83
- case "cmd":
84
- return { form: "cmd", command: body };
85
- case "platform":
86
- return { form: "platform", configId: body };
87
- case "keyring": {
88
- const slash = body.indexOf("/");
89
- if (slash <= 0 || slash === body.length - 1) return void 0;
90
- return { form: "keyring", service: body.slice(0, slash), account: body.slice(slash + 1) };
91
- }
92
- default:
93
- return void 0;
94
- }
95
- }
96
- export function formatCredentialRef(ref) {
97
- switch (ref.form) {
98
- case "env":
99
- return `env:${ref.name}`;
100
- case "cmd":
101
- return `cmd:${ref.command}`;
102
- case "platform":
103
- return `platform:${ref.configId}`;
104
- case "keyring":
105
- return `keyring:${ref.service}/${ref.account}`;
106
- }
107
- }
108
52
  export const CREDENTIAL_REF_FORMS = [
109
53
  "env:NAME",
110
54
  "keyring:service/account",
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import type { EnvLayerId } from "../shared/env-vars.js";
8
8
  import type { CredentialExpiry, ProviderDeploymentRow, ProviderSignInId } from "../shared/provider-accounts.js";
9
- import type { AIProviderCredentialKind, AIProviderKind, AIProviderSurface, AIProviderTransport, DataZone, DataZoneRule } from "../shared/provider-registry.js";
9
+ import type { AIProviderCredentialKind, AIProviderDataZone, AIProviderDataZoneRule, AIProviderKind, AIProviderSurface, AIProviderTransport } from "../shared/provider-registry.js";
10
10
  import type { ProvidersPanelClient } from "./providers-client.js";
11
11
  /** Registry document a provider entry is defined in. */
12
12
  export type ProvidersPanelLayer = "instance" | "workspace";
@@ -54,7 +54,7 @@ export interface ProvidersPanelProps {
54
54
  * The host's data-zone rule ("Where may AI process data?"). Rows outside it
55
55
  * show a muted note naming the rule. Absent or `any`: no row is marked.
56
56
  */
57
- dataZoneRule?: DataZoneRule;
57
+ dataZoneRule?: AIProviderDataZoneRule;
58
58
  }
59
59
  /** Emits both variants declare. */
60
60
  export type ProvidersPanelEmit = (event: "changed") => void;
@@ -91,7 +91,7 @@ export interface ProvidersPanelEntry {
91
91
  /** The scope's own document's values for the editable fields the dialog does not show. */
92
92
  layerFields?: Record<string, unknown>;
93
93
  /** Where the entry processes data. Absent (an older host): the preset's zone, else `transfer`. */
94
- dataZone?: DataZone;
94
+ dataZone?: AIProviderDataZone;
95
95
  /** The entry's `eu_processing` switch. */
96
96
  euProcessing?: boolean;
97
97
  /** The entry's `allowed_models`; absent = every model, `[]` = none. */
@@ -164,7 +164,7 @@ export interface ProvidersPanelRow extends ProvidersPanelEntry {
164
164
  /** "Renew" is offered next to the expiry: the credential can be changed here. */
165
165
  canRenew: boolean;
166
166
  /** The entry's effective data zone. */
167
- zone: DataZone;
167
+ zone: AIProviderDataZone;
168
168
  /** The zone chip: EU / EU option / Transfer / Local. */
169
169
  zoneLabel: string;
170
170
  /** "EU processing is on for this account" is offered: an `eu_option` entry the panel may write. */
@@ -23,8 +23,8 @@
23
23
  import type { HarnessDetection } from "@skaile/workspaces/sdk/bridge";
24
24
  import type { HarnessInfo } from "@skaile/workspaces/types";
25
25
  import { type HarnessProviderBlock, type HarnessProviderCandidate } from "../shared/harness-providers.js";
26
- import type { DataZoneRule } from "../shared/provider-registry.js";
27
- import type { SkaileSettings } from "../shared/settings-types.js";
26
+ import type { AIProviderDataZoneRule } from "../shared/provider-registry.js";
27
+ import type { SkaileSettings } from "./settings-types.js";
28
28
  /** The slice of {@link SkaileSettings} that selects, configures and locks harnesses. */
29
29
  export type HarnessSettings = Pick<SkaileSettings, "harness" | "harnessModels" | "harnessRoles" | "harnessProviders" | "enabledHarnesses" | "lockHarness" | "lockProvider" | "lockModel" | "lockAudio">;
30
30
  /** One lock flag and where its value comes from. */
@@ -79,7 +79,7 @@ export interface HarnessRowProviders {
79
79
  /** `GET /api/providers` rows (or any {@link HarnessProviderCandidate}s), in registry order. */
80
80
  candidates: readonly HarnessProviderCandidate[];
81
81
  /** The host's data-zone rule. Absent: `any`. */
82
- dataZoneRule?: DataZoneRule;
82
+ dataZoneRule?: AIProviderDataZoneRule;
83
83
  }
84
84
  /**
85
85
  * The rows of `GET /api/agents`: one per harness, in `harnesses` order. Hosts
@@ -0,0 +1,16 @@
1
+ /**
2
+ * `@skaile/forge-settings/workspaces` — the Skaile Workspaces integration:
3
+ * the Workspaces `settings.json` document (`SkaileSettings`, defaults, legacy
4
+ * migrations), harness settings, project-registry model resolution with the
5
+ * Workspaces keyring location, and the address of a running Workspaces model
6
+ * endpoint.
7
+ *
8
+ * The only entry that needs the optional peer `@skaile/workspaces`; the generic
9
+ * entries (`/server`, `/vue`, `/nuxt`, `/ai-sdk`, `/editor`) never import it.
10
+ */
11
+ export * from "./harness-settings.js";
12
+ export * from "./language-model.js";
13
+ export * from "./model-endpoint.js";
14
+ export * from "./provider-settings.js";
15
+ export * from "./settings-defaults.js";
16
+ export type { DomainConfig, SkaileSettings } from "./settings-types.js";
@@ -0,0 +1,5 @@
1
+ export * from "./harness-settings.js";
2
+ export * from "./language-model.js";
3
+ export * from "./model-endpoint.js";
4
+ export * from "./provider-settings.js";
5
+ export * from "./settings-defaults.js";
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Project-oriented model resolution for a Skaile Workspaces host: load the
3
+ * project's merged provider registry once (user + workspace layers + catalog),
4
+ * keep the forge keyring where Workspaces hosts always kept it, and delegate to
5
+ * the generic `resolveRegistryLanguageModel` (`@skaile/forge-settings/ai-sdk`).
6
+ */
7
+ import type { MergedAIProviderRegistry } from "@skaile/provider-registry";
8
+ import { type ResolvedLanguageModel, type ResolveRegistryLanguageModelOptions } from "../ai-sdk/index.js";
9
+ /**
10
+ * The forge keyring of a Workspaces host: `FORGE_CREDENTIAL_STORE` of `env`
11
+ * when set, else `<skaile home>/forge-credentials.json`, beside the user's
12
+ * `settings.json` (follows `$SKAILE_HOME`). Generic entries never assume this
13
+ * home; a Workspaces host passes it as the store `path` (or pins it into
14
+ * `FORGE_CREDENTIAL_STORE`).
15
+ */
16
+ export declare function workspaceCredentialStorePath(env?: Readonly<Record<string, string | undefined>>): string;
17
+ /**
18
+ * The merged provider registry of `projectDir` — the user layer, the
19
+ * project's `.skaile/providers.yml` and the catalog presets they name.
20
+ *
21
+ * @throws {ModelResolutionError} `registry_invalid` on any error diagnostic.
22
+ */
23
+ export declare function loadWorkspaceProviderRegistry(projectDir: string): MergedAIProviderRegistry;
24
+ /** Options for {@link resolveWorkspaceLanguageModel}. */
25
+ export interface ResolveWorkspaceLanguageModelOptions extends Omit<ResolveRegistryLanguageModelOptions, "registry"> {
26
+ /** Workspace whose `.skaile/providers.yml` is the workspace layer. */
27
+ projectDir: string;
28
+ /**
29
+ * An already merged (e.g. transform-proxy-routed) registry. When set,
30
+ * `projectDir` is not read for providers; the selector, the policy check
31
+ * and the model all resolve against this registry.
32
+ */
33
+ registry?: MergedAIProviderRegistry;
34
+ }
35
+ /**
36
+ * The AI SDK model for `selector`, resolved against the registry of
37
+ * `projectDir` (read once) or the given `registry`.
38
+ *
39
+ * `keyring:` references read the store at `path`, else the environment's
40
+ * `FORGE_CREDENTIAL_STORE`, else {@link workspaceCredentialStorePath}.
41
+ *
42
+ * @throws {ModelResolutionError} `registry_invalid` (only when read from disk),
43
+ * plus every failure of `resolveRegistryLanguageModel`.
44
+ */
45
+ export declare function resolveWorkspaceLanguageModel(selector: string, options: ResolveWorkspaceLanguageModelOptions): Promise<ResolvedLanguageModel>;
@@ -0,0 +1,26 @@
1
+ import { join } from "node:path";
2
+ import { loadMergedProviderRegistry } from "@skaile/workspaces/sdk/bridge";
3
+ import { globalSkaileDir } from "@skaile/workspaces/sdk/core";
4
+ import {
5
+ resolveRegistryLanguageModel
6
+ } from "../ai-sdk/index.js";
7
+ import { ModelResolutionError } from "../model/errors.js";
8
+ export function workspaceCredentialStorePath(env) {
9
+ return env?.FORGE_CREDENTIAL_STORE || join(globalSkaileDir(), "forge-credentials.json");
10
+ }
11
+ export function loadWorkspaceProviderRegistry(projectDir) {
12
+ const { merged, diagnostics } = loadMergedProviderRegistry({ projectDir });
13
+ const errors = diagnostics.filter((d) => d.severity === "error").map((d) => d.path ? `${d.path}: ${d.message}` : d.message);
14
+ if (errors.length > 0) {
15
+ throw new ModelResolutionError("registry_invalid", `providers registry: ${errors.join("; ")}`);
16
+ }
17
+ return merged;
18
+ }
19
+ export async function resolveWorkspaceLanguageModel(selector, options) {
20
+ const { projectDir, registry, ...rest } = options;
21
+ return resolveRegistryLanguageModel(selector, {
22
+ ...rest,
23
+ registry: registry ?? loadWorkspaceProviderRegistry(projectDir),
24
+ path: options.path ?? workspaceCredentialStorePath(options.env)
25
+ });
26
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Where a running `skaile serve --model-endpoint` can be reached: the
3
+ * OpenAI-compatible URL and its bearer token.
4
+ *
5
+ * Read-only: the runner writes, owns and removes `.skaile/model-endpoint.json`;
6
+ * this module only reads what the host points it at. No directory climbing, no
7
+ * working-directory lookup, and the token never appears in an error.
8
+ */
9
+ /** Env var names the runner hands its endpoint URL and token through. */
10
+ export declare const WORKSPACE_MODEL_ENDPOINT_URL_ENV = "SKAILE_MODEL_ENDPOINT_URL";
11
+ export declare const WORKSPACE_MODEL_ENDPOINT_TOKEN_ENV = "SKAILE_MODEL_ENDPOINT_TOKEN";
12
+ /** Address of a Workspaces model endpoint. `token` is a secret. */
13
+ export interface WorkspaceModelEndpoint {
14
+ /** `http(s)://<host>:<port>/v1`. */
15
+ url: string;
16
+ /** Bearer token; never log it. */
17
+ token: string;
18
+ }
19
+ /** Options for {@link readWorkspaceModelEndpoint}. */
20
+ export interface ReadWorkspaceModelEndpointOptions {
21
+ /** Environment snapshot the URL/token pair is read from. */
22
+ env: Record<string, string | undefined>;
23
+ /** Project whose `.skaile/model-endpoint.json` is read when the env pair is absent. */
24
+ projectDir?: string;
25
+ }
26
+ /**
27
+ * The endpoint address, or `null` when none is configured.
28
+ *
29
+ * 1. `SKAILE_MODEL_ENDPOINT_URL` + `SKAILE_MODEL_ENDPOINT_TOKEN` in `env` win.
30
+ * Both must be set and the URL must be HTTP(S); one without the other is a
31
+ * configuration error, not an absence.
32
+ * 2. Otherwise, only when `projectDir` is given, `<projectDir>/.skaile/model-endpoint.json`
33
+ * (`{ url, token, pid }`). A missing file is `null`.
34
+ *
35
+ * @throws Error a malformed or incomplete configuration; the message names the
36
+ * source and the problem, never the token.
37
+ */
38
+ export declare function readWorkspaceModelEndpoint(options: ReadWorkspaceModelEndpointOptions): Promise<WorkspaceModelEndpoint | null>;