@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
@@ -0,0 +1,194 @@
1
+ import { providerDefaultBaseUrl, subscriptionInfo } from "@velum-labs/routekit-registry";
2
+ import { executeWebRequest, toRouteKitFailure } from "@velum-labs/routekit-runtime/effect";
3
+ import { Effect } from "effect";
4
+ import { loadSubscriptionCredential, persistSubscriptionCredential } from "@velum-labs/routekit-accounts";
5
+ import { decodeJsonBody } from "@velum-labs/routekit-accounts";
6
+ import { discoveredProviderLimitsFromUsagePayload, extraUsageFromUsagePayload } from "@velum-labs/routekit-accounts/provider";
7
+ import { adminRequest, authenticationFailure, canonicalRateLimitWindowKey, defineWindow, discoverSubscriptionModels, epochSeconds, errorMessage, refreshNetworkFailure, refreshResponseBody, retryAfter, SubscriptionRefreshError, usageRequest, utilization, windowsFromUsagePayload } from "@velum-labs/routekit-accounts";
8
+ export function anthropicProvider() {
9
+ const mode = "claude-code";
10
+ const info = subscriptionInfo(mode);
11
+ return {
12
+ mode,
13
+ upstreamBaseUrl: providerDefaultBaseUrl("anthropic") ?? "https://api.anthropic.com",
14
+ requestPath: "/v1/messages",
15
+ loadCredential: (path) => Effect.tryPromise({
16
+ try: () => loadSubscriptionCredential(mode, path),
17
+ catch: toRouteKitFailure
18
+ }),
19
+ persistCredential: (previous, next) => Effect.tryPromise({
20
+ try: () => persistSubscriptionCredential(previous, next),
21
+ catch: toRouteKitFailure
22
+ }),
23
+ discoverModels: (credential, signal) => discoverSubscriptionModels(mode, providerDefaultBaseUrl("anthropic") ?? "https://api.anthropic.com", thisAnthropicHeaders(info, credential), signal),
24
+ authHeaders: (credential) => ({
25
+ authorization: `Bearer ${credential.accessToken}`,
26
+ "anthropic-beta": info.oauthBetaHeader ?? "oauth-2025-04-20"
27
+ }),
28
+ refresh: (credential, signal) => Effect.gen(function* () {
29
+ if (credential.refreshToken === undefined) {
30
+ return yield* Effect.fail(new SubscriptionRefreshError({
31
+ kind: "permanent",
32
+ reasonCode: "missing_refresh_token"
33
+ }));
34
+ }
35
+ const response = yield* executeWebRequest(info.oauth.tokenEndpoint, {
36
+ method: "POST",
37
+ headers: { accept: "application/json", "content-type": "application/json" },
38
+ body: JSON.stringify({
39
+ grant_type: "refresh_token",
40
+ refresh_token: credential.refreshToken,
41
+ client_id: credential.oauthClientId ?? info.oauth.clientId
42
+ }),
43
+ ...(signal !== undefined ? { signal } : {})
44
+ }).pipe(Effect.mapError((error) => refreshNetworkFailure(error)));
45
+ const decoded = yield* Effect.tryPromise({
46
+ try: () => decodeJsonBody(response),
47
+ catch: (cause) => refreshNetworkFailure(cause)
48
+ });
49
+ if (response.ok && !decoded.hasJsonBody) {
50
+ return yield* Effect.fail(new SubscriptionRefreshError({
51
+ kind: "transient",
52
+ status: response.status,
53
+ failureKind: "protocol"
54
+ }));
55
+ }
56
+ return yield* Effect.try({
57
+ try: () => refreshResponseBody(response, decoded.body, credential),
58
+ catch: (cause) => refreshNetworkFailure(cause)
59
+ });
60
+ }).pipe(Effect.mapError(refreshNetworkFailure)),
61
+ fetchUsage: (credential, signal) => Effect.gen(function* () {
62
+ const payload = yield* usageRequest(info.oauth.usageEndpoint, {
63
+ ...thisAnthropicHeaders(info, credential)
64
+ }, signal);
65
+ const observedAt = Date.now() / 1000;
66
+ const parsed = windowsFromUsagePayload(mode, payload, observedAt, "usage");
67
+ const discovered = discoveredProviderLimitsFromUsagePayload(payload, observedAt, "usage");
68
+ const extraUsage = extraUsageFromUsagePayload(payload, observedAt, "usage");
69
+ const diagnostics = [...parsed.diagnostics, ...discovered.diagnostics];
70
+ return {
71
+ windows: parsed.windows,
72
+ ...(discovered.limits.length > 0 ? { discoveredLimits: discovered.limits } : {}),
73
+ ...(extraUsage !== undefined ? { extraUsage } : {}),
74
+ ...(diagnostics.length > 0 ? { diagnostics } : {}),
75
+ observedAt,
76
+ source: "usage",
77
+ completeness: "snapshot"
78
+ };
79
+ }),
80
+ parseLimits: (headers) => anthropicLimitsFromHeaders(headers),
81
+ parseStreamEvent: () => undefined,
82
+ classify: (status, headers, body) => {
83
+ const authentication = authenticationFailure(status, body, `Anthropic returned ${status}`);
84
+ if (authentication !== undefined)
85
+ return authentication;
86
+ if (status !== 429 && status < 500)
87
+ return undefined;
88
+ const rejection = anthropicRejectionFromHeaders(headers);
89
+ const message = errorMessage(body, `Anthropic returned ${status}`);
90
+ const quota = rejection.rejected ||
91
+ /(?:usage|weekly|five.?hour).*(?:limit|quota)|limit reached/i.test(message);
92
+ const retryAfterSeconds = retryAfter(headers, mode);
93
+ return {
94
+ category: quota ? "quota_exhausted" : "transient",
95
+ status,
96
+ message,
97
+ ...(retryAfterSeconds !== undefined ? { retryAfter: retryAfterSeconds } : {}),
98
+ ...(rejection.resetsAt !== undefined ? { resetsAt: rejection.resetsAt } : {})
99
+ };
100
+ },
101
+ fetchAdminUsageCost: (adminKey, range, signal) => Effect.gen(function* () {
102
+ const query = new URLSearchParams({
103
+ starting_at: new Date(range.startTime * 1000).toISOString(),
104
+ bucket_width: "1d"
105
+ });
106
+ if (range.endTime !== undefined) {
107
+ query.set("ending_at", new Date(range.endTime * 1000).toISOString());
108
+ }
109
+ const headers = {
110
+ "x-api-key": adminKey,
111
+ "anthropic-version": "2023-06-01"
112
+ };
113
+ const [usage, cost] = yield* Effect.all([
114
+ adminRequest(info.admin.usageEndpoint, query, headers, signal),
115
+ adminRequest(info.admin.costEndpoint, query, headers, signal)
116
+ ], { concurrency: "unbounded" });
117
+ return { usage, cost };
118
+ })
119
+ };
120
+ }
121
+ function anthropicRejectionFromHeaders(headers) {
122
+ const prefix = subscriptionInfo("claude-code").rateLimit.headerPrefix.toLowerCase();
123
+ const now = Date.now() / 1000;
124
+ let rejected = false;
125
+ let resetsAt;
126
+ for (const [name, value] of headers) {
127
+ if ((name !== `${prefix}-status` &&
128
+ !(name.startsWith(`${prefix}-`) && name.endsWith("-status"))) ||
129
+ !["rejected", "exceeded"].includes(value.trim().toLowerCase())) {
130
+ continue;
131
+ }
132
+ rejected = true;
133
+ const reset = epochSeconds(headers.get(`${name.slice(0, -"-status".length)}-reset`));
134
+ // Rejection is useful even when utilization is absent. Do not fabricate a
135
+ // usage window, or let a healthy/expired window shorten its cooldown. All
136
+ // known rejected windows must clear before the account is ready again.
137
+ if (reset !== undefined && reset > now) {
138
+ resetsAt = Math.max(resetsAt ?? reset, reset);
139
+ }
140
+ }
141
+ return { rejected, ...(resetsAt !== undefined ? { resetsAt } : {}) };
142
+ }
143
+ function thisAnthropicHeaders(info, credential) {
144
+ return {
145
+ authorization: `Bearer ${credential.accessToken}`,
146
+ "anthropic-beta": info.oauthBetaHeader ?? "oauth-2025-04-20"
147
+ };
148
+ }
149
+ function anthropicLimitsFromHeaders(headers) {
150
+ const prefix = subscriptionInfo("claude-code").rateLimit.headerPrefix.toLowerCase();
151
+ const observedAt = Date.now() / 1000;
152
+ const windows = Object.create(null);
153
+ const diagnostics = [];
154
+ const suffixes = new Set();
155
+ for (const [name] of headers) {
156
+ const lowered = name.toLowerCase();
157
+ const match = new RegExp(`^${prefix}-(.+)-(utilization|status|reset)$`).exec(lowered);
158
+ if (match?.[1] !== undefined)
159
+ suffixes.add(match[1]);
160
+ }
161
+ for (const key of suffixes) {
162
+ const window = canonicalRateLimitWindowKey("claude-code", key);
163
+ const raw = headers.get(`${prefix}-${key}-utilization`);
164
+ const used = utilization(raw);
165
+ if (used === undefined) {
166
+ if (raw !== null) {
167
+ diagnostics.push({
168
+ code: "invalid_utilization",
169
+ window,
170
+ field: "utilization"
171
+ });
172
+ }
173
+ continue;
174
+ }
175
+ const status = headers.get(`${prefix}-${key}-status`);
176
+ const resetsAt = epochSeconds(headers.get(`${prefix}-${key}-reset`));
177
+ defineWindow(windows, window, {
178
+ utilization: used,
179
+ ...(status !== null ? { status } : {}),
180
+ ...(resetsAt !== undefined ? { resetsAt } : {}),
181
+ observedAt,
182
+ source: "headers"
183
+ });
184
+ }
185
+ return Object.keys(windows).length > 0 || diagnostics.length > 0
186
+ ? {
187
+ windows,
188
+ ...(diagnostics.length > 0 ? { diagnostics } : {}),
189
+ observedAt,
190
+ source: "headers",
191
+ completeness: "partial"
192
+ }
193
+ : undefined;
194
+ }
@@ -0,0 +1,140 @@
1
+ import assert from "node:assert/strict";
2
+ import { mkdtemp, rm, writeFile } from "node:fs/promises";
3
+ import { createServer } from "node:http";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { test } from "node:test";
7
+ import { SubscriptionAccountSet } from "@velum-labs/routekit-accounts";
8
+ import { HttpSubmissionAuthority, HttpSubmissionError, runRouteKitEffect, toRouteKitFailure } from "@velum-labs/routekit-runtime/effect";
9
+ import { Effect } from "effect";
10
+ import { AnthropicBackendRelay } from "../relay.js";
11
+ import { anthropicProvider } from "../subscription.js";
12
+ const MODEL = "claude-sonnet-4-5";
13
+ const TOKEN = "offline-native-claude-token";
14
+ for (const disconnect of [false, true]) {
15
+ test(`native managed Claude relay observes ${disconnect ? "unknown without retry" : "raw completion"} at its credential-owning POST`, async (t) => {
16
+ const directory = await mkdtemp(join(tmpdir(), "routekit-native-claude-submission-"));
17
+ const intents = [];
18
+ const outcomes = [];
19
+ const posts = [];
20
+ const errors = [];
21
+ let accounts;
22
+ const wire = [
23
+ 'event: content_block_delta\ndata: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"offline claude"}}\n\n',
24
+ 'event: message_stop\ndata: {"type":"message_stop"}\n\n'
25
+ ].join("");
26
+ const server = createServer((request, response) => {
27
+ void (async () => {
28
+ const chunks = [];
29
+ for await (const chunk of request)
30
+ chunks.push(Buffer.from(chunk));
31
+ posts.push({
32
+ body: JSON.parse(Buffer.concat(chunks).toString("utf8")),
33
+ complete: request.complete,
34
+ authorization: request.headers.authorization,
35
+ apiKey: request.headers["x-api-key"],
36
+ intentsBeforePost: intents.length
37
+ });
38
+ assert.equal(request.url, "/v1/messages");
39
+ if (disconnect) {
40
+ response.destroy();
41
+ return;
42
+ }
43
+ response.writeHead(200, { "content-type": "text/event-stream" });
44
+ response.end(wire);
45
+ })().catch((cause) => {
46
+ errors.push(cause);
47
+ response.destroy();
48
+ });
49
+ });
50
+ t.after(async () => {
51
+ try {
52
+ if (accounts !== undefined)
53
+ await runRouteKitEffect(accounts.close());
54
+ }
55
+ finally {
56
+ try {
57
+ await new Promise((resolve, reject) => {
58
+ server.close((error) => (error === undefined ? resolve() : reject(error)));
59
+ server.closeAllConnections();
60
+ });
61
+ }
62
+ finally {
63
+ await rm(directory, { recursive: true, force: true });
64
+ }
65
+ }
66
+ assert.deepEqual(errors, []);
67
+ });
68
+ await new Promise((resolve, reject) => {
69
+ server.once("error", reject);
70
+ server.listen(0, "127.0.0.1", resolve);
71
+ });
72
+ const address = server.address();
73
+ assert.ok(address !== null && typeof address === "object");
74
+ await writeFile(join(directory, "account.json"), JSON.stringify({
75
+ claudeAiOauth: {
76
+ accessToken: TOKEN,
77
+ expiresAt: Date.now() + 3_600_000
78
+ }
79
+ }), { mode: 0o600 });
80
+ accounts = await runRouteKitEffect(SubscriptionAccountSet.open(anthropicProvider(), {
81
+ source: { kind: "directory", path: directory },
82
+ publishedModels: [MODEL],
83
+ probeIntervalMs: 0
84
+ }));
85
+ const relay = new AnthropicBackendRelay({
86
+ accounts,
87
+ backendUrl: `http://127.0.0.1:${address.port}`
88
+ });
89
+ assert.equal(relay.httpSubmission, undefined, "an overridden endpoint is not publicly certified");
90
+ assert.equal(new AnthropicBackendRelay({ accounts }).httpSubmission, "credential-owner-http-v1");
91
+ const authority = {
92
+ begin: (request) => Effect.sync(() => {
93
+ intents.push(request);
94
+ return { attemptId: `offline-native-${intents.length}` };
95
+ }),
96
+ settle: (_attemptId, outcome) => Effect.sync(() => {
97
+ outcomes.push(outcome);
98
+ })
99
+ };
100
+ let retries = 0;
101
+ const result = await runRouteKitEffect(relay
102
+ .relay({ authorization: "Bearer offline-ingress-token", "x-api-key": "offline-ingress-key" }, {
103
+ model: MODEL,
104
+ stream: true,
105
+ max_tokens: 32,
106
+ messages: [{ role: "user", content: "hello" }]
107
+ }, undefined, {
108
+ responseMode: "buffered",
109
+ onAttribution: (update) => {
110
+ if (update.accountAttemptResult !== undefined)
111
+ retries += 1;
112
+ }
113
+ })
114
+ .pipe(Effect.flatMap((response) => Effect.tryPromise({
115
+ try: () => response.text(),
116
+ catch: (cause) => cause instanceof HttpSubmissionError ? cause : toRouteKitFailure(cause)
117
+ })), Effect.catch((cause) => Effect.succeed(cause)), Effect.scoped, Effect.provideService(HttpSubmissionAuthority, authority)));
118
+ assert.equal(posts.length, 1);
119
+ assert.equal(posts[0]?.complete, true);
120
+ assert.equal(posts[0]?.intentsBeforePost, 1);
121
+ assert.equal(posts[0]?.authorization, `Bearer ${TOKEN}`);
122
+ assert.equal(posts[0]?.apiKey, undefined);
123
+ assert.equal(posts[0]?.body.model, MODEL);
124
+ assert.equal(intents[0]?.model, MODEL);
125
+ assert.match(intents[0]?.accountIdentity ?? "", /^[a-f0-9]{64}$/u);
126
+ assert.equal(retries, 0);
127
+ assert.ok(accounts.snapshot().members.every((member) => member.inFlight === 0));
128
+ assert.equal(JSON.stringify(intents).includes(TOKEN), false);
129
+ if (disconnect) {
130
+ assert.ok(result instanceof HttpSubmissionError);
131
+ assert.equal(result.code, "unknown-submission");
132
+ assert.deepEqual(outcomes, [{ state: "unknown", reason: "transport-error" }]);
133
+ }
134
+ else {
135
+ assert.equal(result, wire, "the native relay must preserve exact upstream bytes");
136
+ assert.equal(outcomes[0]?.state, "completed");
137
+ assert.equal(outcomes[0]?.state === "completed" ? outcomes[0].terminal : undefined, "messages-stop");
138
+ }
139
+ });
140
+ }