@skaile/forge-settings 1.3.3 → 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 (88) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +52 -9
  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 +13 -0
  7. package/dist/runtime/components/ForgeProvidersPanel.vue +461 -78
  8. package/dist/runtime/components/ForgeProvidersPanel.vue.d.ts +13 -0
  9. package/dist/runtime/editor/index.d.ts +55 -16
  10. package/dist/runtime/editor/index.js +82 -40
  11. package/dist/runtime/model/selector.d.ts +16 -0
  12. package/dist/runtime/model/selector.js +12 -0
  13. package/dist/runtime/server/credential-store.d.ts +16 -4
  14. package/dist/runtime/server/credential-store.js +3 -4
  15. package/dist/runtime/server/env-layers.d.ts +1 -1
  16. package/dist/runtime/server/env-layers.js +3 -3
  17. package/dist/runtime/server/index.d.ts +9 -8
  18. package/dist/runtime/server/index.js +3 -3
  19. package/dist/runtime/server/provider-discovery.d.ts +28 -8
  20. package/dist/runtime/server/provider-discovery.js +57 -33
  21. package/dist/runtime/server/provider-layers.d.ts +1 -1
  22. package/dist/runtime/server/provider-layers.js +3 -5
  23. package/dist/runtime/server/provider-service.d.ts +26 -2
  24. package/dist/runtime/server/provider-service.js +16 -1
  25. package/dist/runtime/server/provider-write.d.ts +26 -10
  26. package/dist/runtime/server/provider-write.js +75 -7
  27. package/dist/runtime/server/voice-provider.d.ts +61 -0
  28. package/dist/runtime/server/voice-provider.js +31 -0
  29. package/dist/runtime/shared/data-zone.d.ts +68 -0
  30. package/dist/runtime/shared/data-zone.js +79 -0
  31. package/dist/runtime/shared/harness-providers.d.ts +42 -3
  32. package/dist/runtime/shared/harness-providers.js +31 -8
  33. package/dist/runtime/shared/harnesses.d.ts +7 -0
  34. package/dist/runtime/shared/harnesses.js +18 -0
  35. package/dist/runtime/shared/provider-accounts.d.ts +215 -0
  36. package/dist/runtime/shared/provider-accounts.js +255 -0
  37. package/dist/runtime/shared/provider-registry.d.ts +19 -182
  38. package/dist/runtime/shared/provider-registry.js +25 -65
  39. package/dist/runtime/vue/index.d.ts +2 -0
  40. package/dist/runtime/vue/index.js +2 -0
  41. package/dist/runtime/vue/providers-panel-model.d.ts +98 -5
  42. package/dist/runtime/vue/providers-panel-model.js +13 -1
  43. package/dist/runtime/vue/useProvidersPanel.d.ts +66 -5
  44. package/dist/runtime/vue/useProvidersPanel.js +417 -29
  45. package/dist/runtime/{server → workspaces}/harness-settings.d.ts +51 -13
  46. package/dist/runtime/{server → workspaces}/harness-settings.js +93 -9
  47. package/dist/runtime/workspaces/index.d.ts +16 -0
  48. package/dist/runtime/workspaces/index.js +5 -0
  49. package/dist/runtime/workspaces/language-model.d.ts +45 -0
  50. package/dist/runtime/workspaces/language-model.js +26 -0
  51. package/dist/runtime/workspaces/model-endpoint.d.ts +38 -0
  52. package/dist/runtime/workspaces/model-endpoint.js +54 -0
  53. package/dist/runtime/workspaces/provider-settings.d.ts +35 -0
  54. package/dist/runtime/{server → workspaces}/provider-settings.js +0 -31
  55. package/dist/runtime/{server → workspaces}/settings-defaults.d.ts +1 -1
  56. package/dist/runtime/{shared → workspaces}/settings-types.d.ts +11 -4
  57. package/package.json +11 -2
  58. package/src/runtime/ai-sdk/index.ts +88 -47
  59. package/src/runtime/components/ForgeProvidersPanel.vue +477 -78
  60. package/src/runtime/editor/index.ts +158 -63
  61. package/src/runtime/model/selector.ts +31 -0
  62. package/src/runtime/server/credential-store.ts +20 -10
  63. package/src/runtime/server/env-layers.ts +4 -4
  64. package/src/runtime/server/index.ts +9 -8
  65. package/src/runtime/server/provider-discovery.ts +114 -48
  66. package/src/runtime/server/provider-layers.ts +6 -6
  67. package/src/runtime/server/provider-service.ts +53 -1
  68. package/src/runtime/server/provider-write.ts +132 -18
  69. package/src/runtime/server/voice-provider.ts +110 -0
  70. package/src/runtime/shared/data-zone.ts +179 -0
  71. package/src/runtime/shared/harness-providers.ts +78 -7
  72. package/src/runtime/shared/harnesses.ts +25 -0
  73. package/src/runtime/shared/provider-accounts.ts +526 -0
  74. package/src/runtime/shared/provider-registry.ts +82 -256
  75. package/src/runtime/vue/index.ts +2 -0
  76. package/src/runtime/vue/providers-panel-model.ts +123 -5
  77. package/src/runtime/vue/useProvidersPanel.ts +589 -37
  78. package/src/runtime/{server → workspaces}/harness-settings.ts +166 -22
  79. package/src/runtime/workspaces/index.ts +17 -0
  80. package/src/runtime/workspaces/language-model.ts +82 -0
  81. package/src/runtime/workspaces/model-endpoint.ts +100 -0
  82. package/src/runtime/workspaces/provider-settings.ts +96 -0
  83. package/src/runtime/{server → workspaces}/settings-defaults.ts +1 -1
  84. package/src/runtime/{shared → workspaces}/settings-types.ts +11 -4
  85. package/dist/runtime/server/provider-settings.d.ts +0 -78
  86. package/src/runtime/server/provider-settings.ts +0 -192
  87. /package/dist/runtime/{server → workspaces}/settings-defaults.js +0 -0
  88. /package/dist/runtime/{shared → workspaces}/settings-types.js +0 -0
