@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,33 +1,43 @@
1
1
  /**
2
2
  * `@skaile/forge-settings/ai-sdk` — the AI SDK language model of a registry
3
- * selector, resolved by `@skaile/workspaces/ai-sdk` and gated by the host's
4
- * credential policy.
3
+ * selector, built by `@skaile/provider-registry/ai-sdk` against one registry
4
+ * snapshot the host passes, with the credential resolved under the host's
5
+ * policy.
5
6
  *
6
- * Workspaces owns endpoint, header and credential wiring for every surface;
7
- * this module only decides *whether* an entry may be resolved and against
8
- * *which* environment. It is not a service locator: the caller passes the
9
- * selector (role-to-selector mapping belongs to the host's settings document)
10
- * and the environment explicitly.
7
+ * The shared constructor owns endpoint, header and SDK wiring for every
8
+ * surface it supports; this module decides *whether* an entry may be resolved
9
+ * and supplies its credential — through the same policy-checked resolver model
10
+ * discovery uses, so a model that lists is a model that calls with the same
11
+ * key. It is not a service locator: the caller passes the selector
12
+ * (role-to-selector mapping belongs to the host's settings document), the
13
+ * registry and the environment explicitly. Nothing is read from disk here;
14
+ * a Skaile Workspaces host that resolves against a project's registry uses
15
+ * `resolveWorkspaceLanguageModel` from `@skaile/forge-settings/workspaces`.
11
16
  *
12
17
  * Provider SDKs (`@ai-sdk/anthropic`, `@ai-sdk/openai`,
13
- * `@ai-sdk/openai-compatible`) are optional peers of Workspaces: install the
14
- * one the selected surface needs.
18
+ * `@ai-sdk/openai-compatible`) are optional peers: install the one the
19
+ * selected surface needs.
15
20
  */
16
21
 
17
- import { type MergedAIProviderRegistry, parseModelRef } from "@skaile/workspaces/core";
18
- import { type ResolvedLanguageModel, resolveLanguageModel } from "@skaile/workspaces/ai-sdk";
19
- import { loadMergedProviderRegistry } from "@skaile/workspaces/sdk/bridge";
22
+ import { type MergedAIProviderRegistry, parseModelRef } from "@skaile/provider-registry";
23
+ import {
24
+ type EntryCredentialResolver,
25
+ type ResolvedLanguageModel,
26
+ resolveLanguageModel,
27
+ } from "@skaile/provider-registry/ai-sdk";
28
+ import { assertEntryAllowed, ModelResolutionError } from "../model/errors";
29
+ import { credentialStoreOptions, selectorProviderId } from "../model/selector";
20
30
  import type { CredentialPolicy } from "../server/credential-policy";
21
- import { ModelResolutionError, assertEntryAllowed } from "../model/errors";
31
+ import { resolveEntryCredential } from "../server/credential-store";
22
32
 
23
- export { ModelResolutionError } from "../model/errors";
24
33
  export type { ModelResolutionCode } from "../model/errors";
34
+ export { ModelResolutionError } from "../model/errors";
25
35
  export type { ResolvedLanguageModel };
26
36
 
27
37
  /** Options for {@link resolveRegistryLanguageModel}. */
28
38
  export interface ResolveRegistryLanguageModelOptions {
29
- /** Workspace whose `.skaile/providers.yml` is the workspace layer. */
30
- projectDir: string;
39
+ /** The one merged registry snapshot the selector resolves against. */
40
+ registry: MergedAIProviderRegistry;
31
41
  /**
32
42
  * The environment `env:` credential references are read from. Required, so a
33
43
  * host never reads the ambient process by accident.
@@ -40,48 +50,91 @@ export interface ResolveRegistryLanguageModelOptions {
40
50
  * before anything is read; a refused one is a `policy_refused` failure.
41
51
  */
42
52
  policy?: CredentialPolicy;
43
- /**
44
- * An already merged (e.g. transform-proxy-routed) registry. When set,
45
- * `projectDir` is not read for providers, the policy check runs on this
46
- * registry, and it is handed to Workspaces as the registry to resolve against.
47
- */
48
- registry?: MergedAIProviderRegistry;
53
+ /** Local encrypted store for `keyring:` references; else `FORGE_CREDENTIAL_STORE` of `env`. */
54
+ path?: string;
55
+ /** 64-char hex key of the local store; else `FORGE_CREDENTIAL_KEY` of `env`. */
56
+ key?: string;
57
+ }
58
+
59
+ /**
60
+ * The credential resolver discovery and invocation share: the policy refuses
61
+ * an entry before any reference is read, then the reference resolves from
62
+ * `env` or the local store. Secret-free errors only.
63
+ */
64
+ function policyCredentialResolver(
65
+ options: Pick<ResolveRegistryLanguageModelOptions, "env" | "policy" | "path" | "key">,
66
+ ): EntryCredentialResolver {
67
+ return (entry, providerId) => {
68
+ assertEntryAllowed(entry, providerId, options.policy);
69
+ const resolved = resolveEntryCredential(entry, credentialStoreOptions(options.env, options));
70
+ return resolved.error && !resolved.value
71
+ ? { error: resolved.error }
72
+ : { ...(resolved.value !== undefined ? { value: resolved.value } : {}) };
73
+ };
49
74
  }
