@velum-labs/routekit-daemon 1.3.2 → 1.5.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 (172) hide show
  1. package/dist/account-enroll-service.d.ts +1 -3
  2. package/dist/account-enroll-service.js +262 -262
  3. package/dist/account-mutation-service.d.ts +1 -3
  4. package/dist/account-mutation-service.js +302 -283
  5. package/dist/account-query-service.d.ts +1 -3
  6. package/dist/account-query-service.js +88 -86
  7. package/dist/call-attribution-store.js +544 -1
  8. package/dist/control-dispatch.js +9 -15
  9. package/dist/daemon-bootstrap-preflight.js +1 -1
  10. package/dist/daemon-control-handlers.d.ts +1 -1
  11. package/dist/daemon-control-handlers.js +24 -27
  12. package/dist/daemon-generations.d.ts +14 -28
  13. package/dist/daemon-generations.js +114 -53
  14. package/dist/daemon-lifecycle-service.d.ts +1 -3
  15. package/dist/daemon-lifecycle-service.js +88 -94
  16. package/dist/daemon-maintenance.d.ts +27 -49
  17. package/dist/daemon-maintenance.js +5 -1
  18. package/dist/daemon-options.d.ts +3 -2
  19. package/dist/daemon-policy-context.d.ts +1 -0
  20. package/dist/daemon-policy-context.js +1 -0
  21. package/dist/daemon-runtime-state.d.ts +1 -1
  22. package/dist/daemon-runtime-state.js +2 -3
  23. package/dist/daemon-state-context.d.ts +1 -1
  24. package/dist/daemon-state-context.js +1 -38
  25. package/dist/doctor-service.d.ts +1 -3
  26. package/dist/doctor-service.js +87 -91
  27. package/dist/effect/daemon-live.d.ts +7 -6
  28. package/dist/effect/daemon-live.js +57 -135
  29. package/dist/eval-routing-service.d.ts +1 -3
  30. package/dist/eval-routing-service.js +45 -69
  31. package/dist/eval-session-execution-store.d.ts +24 -0
  32. package/dist/eval-session-execution-store.js +253 -0
  33. package/dist/host-protocol.d.ts +1 -0
  34. package/dist/host.js +3 -0
  35. package/dist/internal-providers/provider-anthropic/index.d.ts +23 -0
  36. package/dist/internal-providers/provider-anthropic/index.js +42 -0
  37. package/dist/internal-providers/provider-anthropic/test/provider.test.js +33 -0
  38. package/dist/internal-providers/provider-azure/index.d.ts +25 -0
  39. package/dist/internal-providers/provider-azure/index.js +101 -0
  40. package/dist/internal-providers/provider-azure/test/provider.test.js +83 -0
  41. package/dist/internal-providers/provider-bedrock/codec.d.ts +11 -0
  42. package/dist/internal-providers/provider-bedrock/codec.js +484 -0
  43. package/dist/internal-providers/provider-bedrock/connection-settings.d.ts +6 -0
  44. package/dist/internal-providers/provider-bedrock/connection-settings.js +41 -0
  45. package/dist/internal-providers/provider-bedrock/connection.d.ts +17 -0
  46. package/dist/internal-providers/provider-bedrock/connection.js +143 -0
  47. package/dist/internal-providers/provider-bedrock/index.d.ts +27 -0
  48. package/dist/internal-providers/provider-bedrock/index.js +47 -0
  49. package/dist/internal-providers/provider-bedrock/source.d.ts +39 -0
  50. package/dist/internal-providers/provider-bedrock/source.js +454 -0
  51. package/dist/internal-providers/provider-bedrock/test/bedrock-source.test.d.ts +1 -0
  52. package/dist/internal-providers/provider-bedrock/test/bedrock-source.test.js +786 -0
  53. package/dist/internal-providers/provider-bedrock/test/connection.test.d.ts +1 -0
  54. package/dist/internal-providers/provider-bedrock/test/connection.test.js +249 -0
  55. package/dist/internal-providers/provider-bedrock/test/workload-credentials.test.d.ts +1 -0
  56. package/dist/internal-providers/provider-bedrock/test/workload-credentials.test.js +29 -0
  57. package/dist/internal-providers/provider-bedrock/workload-credentials.d.ts +5 -0
  58. package/dist/internal-providers/provider-bedrock/workload-credentials.js +14 -0
  59. package/dist/internal-providers/provider-claude-code/index.d.ts +6 -0
  60. package/dist/internal-providers/provider-claude-code/index.js +4 -0
  61. package/dist/internal-providers/provider-claude-code/module-api.d.ts +25 -0
  62. package/dist/internal-providers/provider-claude-code/module-api.js +31 -0
  63. package/dist/internal-providers/provider-claude-code/relay.d.ts +20 -0
  64. package/dist/internal-providers/provider-claude-code/relay.js +115 -0
  65. package/dist/internal-providers/provider-claude-code/subscription.d.ts +2 -0
  66. package/dist/internal-providers/provider-claude-code/subscription.js +194 -0
  67. package/dist/internal-providers/provider-claude-code/test/http-submission.test.d.ts +1 -0
  68. package/dist/internal-providers/provider-claude-code/test/http-submission.test.js +140 -0
  69. package/dist/internal-providers/provider-claude-code/test/relay.test.d.ts +1 -0
  70. package/dist/internal-providers/provider-claude-code/test/relay.test.js +379 -0
  71. package/dist/internal-providers/provider-claude-code/test/subscription.test.d.ts +1 -0
  72. package/dist/internal-providers/provider-claude-code/test/subscription.test.js +256 -0
  73. package/dist/internal-providers/provider-cliproxy/index.d.ts +9 -0
  74. package/dist/internal-providers/provider-cliproxy/index.js +15 -0
  75. package/dist/internal-providers/provider-codex/index.d.ts +6 -0
  76. package/dist/internal-providers/provider-codex/index.js +7 -0
  77. package/dist/internal-providers/provider-codex/module-api.d.ts +25 -0
  78. package/dist/internal-providers/provider-codex/module-api.js +31 -0
  79. package/dist/internal-providers/provider-codex/relay.d.ts +123 -0
  80. package/dist/internal-providers/provider-codex/relay.js +261 -0
  81. package/dist/internal-providers/provider-codex/subscription.d.ts +2 -0
  82. package/dist/internal-providers/provider-codex/subscription.js +555 -0
  83. package/dist/internal-providers/provider-codex/test/http-submission.test.d.ts +1 -0
  84. package/dist/internal-providers/provider-codex/test/http-submission.test.js +142 -0
  85. package/dist/internal-providers/provider-codex/test/relay.test.d.ts +1 -0
  86. package/dist/internal-providers/provider-codex/test/relay.test.js +600 -0
  87. package/dist/internal-providers/provider-codex/test/subscription.test.d.ts +1 -0
  88. package/dist/internal-providers/provider-codex/test/subscription.test.js +70 -0
  89. package/dist/internal-providers/provider-codex/test/translated-content.test.d.ts +1 -0
  90. package/dist/internal-providers/provider-codex/test/translated-content.test.js +91 -0
  91. package/dist/internal-providers/provider-custom/index.d.ts +9 -0
  92. package/dist/internal-providers/provider-custom/index.js +15 -0
  93. package/dist/internal-providers/provider-google/index.d.ts +23 -0
  94. package/dist/internal-providers/provider-google/index.js +43 -0
  95. package/dist/internal-providers/provider-google/test/provider.test.d.ts +1 -0
  96. package/dist/internal-providers/provider-google/test/provider.test.js +34 -0
  97. package/dist/internal-providers/provider-google-vertex/index.d.ts +28 -0
  98. package/dist/internal-providers/provider-google-vertex/index.js +137 -0
  99. package/dist/internal-providers/provider-google-vertex/test/provider.test.d.ts +1 -0
  100. package/dist/internal-providers/provider-google-vertex/test/provider.test.js +85 -0
  101. package/dist/internal-providers/provider-openai/documented-reasoning.d.ts +9 -0
  102. package/dist/internal-providers/provider-openai/documented-reasoning.js +26 -0
  103. package/dist/internal-providers/provider-openai/index.d.ts +23 -0
  104. package/dist/internal-providers/provider-openai/index.js +74 -0
  105. package/dist/internal-providers/provider-openai/test/provider.test.d.ts +1 -0
  106. package/dist/internal-providers/provider-openai/test/provider.test.js +268 -0
  107. package/dist/internal-providers/provider-openai-compatible/index.d.ts +9 -0
  108. package/dist/internal-providers/provider-openai-compatible/index.js +15 -0
  109. package/dist/internal-providers/provider-openai-compatible/test/provider.test.d.ts +1 -0
  110. package/dist/internal-providers/provider-openai-compatible/test/provider.test.js +40 -0
  111. package/dist/internal-providers/provider-openrouter/connection.d.ts +5 -0
  112. package/dist/internal-providers/provider-openrouter/connection.js +143 -0
  113. package/dist/internal-providers/provider-openrouter/index.d.ts +28 -0
  114. package/dist/internal-providers/provider-openrouter/index.js +42 -0
  115. package/dist/internal-providers/provider-openrouter/pricing.d.ts +4 -0
  116. package/dist/internal-providers/provider-openrouter/pricing.js +108 -0
  117. package/dist/internal-providers/provider-openrouter/test/connection.test.d.ts +1 -0
  118. package/dist/internal-providers/provider-openrouter/test/connection.test.js +71 -0
  119. package/dist/internal-providers/provider-openrouter/test/pricing.test.d.ts +1 -0
  120. package/dist/internal-providers/provider-openrouter/test/pricing.test.js +45 -0
  121. package/dist/internal-providers/provider-openrouter/test/provider.test.d.ts +1 -0
  122. package/dist/internal-providers/provider-openrouter/test/provider.test.js +141 -0
  123. package/dist/internal-providers/provider-openrouter/test/validation.test.d.ts +1 -0
  124. package/dist/internal-providers/provider-openrouter/test/validation.test.js +225 -0
  125. package/dist/launcher-service.d.ts +3 -7
  126. package/dist/launcher-service.js +144 -133
  127. package/dist/provider-modules.d.ts +6 -0
  128. package/dist/provider-modules.js +55 -0
  129. package/dist/provider-query-service.d.ts +1 -3
  130. package/dist/provider-query-service.js +140 -143
  131. package/dist/router-generation-service.d.ts +1 -3
  132. package/dist/router-generation-service.js +83 -78
  133. package/dist/services/eval-session/service.d.ts +20 -8
  134. package/dist/services/eval-session/service.js +410 -40
  135. package/dist/services/gateway-generation/service.d.ts +7 -3
  136. package/dist/services/gateway-generation/service.js +126 -63
  137. package/dist/services/generations/service.d.ts +3 -3
  138. package/dist/services/generations/service.js +5 -1
  139. package/dist/telemetry-application-service.d.ts +1 -3
  140. package/dist/telemetry-application-service.js +41 -45
  141. package/dist/test/call-attribution-store.test.js +186 -0
  142. package/dist/test/daemon-accounts.test.js +2 -2
  143. package/dist/test/daemon-data-plane.test.js +24 -45
  144. package/dist/test/daemon-fixtures.js +1 -1
  145. package/dist/test/daemon-generation-transaction.test.js +66 -2
  146. package/dist/test/daemon-runtime-state.test.js +28 -1
  147. package/dist/test/effect-daemon.test.js +38 -0
  148. package/dist/test/eval-routing-application-service.test.js +6 -6
  149. package/dist/test/eval-session-execution.test.d.ts +1 -0
  150. package/dist/test/eval-session-execution.test.js +356 -0
  151. package/dist/test/eval-session-service.test.js +70 -1
  152. package/dist/test/host-worker-session.test.js +1 -0
  153. package/dist/test/launcher-session.test.d.ts +1 -0
  154. package/dist/test/launcher-session.test.js +87 -0
  155. package/dist/test/leaderboard.test.js +25 -1
  156. package/dist/test/provider-modules.test.d.ts +1 -0
  157. package/dist/test/provider-modules.test.js +65 -0
  158. package/dist/test/routekit-upstream-sources.test.d.ts +1 -0
  159. package/dist/test/routekit-upstream-sources.test.js +40 -0
  160. package/dist/test/runtime-composition-types.test.js +11 -0
  161. package/dist/token-application-service.d.ts +1 -3
  162. package/dist/token-application-service.js +38 -44
  163. package/dist/worker.js +5 -3
  164. package/package.json +35 -11
  165. package/dist/application-services.d.ts +0 -10
  166. package/dist/application-services.js +0 -22
  167. package/dist/daemon-lifecycle.d.ts +0 -54
  168. package/dist/daemon-lifecycle.js +0 -177
  169. package/dist/test/application-services.test.js +0 -135
  170. package/dist/test/daemon-lifecycle.test.js +0 -182
  171. /package/dist/{test/application-services.test.d.ts → internal-providers/provider-anthropic/test/provider.test.d.ts} +0 -0
  172. /package/dist/{test/daemon-lifecycle.test.d.ts → internal-providers/provider-azure/test/provider.test.d.ts} +0 -0