@@ -1,211 +1,74 @@
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
18
 
19
- // ── Enums ──────────────────────────────────────────────────────────────────
20
-
21
- /** What an entry produces. `stt`/`tts` use the reduced voice record. */
22
- export const AI_PROVIDER_KINDS = ["llm", "stt", "tts", "embed"] as const;
23
-
24
- /** Union of {@link AI_PROVIDER_KINDS}. */
25
- export type AIProviderKind = (typeof AI_PROVIDER_KINDS)[number];
26
-
27
- /** Kinds that use the voice subset of the record. */
28
- export const AI_PROVIDER_VOICE_KINDS = ["stt", "tts"] as const;
29
-
30
- /** Union of {@link AI_PROVIDER_VOICE_KINDS}. */
31
- export type AIProviderVoiceKind = (typeof AI_PROVIDER_VOICE_KINDS)[number];
32
-
33
- /** Wire protocol of an `llm`/`embed` entry — omp's `api` values verbatim. */
34
- export const AI_PROVIDER_SURFACES = [
35
- "anthropic-messages",
36
- "openai-completions",
37
- "openai-responses",
38
- "openai-codex-responses",
39
- "azure-openai-responses",
40
- "bedrock-converse-stream",
41
- "google-generative-ai",
42
- "google-vertex",
43
- ] as const;
44
-
45
- /** Union of {@link AI_PROVIDER_SURFACES}. */
46
- export type AIProviderSurface = (typeof AI_PROVIDER_SURFACES)[number];
47
-
48
- /** How the request reaches the surface — selects the `params` shape. */
49
- export const AI_PROVIDER_TRANSPORTS = [
50
- "direct",
51
- "bedrock",
52
- "vertex",
53
- "foundry",
54
- "azure-openai",
55
- ] as const;
56
-
57
- /** Union of {@link AI_PROVIDER_TRANSPORTS}. */
58
- export type AIProviderTransport = (typeof AI_PROVIDER_TRANSPORTS)[number];
59
-
60
- /** How the credential is presented. */
61
- export const AI_PROVIDER_CREDENTIAL_KINDS = [
62
- "api_key_header",
63
- "bearer",
64
- "sigv4",
65
- "google_adc",
66
- "azure_entra",
67
- "oauth",
68
- "command",
69
- "none",
70
- ] as const;
71
-
72
- /** Union of {@link AI_PROVIDER_CREDENTIAL_KINDS}. */
73
- export type AIProviderCredentialKind = (typeof AI_PROVIDER_CREDENTIAL_KINDS)[number];
74
-
75
- // ── Record ─────────────────────────────────────────────────────────────────
76
-
77
- /** `transport: bedrock` routing parameters. */
78
- export interface AIProviderBedrockParams {
79
- /** AWS region, e.g. `us-east-1`. */
80
- region: string;
81
- }
82
-
83
- /** `transport: vertex` routing parameters. */
84
- export interface AIProviderVertexParams {
85
- /** GCP project id. */
86
- project: string;
87
- /** Vertex location, e.g. `europe-west4`. */
88
- location: string;
89
- /** Model publisher; defaults to `google`. */
90
- publisher?: string;
91
- }
92
-
93
- /** `transport: foundry` routing parameters. */
94
- export interface AIProviderFoundryParams {
95
- /** Azure AI Foundry resource name. */
96
- resource: string;
97
- }
98
-
99
- /** `transport: azure-openai` routing parameters. */
100
- export interface AIProviderAzureOpenAIParams {
101
- /** Azure OpenAI resource name. */
102
- resource: string;
103
- /** Deployment the request is routed to. */
104
- deployment: string;
105
- /** `api-version` query value. */
106
- api_version: string;
107
- }
108
-
109
- /** Voice parameters, typed per preset: Deepgram `{model, language}`, ElevenLabs `{voice_id, output_format}`. */
110
- export type AIProviderVoiceParams = Record<string, string | number | boolean>;
111
-
112
- /** The `transport` → `params` discriminated union. */
113
- export type AIProviderTransportSlice =
114
- | { transport?: "direct"; params?: Record<string, never> }
115
- | { transport: "bedrock"; params: AIProviderBedrockParams }
116
- | { transport: "vertex"; params: AIProviderVertexParams }
117
- | { transport: "foundry"; params: AIProviderFoundryParams }
118
- | { transport: "azure-openai"; params: AIProviderAzureOpenAIParams };
119
-
120
- /** Reference to a secret — never the secret itself. See {@link parseCredentialRef}. */
121
- export interface AIProviderCredential {
122
- kind: AIProviderCredentialKind;
123
- /**
124
- * `env:NAME` · `cmd:<shell>` · `platform:<configId>` · `keyring:<service>/<account>`.
125
- * A literal secret here is a validation error, not a warning.
126
- */
127
- ref?: string;
128
- }
129
-
130
- /** Per-model capability facts, keyed by the logical (`model_map`) id. */
131
- export interface AIProviderCapabilities {
132
- context_window?: number;
133
- tools?: boolean;
134
- images?: boolean;
135
- thinking_max?: number;
136
- }
137
-
138
- /** omp-compatible discovery descriptor. */
139
- export interface AIProviderDiscovery {
140
- type: string;
141
- /** Path appended to `base_url` when the surface default does not apply. */
142
- path?: string;
143
- }
144
-
145
- /** Fields every entry carries, whatever its `kind`. */
146
- export interface AIProviderEntryCommon {
147
- /** Display name. Never load-bearing. */
148
- label?: string;
149
- /** Catalog preset this entry inherits from. */
150
- preset?: string;
151
- /** Literal endpoint; wins over the preset value and the transport derivation. */
152
- base_url?: string;
153
- /** Static, non-secret request headers. */
154
- headers?: Record<string, string>;
155
- credential?: AIProviderCredential;
156
- /** Default `true`; read it through {@link isEntryEnabled}. */
157
- enabled?: boolean;
158
- /** Set by the control plane: lower layers cannot override this entry. */
159
- locked?: boolean;
160
- }
161
-
162
- /** An `llm`/`embed` entry — the full record. */
163
- export type AIProviderLlmEntry = AIProviderEntryCommon &
164
- AIProviderTransportSlice & {
165
- kind: "llm" | "embed";
166
- /** Required unless a `preset` supplies it. */
167
- surface?: AIProviderSurface;
168
- /** Azure's `api-version` lives here. */
169
- query_params?: Record<string, string>;
170
- /** Logical id → provider-side id. */
171
- model_map?: Record<string, string>;
172
- /** Keyed by the logical id, i.e. the `model_map` key. */
173
- capabilities?: Record<string, AIProviderCapabilities>;
174
- discovery?: AIProviderDiscovery;
175
- };
176
-
177
- /**
178
- * An `stt`/`tts` entry — the reduced record. `surface`, `transport`,
179
- * `model_map`, `capabilities` and `discovery` are `llm`-only: a TTS voice is
180
- * not a wire protocol. The voice defaults forge apps used to hardcode at their
181
- * call sites (Deepgram model/language, ElevenLabs voice id/output format) live
182
- * in `params`.
183
- */
184
- export type AIProviderVoiceEntry = AIProviderEntryCommon & {
185
- kind: AIProviderVoiceKind;
186
- params?: AIProviderVoiceParams;
187
- };
188
-
189
- /** One registry entry. */
190
- export type AIProviderEntry = AIProviderLlmEntry | AIProviderVoiceEntry;
191
-
192
- /** Entry id → entry. Iteration order is registry order. */
193
- export type AIProviderRegistry = Record<string, AIProviderEntry>;
194
-
195
- /** Configuration layers, lowest precedence first. */
196
- export type AIProviderLayerId = "catalog" | "user" | "workspace" | "runtime" | "session";
197
-
198
- /** The merged, layered view the runtime hands to a session. */
199
- export interface MergedAIProviderRegistry {
200
- version: 1;
201
- providers: AIProviderRegistry;
202
- /** Registry order: highest-precedence layer first, document order within a layer. */
203
- order: string[];
204
- /** Highest-precedence layer that contributed to each entry. */
205
- origin: Record<string, AIProviderLayerId>;
206
- /** Entries frozen by `locked: true`. */
207
- locked: string[];
208
- }
19
+ import type {
20
+ AIProviderEntry,
21
+ AIProviderKind,
22
+ AIProviderLayerId,
23
+ AIProviderLlmEntry,
24
+ AIProviderRegistry,
25
+ AIProviderVoiceEntry,
26
+ AIProviderVoiceKind,
27
+ MergedAIProviderRegistry,
28
+ } from "@skaile/provider-registry";
29
+ import { isEntryEnabled, parseCredentialRef } from "@skaile/provider-registry";
30
+
31
+ export type {
32
+ AIProviderAzureOpenAIParams,
33
+ AIProviderBedrockParams,
34
+ AIProviderCapabilities,
35
+ AIProviderCredential,
36
+ AIProviderCredentialKind,
37
+ AIProviderCredentialRef,
38
+ AIProviderDataZone,
39
+ AIProviderDataZoneRule,
40
+ AIProviderDiscovery,
41
+ AIProviderEntry,
42
+ AIProviderEntryCommon,
43
+ AIProviderFoundryParams,
44
+ AIProviderKind,
45
+ AIProviderLayerId,
46
+ AIProviderLlmEntry,
47
+ AIProviderModelAllowListFields,
48
+ AIProviderRegistry,
49
+ AIProviderSurface,
50
+ AIProviderTransport,
51
+ AIProviderTransportSlice,
52
+ AIProviderVertexParams,
53
+ AIProviderVoiceEntry,
54
+ AIProviderVoiceKind,
55
+ AIProviderVoiceParams,
56
+ MergedAIProviderRegistry,
57
+ } from "@skaile/provider-registry";
58
+ export {
59
+ AI_PROVIDER_CREDENTIAL_KINDS,
60
+ AI_PROVIDER_DATA_ZONE_RULES,
61
+ AI_PROVIDER_DATA_ZONES,
62
+ AI_PROVIDER_KINDS,
63
+ AI_PROVIDER_SURFACES,
64
+ AI_PROVIDER_TRANSPORTS,
65
+ AI_PROVIDER_VOICE_KINDS,
66
+ allowedModelIds,
67
+ formatCredentialRef,
68
+ isEntryEnabled,
69
+ isModelAllowed,
70
+ parseCredentialRef,
71
+ } from "@skaile/provider-registry";
209
72
 