50
75
 
51
76
  /**
52
77
  * The AI SDK model for `selector` (`<providerId>/<modelId>`, a bare model id or
53
- * `@role`), resolved by Workspaces against the registry on disk, or the given
54
- * `registry`.
78
+ * `@role`), resolved against `options.registry` only.
55
79
  *
56
- * @throws {ModelResolutionError} `registry_invalid` (only when read from disk) ·
57
- * `unknown_selector` · `policy_refused` · `unresolved` (credential, endpoint or
58
- * provider SDK; the message is fixed and never carries Workspaces' text, which
59
- * stays on `cause`).
80
+ * Strict: a selector naming a provider that is missing from the registry's
81
+ * enabled `llm` entries (and that no entry maps as a model id), disabled, or an
82
+ * unassigned `@role` fails — nothing is rerouted to another endpoint and
83
+ * nothing is sent. A bare model id without `/` resolves as the registry orders it.
84
+ *
85
+ * @throws {ModelResolutionError} `unknown_selector` (no enabled entry serves the
86
+ * selector, or the named entry is disabled / not an `llm` entry) ·
87
+ * `policy_refused` · `unresolved` (credential, endpoint or provider SDK; the
88
+ * message is fixed, the underlying error stays on `cause`).
60
89
  */
61
90
  export async function resolveRegistryLanguageModel(
62
91
  selector: string,
63
92
  options: ResolveRegistryLanguageModelOptions,
64
93
  ): Promise<ResolvedLanguageModel> {
65
- const merged = options.registry ?? loadRegistry(options.projectDir);
94
+ const merged = options.registry;
95
+
96
+ const head = selectorProviderId(selector);
97
+ const named = head === undefined ? undefined : merged.providers[head];
98
+ if (
99
+ head !== undefined &&
100
+ named !== undefined &&
101
+ (named.kind !== "llm" || named.enabled === false)
102
+ ) {
103
+ throw new ModelResolutionError(
104
+ "unknown_selector",
105
+ `provider '${head}' is ${named.kind !== "llm" ? "not an llm entry" : "disabled"}`,
106
+ { providerId: head },
107
+ );
108
+ }
66
109
 
67
110
  const ref = parseModelRef(selector, merged, options.roles);
68
- if (!ref) {
111
+ // `<head>/<model>` that no entry is named by or maps: the general resolver would
112
+ // hand it to the first entry in registry order, i.e. send it (and that entry's
113
+ // credential) to an endpoint the caller did not name.
114
+ if (!ref || (head !== undefined && named === undefined && ref.fallback === true)) {
69
115
  throw new ModelResolutionError("unknown_selector", `no provider serves '${selector}'`);
70
116
  }
117
+ if (ref.role !== undefined && !options.roles?.[ref.role]) {
118
+ throw new ModelResolutionError(
119
+ "unknown_selector",
120
+ `role '@${ref.role}' is not assigned to a model`,
121
+ );
122
+ }
71
123
  const entry = merged.providers[ref.providerId];
72
124
  if (entry) assertEntryAllowed(entry, ref.providerId, options.policy);
73
125
 
74
126
  try {
75
127
  return await resolveLanguageModel(selector, {
76
- projectDir: options.projectDir,
128
+ registry: merged,
77
129
  env: options.env,
78
130
  ...(options.roles ? { roles: options.roles } : {}),
79
- ...(options.registry ? { registry: options.registry } : {}),
131
+ resolveCredential: policyCredentialResolver(options),
80
132
  });
81
133
  } catch (cause) {
82
- // Workspaces' message is not ours to vouch for: it can echo a resolved
83
- // credential or an endpoint with a key in its query. The message is fixed;
84
- // the original error stays on `cause` for a host that logs it server-side.
134
+ if (cause instanceof ModelResolutionError) throw cause;
135
+ // The constructor's message is not ours to vouch for: an SDK error can echo
136
+ // an endpoint with a key in its query. The message is fixed; the original
137
+ // error stays on `cause` for a host that logs it server-side.
85
138
  throw new ModelResolutionError(
86
139
  "unresolved",
87
140
  `provider '${ref.providerId}': credential, endpoint or provider SDK could not be resolved`,
@@ -89,15 +142,3 @@ export async function resolveRegistryLanguageModel(
89
142
  );
90
143
  }
91
144
  }
92
-
93
- /** The merged registry of `projectDir`; `registry_invalid` on any error diagnostic. */
94
- function loadRegistry(projectDir: string): MergedAIProviderRegistry {
95
- const { merged, diagnostics } = loadMergedProviderRegistry({ projectDir });
96
- const errors = diagnostics
97
- .filter((d) => d.severity === "error")
98
- .map((d) => (d.path ? `${d.path}: ${d.message}` : d.message));
99
- if (errors.length > 0) {
100
- throw new ModelResolutionError("registry_invalid", `providers registry: ${errors.join("; ")}`);
101
- }
102
- return merged;
103
- }