@@ -6,144 +6,155 @@ import { Effect } from "effect";
6
6
  import { ActiveGateway } from "./services/active-gateway/service.js";
7
7
  import { LauncherSessions } from "./services/launcher-session/service.js";
8
8
  import { Tokens } from "./services/tokens/service.js";
9
- /** Owns coding-tool launch preparation against the live catalog. */
10
- export class LauncherApplicationService {
11
- options;
12
- constructor(options) {
13
- this.options = options;
14
- }
15
- handlers() {
16
- const options = this.options;
17
- return {
18
- "launcher.prepare": (params, context) => Effect.gen(function* () {
19
- const listed = yield* options.listModels({}, {
20
- signal: context.signal,
21
- requestId: "internal"
22
- });
23
- return yield* Effect.gen(function* () {
24
- const requestedModel = params.model?.trim();
25
- const modelProfile = requestedModel?.toLowerCase().startsWith("auto:")
26
- ? requestedModel.slice("auto:".length)
27
- : undefined;
28
- const routingProfile = params.routingProfile ?? modelProfile;
29
- if (params.routingProfile !== undefined && modelProfile !== undefined) {
30
- if (params.routingProfile !== modelProfile) {
31
- return yield* Effect.fail(new ControlError({
32
- code: "bad_request",
33
- message: "routingProfile conflicts with the model profile selector"
34
- }));
35
- }
36
- }
37
- else if (params.routingProfile !== undefined &&
38
- requestedModel !== undefined &&
39
- requestedModel.toLowerCase() !== "auto") {
9
+ /** Bind coding-tool launch preparation to the live catalog's handler. */
10
+ export function launcherHandlers({ listModels }) {
11
+ return {
12
+ "launcher.prepare": (params, context) => Effect.gen(function* () {
13
+ const listed = yield* listModels({}, {
14
+ signal: context.signal,
15
+ requestId: "internal"
16
+ });
17
+ return yield* Effect.gen(function* () {
18
+ const requestedModel = params.model?.trim();
19
+ const modelProfile = requestedModel?.toLowerCase().startsWith("auto:")
20
+ ? requestedModel.slice("auto:".length)
21
+ : undefined;
22
+ const routingProfile = params.routingProfile ?? modelProfile;
23
+ if (params.routingProfile !== undefined && modelProfile !== undefined) {
24
+ if (params.routingProfile !== modelProfile) {
40
25
  return yield* Effect.fail(new ControlError({
41
26
  code: "bad_request",
42
- message: "routingProfile may be used only with model auto or an omitted model"
43
- }));
44
- }
45
- let launcherAuthToken;
46
- if (routingProfile !== undefined) {
47
- const cwd = resolve(params.cwd ?? ".");
48
- const repositoryRoot = yield* Effect.try({
49
- try: () => {
50
- const root = findRepositoryRoutingProfileRoot(cwd, routingProfile);
51
- if (root === undefined) {
52
- throw new Error(`routing profile ${JSON.stringify(routingProfile)} was not found from ${cwd}`);
53
- }
54
- loadRepositoryRoutingProfile({ repositoryRoot: root, name: routingProfile });
55
- return root;
56
- },
57
- catch: (cause) => new ControlError({
58
- code: "not_found",
59
- message: cause instanceof Error
60
- ? cause.message
61
- : `routing profile ${JSON.stringify(routingProfile)} was not found`
62
- })
63
- });
64
- const launcherSessions = yield* LauncherSessions;
65
- launcherAuthToken = launcherSessions.open({
66
- repositoryRoot,
67
- profileName: routingProfile,
68
- role: context.principal === undefined || context.principal.role === "ephemeral"
69
- ? "owner"
70
- : context.principal.role
71
- });
72
- }
73
- let model = routingProfile === undefined
74
- ? (params.model ?? listed.defaultModel ?? listed.models[0]?.id)
75
- : `auto:${routingProfile}`;
76
- let codexSelection;
77
- if (params.tool === "codex" && routingProfile === undefined) {
78
- const gateway = yield* ActiveGateway;
79
- const candidates = listed.models.flatMap((entry) => {
80
- const info = gateway.router().modelInfo(entry.id);
81
- if (info === undefined)
82
- return [];
83
- return [
84
- {
85
- id: info.id,
86
- nativeId: info.nativeModel,
87
- provider: info.provider,
88
- billingScope: info.billingMode,
89
- ...(info.createdAt !== undefined ? { createdAt: info.createdAt } : {}),
90
- ...(info.providerPriority !== undefined
91
- ? { providerPriority: info.providerPriority }
92
- : {}),
93
- ...(info.metadata?.architecture !== undefined
94
- ? { architecture: info.metadata.architecture }
95
- : {}),
96
- ...(info.metadata?.supportedParameters !== undefined
97
- ? { supportedParameters: info.metadata.supportedParameters }
98
- : {}),
99
- ...(info.reasoning !== null ? { reasoning: info.reasoning } : {})
100
- }
101
- ];
102
- });
103
- const selected = yield* resolveCodexStartupModel({
104
- models: candidates,
105
- ...(listed.defaultModel !== undefined
106
- ? { preferredModel: listed.defaultModel }
107
- : {}),
108
- ...(params.model !== undefined ? { requestedModel: params.model } : {}),
109
- signal: context.signal
110
- }).pipe(Effect.mapError((error) => {
111
- const message = error instanceof Error ? error.message : String(error);
112
- return new ControlError({
113
- code: params.model !== undefined && message.startsWith("unknown model")
114
- ? "not_found"
115
- : "unavailable",
116
- message
117
- });
27
+ message: "routingProfile conflicts with the model profile selector"
118
28
  }));
119
- model = selected.model;
120
- codexSelection = {
121
- compatibleModelIds: [...selected.compatibleModelIds],
122
- models: [...selected.models]
123
- };
124
29
  }
125
- if (model === undefined ||
126
- (routingProfile === undefined && !listed.models.some((entry) => entry.id === model))) {
127
- return yield* Effect.fail(new ControlError({
30
+ }
31
+ else if (params.routingProfile !== undefined &&
32
+ requestedModel !== undefined &&
33
+ requestedModel.toLowerCase() !== "auto") {
34
+ return yield* Effect.fail(new ControlError({
35
+ code: "bad_request",
36
+ message: "routingProfile may be used only with model auto or an omitted model"
37
+ }));
38
+ }
39
+ let routingContext;
40
+ if (routingProfile !== undefined) {
41
+ const cwd = resolve(params.cwd ?? ".");
42
+ const repositoryRoot = yield* Effect.try({
43
+ try: () => {
44
+ const root = findRepositoryRoutingProfileRoot(cwd, routingProfile);
45
+ if (root === undefined) {
46
+ throw new Error(`routing profile ${JSON.stringify(routingProfile)} was not found from ${cwd}`);
47
+ }
48
+ loadRepositoryRoutingProfile({ repositoryRoot: root, name: routingProfile });
49
+ return root;
50
+ },
51
+ catch: (cause) => new ControlError({
128
52
  code: "not_found",
129
- message: params.model === undefined
130
- ? "no model is available"
131
- : `unknown model: ${params.model}`
132
- }));
133
- }
53
+ message: cause instanceof Error
54
+ ? cause.message
55
+ : `routing profile ${JSON.stringify(routingProfile)} was not found`
56
+ })
57
+ });
58
+ routingContext = { repositoryRoot, profileName: routingProfile };
59
+ }
60
+ let model = routingProfile === undefined
61
+ ? (params.model ?? listed.defaultModel ?? listed.models[0]?.id)
62
+ : `auto:${routingProfile}`;
63
+ let codexSelection;
64
+ if (params.tool === "codex" && routingProfile === undefined) {
134
65
  const gateway = yield* ActiveGateway;
135
- const tokens = yield* Tokens;
136
- const authToken = launcherAuthToken ?? (yield* tokens.dataTokenForPrincipal(context.principal));
137
- return {
138
- tool: params.tool,
139
- model,
140
- gatewayUrl: gateway.dataUrl() ?? "",
141
- authToken,
142
- env: {},
143
- ...(codexSelection !== undefined ? { codexSelection } : {})
66
+ const candidates = listed.models.flatMap((entry) => {
67
+ const info = gateway.router().modelInfo(entry.id);
68
+ if (info === undefined)
69
+ return [];
70
+ const candidate = {
71
+ id: info.id,
72
+ nativeId: info.nativeModel,
73
+ provider: info.provider,
74
+ billingScope: info.billingMode
75
+ };
76
+ if (info.createdAt !== undefined)
77
+ candidate.createdAt = info.createdAt;
78
+ if (info.providerPriority !== undefined) {
79
+ candidate.providerPriority = info.providerPriority;
80
+ }
81
+ if (info.metadata?.architecture !== undefined) {
82
+ candidate.architecture = info.metadata.architecture;
83
+ }
84
+ if (info.metadata?.supportedParameters !== undefined) {
85
+ candidate.supportedParameters = info.metadata.supportedParameters;
86
+ }
87
+ const capabilities = {};
88
+ for (const name of ["responses", "tools"]) {
89
+ const status = info.capabilities[name];
90
+ if (status === "supported" ||
91
+ status === "unsupported" ||
92
+ status === "degraded" ||
93
+ status === "unknown") {
94
+ capabilities[name] = status;
95
+ }
96
+ }
97
+ if (Object.keys(capabilities).length > 0) {
98
+ candidate.capabilities = capabilities;
99
+ }
100
+ if (info.reasoning !== null)
101
+ candidate.reasoning = info.reasoning;
102
+ return [candidate];
103
+ });
104
+ const selected = yield* resolveCodexStartupModel({
105
+ models: candidates,
106
+ ...(listed.defaultModel !== undefined ? { preferredModel: listed.defaultModel } : {}),
107
+ ...(params.model !== undefined ? { requestedModel: params.model } : {}),
108
+ signal: context.signal
109
+ }).pipe(Effect.mapError((error) => {
110
+ const message = error instanceof Error ? error.message : String(error);
111
+ return new ControlError({
112
+ code: params.model !== undefined && message.startsWith("unknown model")
113
+ ? "not_found"
114
+ : "unavailable",
115
+ message
116
+ });
117
+ }));
118
+ model = selected.model;
119
+ codexSelection = {
120
+ compatibleModelIds: [...selected.compatibleModelIds],
121
+ models: [...selected.models]
144
122
  };
145
- });
146
- })
147
- };
148
- }
123
+ }
124
+ if (model === undefined ||
125
+ (routingProfile === undefined && !listed.models.some((entry) => entry.id === model))) {
126
+ return yield* Effect.fail(new ControlError({
127
+ code: "not_found",
128
+ message: params.model === undefined
129
+ ? "no model is available"
130
+ : `unknown model: ${params.model}`
131
+ }));
132
+ }
133
+ const gateway = yield* ActiveGateway;
134
+ const tokens = yield* Tokens;
135
+ const gatewayUrl = gateway.dataUrl() ?? "";
136
+ let authToken;
137
+ if (routingContext === undefined) {
138
+ authToken = yield* tokens.dataTokenForPrincipal(context.principal);
139
+ }
140
+ else {
141
+ const launcherSessions = yield* LauncherSessions;
142
+ authToken = launcherSessions.open({
143
+ ...routingContext,
144
+ role: context.principal === undefined || context.principal.role === "ephemeral"
145
+ ? "owner"
146
+ : context.principal.role
147
+ });
148
+ }
149
+ return {
150
+ tool: params.tool,
151
+ model,
152
+ gatewayUrl,
153
+ authToken,
154
+ env: {},
155
+ ...(codexSelection !== undefined ? { codexSelection } : {})
156
+ };
157
+ });
158
+ })
159
+ };
149
160
  }
@@ -0,0 +1,6 @@
1
+ import { ProviderModuleRegistry } from "@velum-labs/routekit-provider-core";
2
+ export declare const providerModules: ProviderModuleRegistry;
3
+ export declare function validateProviderModules(registry: ProviderModuleRegistry): ProviderModuleRegistry;
4
+ export declare function currentProviderModules(): ProviderModuleRegistry;
5
+ /** Install an alternate registry for one in-process test and return its restore operation. */
6
+ export declare function installProviderModulesForTest(registry: ProviderModuleRegistry): () => void;
@@ -0,0 +1,55 @@
1
+ import { anthropic } from "./internal-providers/provider-anthropic/index.js";
2
+ import { azure } from "./internal-providers/provider-azure/index.js";
3
+ import { bedrock } from "./internal-providers/provider-bedrock/index.js";
4
+ import { claudeCode } from "./internal-providers/provider-claude-code/index.js";
5
+ import { cliproxy } from "./internal-providers/provider-cliproxy/index.js";
6
+ import { codex } from "./internal-providers/provider-codex/index.js";
7
+ import { ProviderModuleRegistry } from "@velum-labs/routekit-provider-core";
8
+ import { custom } from "./internal-providers/provider-custom/index.js";
9
+ import { google } from "./internal-providers/provider-google/index.js";
10
+ import { googleVertex } from "./internal-providers/provider-google-vertex/index.js";
11
+ import { openai } from "./internal-providers/provider-openai/index.js";
12
+ import { openaiCompatible } from "./internal-providers/provider-openai-compatible/index.js";
13
+ import { openrouter } from "./internal-providers/provider-openrouter/index.js";
14
+ import { providerModuleInfo } from "@velum-labs/routekit-registry";
15
+ export const providerModules = new ProviderModuleRegistry([
16
+ openai,
17
+ anthropic,
18
+ bedrock,
19
+ google,
20
+ openrouter,
21
+ cliproxy,
22
+ claudeCode,
23
+ codex,
24
+ openaiCompatible,
25
+ custom,
26
+ azure,
27
+ googleVertex
28
+ ]);
29
+ export function validateProviderModules(registry) {
30
+ for (const module of registry.list()) {
31
+ const catalog = providerModuleInfo(module.id);
32
+ if (catalog === undefined) {
33
+ throw new Error(`provider module "${module.id}" is missing its catalog row`);
34
+ }
35
+ if (catalog.protocol !== module.protocol ||
36
+ catalog.accountClass !== module.accountClass ||
37
+ catalog.billingMode !== module.billingMode) {
38
+ throw new Error(`provider module "${module.id}" disagrees with its catalog row`);
39
+ }
40
+ }
41
+ return registry;
42
+ }
43
+ validateProviderModules(providerModules);
44
+ let activeProviderModules = providerModules;
45
+ export function currentProviderModules() {
46
+ return activeProviderModules;
47
+ }
48
+ /** Install an alternate registry for one in-process test and return its restore operation. */
49
+ export function installProviderModulesForTest(registry) {
50
+ const previous = activeProviderModules;
51
+ activeProviderModules = validateProviderModules(registry);
52
+ return () => {
53
+ activeProviderModules = previous;
54
+ };
55
+ }
@@ -1,7 +1,5 @@
1
1
  import type { EffectRouteKitControlHandlers } from "@velum-labs/routekit-control/effect";
2
2
  type ProviderHandlers = Pick<EffectRouteKitControlHandlers, "providers.status" | "models.list" | "models.info" | "calls.inspect" | "calls.leaderboard">;
3
3
  /** Owns provider health, catalog, inspection, and leaderboard queries. */
4
- export declare class ProviderQueryService {
5
- handlers(): ProviderHandlers;
6
- }
4
+ export declare const providerHandlers: ProviderHandlers;
7
5
  export {};
@@ -10,150 +10,147 @@ import { accountEntriesEffect, providerCredentialAvailable } from "./daemon-main
10
10
  import { writeSnapshotEffect } from "./daemon-state.js";
11
11
  import { aggregateInspections, buildLeaderboardResult, defaultLeaderboardWindow } from "./leaderboard.js";
12
12
  /** Owns provider health, catalog, inspection, and leaderboard queries. */
13
- export class ProviderQueryService {
14
- handlers() {
15
- return {
16
- "providers.status": (_params, context) => Effect.gen(function* () {
17
- const env = yield* DaemonEnv;
18
- const state = yield* DaemonState;
19
- const gateway = yield* ActiveGateway;
20
- const accounts = yield* accountEntriesEffect(env.env);
21
- const live = yield* gateway.router().providerStatuses(context.signal);
22
- const result = {
23
- providers: configuredProviderIds(state.config).map((provider) => {
24
- const status = live.find((entry) => entry.provider === provider);
25
- return {
26
- provider,
27
- configured: true,
28
- credentialAvailable: providerCredentialAvailable(provider, accounts, env.env),
29
- models: status?.models ?? [],
30
- ...(status?.error !== undefined ? { error: status.error } : {})
31
- };
32
- })
33
- };
34
- yield* writeSnapshotEffect(env.home, "health", "providers", {
35
- checkedAt: new Date().toISOString(),
36
- providers: result.providers
37
- });
38
- return result;
39
- }),
40
- "models.list": (params) => Effect.gen(function* () {
41
- const env = yield* DaemonEnv;
42
- const state = yield* DaemonState;
43
- const gateway = yield* ActiveGateway;
44
- const catalog = gateway.router().modelCatalog();
45
- const models = catalog
46
- .filter((model) => params.provider === undefined || model.id.startsWith(`${params.provider}/`))
47
- .map((model) => ({
48
- id: model.id,
49
- provider: model.provider,
50
- owned_by: model.provider,
51
- routekit_provider_priority: model.providerPriority,
52
- capabilities: { ...model.capabilities },
53
- ...(model.metadata?.architecture !== undefined
54
- ? {
55
- architecture: {
56
- modality: model.metadata.architecture.modality,
57
- input_modalities: model.metadata.architecture.inputModalities,
58
- output_modalities: model.metadata.architecture.outputModalities
59
- }
60
- }
61
- : {}),
62
- ...(model.metadata?.supportedParameters !== undefined
63
- ? { supported_parameters: model.metadata.supportedParameters }
64
- : {}),
65
- reasoning: model.reasoning === null || model.reasoning === undefined
66
- ? undefined
67
- : { ...model.reasoning }
68
- }));
69
- const result = {
70
- models,
71
- ...(state.config.defaultModel !== undefined
72
- ? { defaultModel: state.config.defaultModel }
73
- : catalog.some((model) => model.default)
74
- ? { defaultModel: catalog.find((model) => model.default)?.id }
75
- : {}),
76
- revision: state.revisions.config
77
- };
78
- yield* writeSnapshotEffect(env.home, "catalog", "models", {
79
- updatedAt: new Date().toISOString(),
80
- defaultModel: result.defaultModel,
81
- models
82
- });
83
- return result;
84
- }),
85
- "models.info": (params) => Effect.gen(function* () {
86
- const gateway = yield* ActiveGateway;
87
- const model = gateway.router().modelInfo(params.model);
88
- if (model === undefined) {
89
- return yield* Effect.fail(new ControlError({
90
- code: "not_found",
91
- message: `unknown model: ${params.model}`
92
- }));
93
- }
13
+ export const providerHandlers = {
14
+ "providers.status": (_params, context) => Effect.gen(function* () {
15
+ const env = yield* DaemonEnv;
16
+ const state = yield* DaemonState;
17
+ const gateway = yield* ActiveGateway;
18
+ const accounts = yield* accountEntriesEffect(env.env);
19
+ const live = yield* gateway.router().providerStatuses(context.signal);
20
+ const result = {
21
+ providers: configuredProviderIds(state.config).map((provider) => {
22
+ const source = state.config.sources[provider];
23
+ const status = live.find((entry) => entry.provider === provider);
94
24
  return {
95
- ...model,
96
- capabilities: { ...model.capabilities },
97
- reasoning: model.reasoning === null ? null : { ...model.reasoning }
25
+ provider,
26
+ configured: true,
27
+ credentialAvailable: providerCredentialAvailable(source, accounts, env.env),
28
+ models: status?.models ?? [],
29
+ ...(status?.error !== undefined ? { error: status.error } : {})
98
30
  };
99
- }),
100
- "calls.inspect": (params) => Effect.gen(function* () {
101
- const attributions = yield* CallAttributions;
102
- const inspection = attributions.get(params.callId);
103
- if (inspection === undefined) {
104
- return yield* Effect.fail(new ControlError({
105
- code: "not_found",
106
- message: `unknown or expired model call: ${params.callId}`
107
- }));
108
- }
109
- return inspection;
110
- }),
111
- "calls.leaderboard": (params) => Effect.gen(function* () {
112
- const attributions = yield* CallAttributions;
113
- const leaderboard = yield* Leaderboard;
114
- const config = leaderboard.config();
115
- const by = params.by ?? "principal";
116
- const sort = params.sort ?? "cost";
117
- const limit = params.limit ?? 20;
118
- const window = params.window ?? defaultLeaderboardWindow(config);
119
- const nowIso = new Date().toISOString();
120
- if (window === "live") {
121
- const aggregated = aggregateInspections(attributions.list(), {
122
- by,
123
- sort,
124
- limit
125
- });
126
- return buildLeaderboardResult({
127
- by,
128
- sort,
129
- source: "live",
130
- windowStart: aggregated.windowStart ?? nowIso,
131
- windowEnd: aggregated.windowEnd ?? nowIso,
132
- sampleSize: aggregated.sampleSize,
133
- truncated: attributions.truncated(),
134
- budget: config,
135
- rows: aggregated.rows
136
- });
137
- }
138
- if (!config.durable) {
139
- return yield* Effect.fail(new ControlError({
140
- code: "bad_request",
141
- message: "durable leaderboard rollups are disabled; set leaderboard.durable: true in router.yaml"
142
- }));
143
- }
144
- const aggregated = leaderboard.rollups.query({ by, sort, limit, window });
145
- return buildLeaderboardResult({
146
- by,
147
- sort,
148
- source: "durable",
149
- windowStart: aggregated.windowStart,
150
- windowEnd: aggregated.windowEnd,
151
- sampleSize: aggregated.sampleSize,
152
- truncated: false,
153
- budget: config,
154
- rows: aggregated.rows
155
- });
156
31
  })
157
32
  };
158
- }
159
- }
33
+ yield* writeSnapshotEffect(env.home, "health", "providers", {
34
+ checkedAt: new Date().toISOString(),
35
+ providers: result.providers
36
+ });
37
+ return result;
38
+ }),
39
+ "models.list": (params) => Effect.gen(function* () {
40
+ const env = yield* DaemonEnv;
41
+ const state = yield* DaemonState;
42
+ const gateway = yield* ActiveGateway;
43
+ const catalog = gateway.router().modelCatalog();
44
+ const models = catalog
45
+ .filter((model) => params.provider === undefined || model.id.startsWith(`${params.provider}/`))
46
+ .map((model) => ({
47
+ id: model.id,
48
+ provider: model.provider,
49
+ owned_by: model.provider,
50
+ routekit_provider_priority: model.providerPriority,
51
+ capabilities: { ...model.capabilities },
52
+ ...(model.metadata?.architecture !== undefined
53
+ ? {
54
+ architecture: {
55
+ modality: model.metadata.architecture.modality,
56
+ input_modalities: model.metadata.architecture.inputModalities,
57
+ output_modalities: model.metadata.architecture.outputModalities
58
+ }
59
+ }
60
+ : {}),
61
+ ...(model.metadata?.supportedParameters !== undefined
62
+ ? { supported_parameters: model.metadata.supportedParameters }
63
+ : {}),
64
+ reasoning: model.reasoning === null || model.reasoning === undefined
65
+ ? undefined
66
+ : { ...model.reasoning }
67
+ }));
68
+ const result = {
69
+ models,
70
+ ...(state.config.defaultModel !== undefined
71
+ ? { defaultModel: state.config.defaultModel }
72
+ : catalog.some((model) => model.default)
73
+ ? { defaultModel: catalog.find((model) => model.default)?.id }
74
+ : {}),
75
+ revision: state.revisions.config
76
+ };
77
+ yield* writeSnapshotEffect(env.home, "catalog", "models", {
78
+ updatedAt: new Date().toISOString(),
79
+ defaultModel: result.defaultModel,
80
+ models
81
+ });
82
+ return result;
83
+ }),
84
+ "models.info": (params) => Effect.gen(function* () {
85
+ const gateway = yield* ActiveGateway;
86
+ const model = gateway.router().modelInfo(params.model);
87
+ if (model === undefined) {
88
+ return yield* Effect.fail(new ControlError({
89
+ code: "not_found",
90
+ message: `unknown model: ${params.model}`
91
+ }));
92
+ }
93
+ return {
94
+ ...model,
95
+ capabilities: { ...model.capabilities },
96
+ reasoning: model.reasoning === null ? null : { ...model.reasoning }
97
+ };
98
+ }),
99
+ "calls.inspect": (params) => Effect.gen(function* () {
100
+ const attributions = yield* CallAttributions;
101
+ const inspection = attributions.get(params.callId);
102
+ if (inspection === undefined) {
103
+ return yield* Effect.fail(new ControlError({
104
+ code: "not_found",
105
+ message: `unknown or expired model call: ${params.callId}`
106
+ }));
107
+ }
108
+ return inspection;
109
+ }),
110
+ "calls.leaderboard": (params) => Effect.gen(function* () {
111
+ const attributions = yield* CallAttributions;
112
+ const leaderboard = yield* Leaderboard;
113
+ const config = leaderboard.config();
114
+ const by = params.by ?? "principal";
115
+ const sort = params.sort ?? "cost";
116
+ const limit = params.limit ?? 20;
117
+ const window = params.window ?? defaultLeaderboardWindow(config);
118
+ const nowIso = new Date().toISOString();
119
+ if (window === "live") {
120
+ const aggregated = aggregateInspections(attributions.list(), {
121
+ by,
122
+ sort,
123
+ limit
124
+ });
125
+ return buildLeaderboardResult({
126
+ by,
127
+ sort,
128
+ source: "live",
129
+ windowStart: aggregated.windowStart ?? nowIso,
130
+ windowEnd: aggregated.windowEnd ?? nowIso,
131
+ sampleSize: aggregated.sampleSize,
132
+ truncated: attributions.truncated(),
133
+ budget: config,
134
+ rows: aggregated.rows
135
+ });
136
+ }
137
+ if (!config.durable) {
138
+ return yield* Effect.fail(new ControlError({
139
+ code: "bad_request",
140
+ message: "durable leaderboard rollups are disabled; set leaderboard.durable: true in router.yaml"
141
+ }));
142
+ }
143
+ const aggregated = leaderboard.rollups.query({ by, sort, limit, window });
144
+ return buildLeaderboardResult({
145
+ by,
146
+ sort,
147
+ source: "durable",
148
+ windowStart: aggregated.windowStart,
149
+ windowEnd: aggregated.windowEnd,
150
+ sampleSize: aggregated.sampleSize,
151
+ truncated: false,
152
+ budget: config,
153
+ rows: aggregated.rows
154
+ });
155
+ })
156
+ };
@@ -1,7 +1,5 @@
1
1
  import type { EffectRouteKitControlHandlers } from "@velum-labs/routekit-control/effect";
2
2
  type RouterHandlers = Pick<EffectRouteKitControlHandlers, "daemon.reload" | "config.get" | "config.update" | "config.import" | "providers.set">;
3
3
  /** Owns config mutations and generation publication use cases. */
4
- export declare class RouterGenerationService {
5
- handlers(): RouterHandlers;
6
- }
4
+ export declare const routerGenerationHandlers: RouterHandlers;
7
5
  export {};