210
73
  /** Either shape a consumer may be handed. */
211
74
  export type AIProviderRegistryView = AIProviderRegistry | MergedAIProviderRegistry;
@@ -224,9 +87,25 @@ export function isMergedRegistry(
224
87
  return Array.isArray((registry as MergedAIProviderRegistry).order);
225
88
  }
226
89
 
227
- /** `enabled` defaults to `true`. */
228
- export function isEntryEnabled(entry: AIProviderEntry): boolean {
229
- return entry.enabled !== false;
90
+ /**
91
+ * The layered shape of a view. A merged registry is returned as is; a bare
92
+ * entry map becomes one layer in its own iteration order, reported as the
93
+ * `runtime` layer (the host handed it over in memory) with nothing locked.
94
+ * The model constructor resolves against this shape only.
95
+ */
96
+ export function asMergedRegistry(
97
+ registry: AIProviderRegistryView,
98
+ layer: AIProviderLayerId = "runtime",
99
+ ): MergedAIProviderRegistry {
100
+ if (isMergedRegistry(registry)) return registry;
101
+ const order = Object.keys(registry);
102
+ return {
103
+ version: 1,
104
+ providers: registry,
105
+ order,
106
+ origin: Object.fromEntries(order.map((id) => [id, layer])),
107
+ locked: [],
108
+ };
230
109
  }
231
110
 
232
111
  /** Entries in registry order, as `[id, entry]` pairs. Both registry shapes accepted. */
@@ -294,62 +173,9 @@ export function selectVoiceEntryId(ids: readonly string[], wanted?: string): str
294
173
 
295
174
  // ── Credential references ──────────────────────────────────────────────────
296
175
 
297
- /** POSIX environment variable name. */
298
- const ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
299
-
300
- /** The four reference forms. */
176
+ /** The four reference forms, as a credential editor's refusal distinguishes them. */
301
177
  const CREDENTIAL_REF = /^(env|cmd|platform|keyring):([\s\S]+)$/;
302
178
 
303
- /** A parsed `credential.ref`. */
304
- export type AIProviderCredentialRef =
305
- | { form: "env"; name: string }
306
- | { form: "cmd"; command: string }
307
- | { form: "platform"; configId: string }
308
- | { form: "keyring"; service: string; account: string };
309
-
310
- /**
311
- * Parse a `credential.ref`.
312
- *
313
- * Returns `undefined` for anything that is not one of the four reference forms
314
- * — which, in a registry entry or a credential editor, means someone typed a
315
- * literal secret.
316
- */
317
- export function parseCredentialRef(ref: string): AIProviderCredentialRef | undefined {
318
- const match = CREDENTIAL_REF.exec(ref.trim());
319
- if (!match) return undefined;
320
- const body = match[2].trim();
321
- if (body === "") return undefined;
322
- switch (match[1]) {
323
- case "env":
324
- return ENV_NAME.test(body) ? { form: "env", name: body } : undefined;
325
- case "cmd":
326
- return { form: "cmd", command: body };
327
- case "platform":
328
- return { form: "platform", configId: body };
329
- case "keyring": {
330
- const slash = body.indexOf("/");
331
- if (slash <= 0 || slash === body.length - 1) return undefined;
332
- return { form: "keyring", service: body.slice(0, slash), account: body.slice(slash + 1) };
333
- }
334
- default:
335
- return undefined;
336
- }
337
- }
338
-
339
- /** Render a parsed reference back to its `<form>:<body>` string. */
340
- export function formatCredentialRef(ref: AIProviderCredentialRef): string {
341
- switch (ref.form) {
342
- case "env":
343
- return `env:${ref.name}`;
344
- case "cmd":
345
- return `cmd:${ref.command}`;
346
- case "platform":
347
- return `platform:${ref.configId}`;
348
- case "keyring":
349
- return `keyring:${ref.service}/${ref.account}`;
350
- }
351
- }
352
-
353
179
  /** The reference forms, spelled the way a credential editor offers them. */
354
180
  export const CREDENTIAL_REF_FORMS = [
355
181
  "env:NAME",
@@ -7,10 +7,12 @@
7
7
  * `@skaile/forge-settings/nuxt`.
8
8
  */
9
9
 
10
+ export * from "../shared/data-zone";
10
11
  export * from "../shared/env-vars";
11
12
  export * from "../shared/harness-providers";
12
13
  export * from "../shared/harnesses";
13
14
  export * from "../shared/model-roles";
15
+ export * from "../shared/provider-accounts";
14
16
  export * from "../shared/provider-registry";
15
17
  export * from "./model-roles-panel";
16
18
  export * from "./providers-client";
@@ -6,10 +6,18 @@
6
6
  */
7
7
 
8
8
  import type { EnvLayerId } from "../shared/env-vars";
9
+ import type {
10
+ CredentialExpiry,
11
+ ProviderDeploymentRow,
12
+ ProviderSignInId,
13
+ } from "../shared/provider-accounts";
9
14
  import type {
10
15
  AIProviderCredentialKind,
16
+ AIProviderDataZone,
17
+ AIProviderDataZoneRule,
11
18
  AIProviderKind,
12
19
  AIProviderSurface,
20
+ AIProviderTransport,
13
21
  } from "../shared/provider-registry";
14
22
  import type { ProvidersPanelClient } from "./providers-client";
15
23
 
@@ -50,6 +58,17 @@ export interface ProvidersPanelProps {
50
58
  * `layer`; a string replaces it; `false` hides it (Add provider stays).
51
59
  */
52
60
  note?: string | false;
61
+ /**
62
+ * Expert mode: the sign-in step also offers the raw reference form
63
+ * (`env:` / `keyring:` / `cmd:` / `platform:`) and expert-only parameters
64
+ * (`api_version`) become editable. Default `false`.
65
+ */
66
+ expert?: boolean;
67
+ /**
68
+ * The host's data-zone rule ("Where may AI process data?"). Rows outside it
69
+ * show a muted note naming the rule. Absent or `any`: no row is marked.
70
+ */
71
+ dataZoneRule?: AIProviderDataZoneRule;
53
72
  }
54
73
 
55
74
  /** Emits both variants declare. */
@@ -72,12 +91,29 @@ export interface ProvidersPanelEntry {
72
91
  baseUrl?: string;
73
92
  preset?: string;
74
93
  surface?: AIProviderSurface;
94
+ /** How requests reach the surface, when not `direct`. */
95
+ transport?: AIProviderTransport;
96
+ /** Routing parameters (`resource`, `deployment`, `api_version`, …) of an `llm`/`embed` entry. */
97
+ transportParams?: Record<string, string>;
98
+ /**
99
+ * When the credential expires (ISO timestamp), for a credential that
100
+ * reports one. The row then shows "Expires in N days · Renew".
101
+ */
102
+ credentialExpiresAt?: string | null;
75
103
  enabled: boolean;
76
104
  locked: boolean;
77
105
  /** Registry documents holding the id, lowest first: `["instance"]`, `["workspace"]` or both. */
78
106
  layers: ProvidersPanelLayer[];
79
107
  /** The scope's own document's values for the editable fields the dialog does not show. */
80
108
  layerFields?: Record<string, unknown>;
109
+ /** Where the entry processes data. Absent (an older host): the preset's zone, else `transfer`. */
110
+ dataZone?: AIProviderDataZone;
111
+ /** The entry's `eu_processing` switch. */
112
+ euProcessing?: boolean;
113
+ /** The entry's `allowed_models`; absent = every model, `[]` = none. */
114
+ allowedModels?: string[];
115
+ /** Logical model ids the entry declares (`model_map`); absent: its models come from discovery. */
116
+ modelIds?: string[];
81
117
  }
82
118
 
83
119
  /** One catalog preset, as `GET /api/providers/presets` and `.../detected` list it. */
@@ -86,9 +122,15 @@ export interface ProvidersPanelPreset {
86
122
  label: string;
87
123
  kind: AIProviderKind;
88
124
  surface?: AIProviderSurface;
125
+ /** How requests reach the surface; Azure transports offer Entra ID sign-in. */
126
+ transport?: AIProviderTransport;
89
127
  base_url?: string;
90
128
  base_url_env?: string;
91
129
  credential: { kind: AIProviderCredentialKind; env?: string };
130
+ /** Default parameters. */
131
+ params?: Record<string, string>;
132
+ /** `params` keys an entry naming this preset must supply. */
133
+ required_params?: string[];
92
134
  }
93
135
 
94
136
  /** Where a row's entry lives relative to the scope's own document. */
@@ -131,8 +173,25 @@ export interface ProvidersPanelRow extends ProvidersPanelEntry {
131
173
  origin: string;
132
174
  /** `configured · <source>`, `missing <VAR>`, the resolver's reason, or `No credential needed.`. */
133
175
  status: string;
134
- /** Which other layer defines or overrides the credential; see the panel contract. */
135
176
  overrideNotices: string[];
177
+ /** "Microsoft Azure · Entra ID · contoso-ai": family · sign-in kind · resource. */
178
+ accountLine: string;
179
+ /** The credential's expiry, when it reports one. */
180
+ expiry: CredentialExpiry | null;
181
+ /** "Renew" is offered next to the expiry: the credential can be changed here. */
182
+ canRenew: boolean;
183
+ /** The entry's effective data zone. */
184
+ zone: AIProviderDataZone;
185
+ /** The zone chip: EU / EU option / Transfer / Local. */
186
+ zoneLabel: string;
187
+ /** "EU processing is on for this account" is offered: an `eu_option` entry the panel may write. */
188
+ canSetEuProcessing: boolean;
189
+ /** Why the host's data-zone rule keeps the entry out, naming the rule; `null` when it does not. */
190
+ blockedNote: string | null;
191
+ /** The model chip: "All models" | "N models". `null` for a voice entry. */
192
+ modelsChip: string | null;
193
+ /** "Models allowed here" can be changed: an `llm`/`embed` entry the panel may write. */
194
+ canFilterModels: boolean;
136
195
  }
137
196
 
138
197
  /** The add/edit dialog's fields. */
@@ -144,13 +203,46 @@ export interface ProvidersPanelForm {
144
203
  surface: string;
145
204
  /** Empty: the preset's endpoint. */
146
205
  baseUrl: string;
147
- /** Adding only: the `env:` variable the credential references. */
206
+ /** Key sign-in: the `env:` variable the credential references. */
148
207
  envName: string;
149
- /** Adding only, "Set value now": written to the scope's `.env`, never to the provider route. */
208
+ /** Key sign-in, "Set value now": written to the scope's `.env`, never to the provider route. */
150
209
  secret: string;
210
+ /** The chosen "How do you sign in?" option. */
211
+ signIn: ProviderSignInId | "";
212
+ /** `reference` sign-in: the typed reference. */
213
+ credentialRef: string;
214
+ /** Routing parameters by key (`resource`, `deployment`, `api_version`, …). */
215
+ params: Record<string, string>;
151
216
  }
152
217
 
153
- // ── Constants ─────────────────────────────────────────────────────────────────
218
+ /** The Microsoft Azure card's fields. */
219
+ export interface ProvidersPanelAzureForm {
220
+ /** Azure resource name, shared by both entries. */
221
+ resource: string;
222
+ /** Write an Azure OpenAI entry. */
223
+ useOpenAI: boolean;
224
+ /** Write a Claude on Foundry entry. */
225
+ useFoundry: boolean;
226
+ /** Azure OpenAI deployments: deployment name → logical model id (`model_map`). */
227
+ deployments: ProviderDeploymentRow[];
228
+ /** Azure OpenAI `api_version`; prefilled, editable in expert mode. */
229
+ apiVersion: string;
230
+ }
231
+
232
+ /** The add dialog's step: preset library, then the account fields, then the test of what was created. */
233
+ export type ProvidersPanelDialogStep = "library" | "account" | "done";
234
+
235
+ /** "Models allowed here": every model, or only the listed ones. */
236
+ export type ProvidersPanelModelsMode = "all" | "only";
237
+
238
+ /** The "Models allowed here" editor's fields. */
239
+ export interface ProvidersPanelModelsForm {
240
+ mode: ProvidersPanelModelsMode;
241
+ /** The ticked model ids. */
242
+ selected: string[];
243
+ /** The checklist's search text. */
244
+ query: string;
245
+ }
154
246
 
155
247
  /** The preset select's value for an entry that names no preset. */
156
248
  export const CUSTOM_PRESET = "__custom__";
@@ -158,6 +250,9 @@ export const CUSTOM_PRESET = "__custom__";
158
250
  /** Credential select value: open the inline key editor. */
159
251
  export const CREDENTIAL_ENTER_KEY = "__enter_key__";
160
252
 
253
+ /** The preset select's value for the Microsoft Azure account (Azure OpenAI and/or Claude on Foundry). */
254
+ export const AZURE_ACCOUNT = "__azure__";
255
+
161
256
  /** Credential select value: open the inline reference editor. */
162
257
  export const CREDENTIAL_OTHER_REF = "__other_ref__";
163
258
 
@@ -198,6 +293,27 @@ export const ENV_REF_PATTERN = /^env:(.+)$/;
198
293
  /** The credential kind a written reference gets when the row states none. */
199
294
  export const FALLBACK_CREDENTIAL_KIND: AIProviderCredentialKind = "api_key_header";
200
295
 
296
+ /** A row's model chip: "All models" without an allow-list, else its size. */
297
+ export function providerModelsChip(allowedModels: readonly string[] | undefined): string {
298
+ if (allowedModels === undefined) return "All models";
299
+ return allowedModels.length === 1 ? "1 model" : `${allowedModels.length} models`;
300
+ }
301
+
302
+ /**
303
+ * The checklist's ids: the known models (declared or discovered) in their
304
+ * order, then any allowed id neither lists (so it can still be unticked),
305
+ * narrowed by a case-insensitive `query`.
306
+ */
307
+ export function providerModelChoices(
308
+ known: readonly string[],
309
+ allowed: readonly string[],
310
+ query = "",
311
+ ): string[] {
312
+ const ids = [...new Set([...known, ...allowed])];
313
+ const needle = query.trim().toLowerCase();
314
+ return needle ? ids.filter((id) => id.toLowerCase().includes(needle)) : ids;
315
+ }
316
+
201
317
  export const ENV_SOURCE_LABEL: Record<EnvLayerId, string> = {
202
318
  instance: "instance .env",
203
319
  workspace: "workspace .env",
@@ -237,11 +353,13 @@ export function nonEnvSourceLabel(ref: string): string | undefined {
237
353
  return undefined;
238
354
  }
239
355
 
240
- export const EMPTY_FORM: Readonly<ProvidersPanelForm> = {
356
+ export const EMPTY_FORM: Readonly<Omit<ProvidersPanelForm, "params">> = {
241
357
  preset: "",
242
358
  id: "",
243
359
  surface: "",
244
360
  baseUrl: "",
245
361
  envName: "",
246
362
  secret: "",
363
+ signIn: "",
364
+ credentialRef: "",
247
365
  };