@ambitresearch/genie 1.3.1
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.
- package/README.md +15 -0
- package/dist/auth/bearer.d.ts +49 -0
- package/dist/auth/bearer.d.ts.map +1 -0
- package/dist/auth/bearer.js +159 -0
- package/dist/auth/bearer.js.map +1 -0
- package/dist/auth/oauth/authorize.d.ts +19 -0
- package/dist/auth/oauth/authorize.d.ts.map +1 -0
- package/dist/auth/oauth/authorize.js +111 -0
- package/dist/auth/oauth/authorize.js.map +1 -0
- package/dist/auth/oauth/config.d.ts +10 -0
- package/dist/auth/oauth/config.d.ts.map +1 -0
- package/dist/auth/oauth/config.js +16 -0
- package/dist/auth/oauth/config.js.map +1 -0
- package/dist/auth/oauth/dcr.d.ts +15 -0
- package/dist/auth/oauth/dcr.d.ts.map +1 -0
- package/dist/auth/oauth/dcr.js +38 -0
- package/dist/auth/oauth/dcr.js.map +1 -0
- package/dist/auth/oauth/jwt.d.ts +13 -0
- package/dist/auth/oauth/jwt.d.ts.map +1 -0
- package/dist/auth/oauth/jwt.js +46 -0
- package/dist/auth/oauth/jwt.js.map +1 -0
- package/dist/auth/oauth/metadata.d.ts +9 -0
- package/dist/auth/oauth/metadata.d.ts.map +1 -0
- package/dist/auth/oauth/metadata.js +19 -0
- package/dist/auth/oauth/metadata.js.map +1 -0
- package/dist/auth/oauth/router.d.ts +32 -0
- package/dist/auth/oauth/router.d.ts.map +1 -0
- package/dist/auth/oauth/router.js +159 -0
- package/dist/auth/oauth/router.js.map +1 -0
- package/dist/auth/oauth/store.d.ts +55 -0
- package/dist/auth/oauth/store.d.ts.map +1 -0
- package/dist/auth/oauth/store.js +63 -0
- package/dist/auth/oauth/store.js.map +1 -0
- package/dist/auth/oauth/token.d.ts +21 -0
- package/dist/auth/oauth/token.d.ts.map +1 -0
- package/dist/auth/oauth/token.js +77 -0
- package/dist/auth/oauth/token.js.map +1 -0
- package/dist/auth/oidc/group-policy.d.ts +43 -0
- package/dist/auth/oidc/group-policy.d.ts.map +1 -0
- package/dist/auth/oidc/group-policy.js +54 -0
- package/dist/auth/oidc/group-policy.js.map +1 -0
- package/dist/auth/oidc/verifier.d.ts +42 -0
- package/dist/auth/oidc/verifier.d.ts.map +1 -0
- package/dist/auth/oidc/verifier.js +145 -0
- package/dist/auth/oidc/verifier.js.map +1 -0
- package/dist/auth/token-cli.d.ts +9 -0
- package/dist/auth/token-cli.d.ts.map +1 -0
- package/dist/auth/token-cli.js +98 -0
- package/dist/auth/token-cli.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +149 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/redact.d.ts +20 -0
- package/dist/config/redact.d.ts.map +1 -0
- package/dist/config/redact.js +77 -0
- package/dist/config/redact.js.map +1 -0
- package/dist/config/secrets.d.ts +100 -0
- package/dist/config/secrets.d.ts.map +1 -0
- package/dist/config/secrets.js +130 -0
- package/dist/config/secrets.js.map +1 -0
- package/dist/framework/html.d.ts +103 -0
- package/dist/framework/html.d.ts.map +1 -0
- package/dist/framework/html.js +271 -0
- package/dist/framework/html.js.map +1 -0
- package/dist/framework/interface.d.ts +130 -0
- package/dist/framework/interface.d.ts.map +1 -0
- package/dist/framework/interface.js +112 -0
- package/dist/framework/interface.js.map +1 -0
- package/dist/framework/react.d.ts +133 -0
- package/dist/framework/react.d.ts.map +1 -0
- package/dist/framework/react.js +243 -0
- package/dist/framework/react.js.map +1 -0
- package/dist/framework/vue.d.ts +99 -0
- package/dist/framework/vue.d.ts.map +1 -0
- package/dist/framework/vue.js +375 -0
- package/dist/framework/vue.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/client.d.ts +81 -0
- package/dist/llm/client.d.ts.map +1 -0
- package/dist/llm/client.js +141 -0
- package/dist/llm/client.js.map +1 -0
- package/dist/llm/component-response.d.ts +111 -0
- package/dist/llm/component-response.d.ts.map +1 -0
- package/dist/llm/component-response.js +167 -0
- package/dist/llm/component-response.js.map +1 -0
- package/dist/llm/generated-files.d.ts +10 -0
- package/dist/llm/generated-files.d.ts.map +1 -0
- package/dist/llm/generated-files.js +26 -0
- package/dist/llm/generated-files.js.map +1 -0
- package/dist/llm/prompts/CHANGELOG.md +70 -0
- package/dist/llm/prompts/generate-component.system.md +99 -0
- package/dist/llm/prompts/refine-component.system.md +112 -0
- package/dist/llm/prompts.d.ts +66 -0
- package/dist/llm/prompts.d.ts.map +1 -0
- package/dist/llm/prompts.js +78 -0
- package/dist/llm/prompts.js.map +1 -0
- package/dist/llm/retry.d.ts +183 -0
- package/dist/llm/retry.d.ts.map +1 -0
- package/dist/llm/retry.js +475 -0
- package/dist/llm/retry.js.map +1 -0
- package/dist/llm/schema.d.ts +129 -0
- package/dist/llm/schema.d.ts.map +1 -0
- package/dist/llm/schema.js +147 -0
- package/dist/llm/schema.js.map +1 -0
- package/dist/llm/validate.d.ts +107 -0
- package/dist/llm/validate.d.ts.map +1 -0
- package/dist/llm/validate.js +174 -0
- package/dist/llm/validate.js.map +1 -0
- package/dist/manifest/compiler.d.ts +112 -0
- package/dist/manifest/compiler.d.ts.map +1 -0
- package/dist/manifest/compiler.js +306 -0
- package/dist/manifest/compiler.js.map +1 -0
- package/dist/manifest/ensure.d.ts +46 -0
- package/dist/manifest/ensure.d.ts.map +1 -0
- package/dist/manifest/ensure.js +49 -0
- package/dist/manifest/ensure.js.map +1 -0
- package/dist/manifest/index.d.ts +13 -0
- package/dist/manifest/index.d.ts.map +1 -0
- package/dist/manifest/index.js +13 -0
- package/dist/manifest/index.js.map +1 -0
- package/dist/middleware/plan-guard.d.ts +167 -0
- package/dist/middleware/plan-guard.d.ts.map +1 -0
- package/dist/middleware/plan-guard.js +190 -0
- package/dist/middleware/plan-guard.js.map +1 -0
- package/dist/plans/index.d.ts +71 -0
- package/dist/plans/index.d.ts.map +1 -0
- package/dist/plans/index.js +252 -0
- package/dist/plans/index.js.map +1 -0
- package/dist/schemas/component.schema.json +114 -0
- package/dist/server.d.ts +96 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +258 -0
- package/dist/server.js.map +1 -0
- package/dist/store/empty-manifest.d.ts +58 -0
- package/dist/store/empty-manifest.d.ts.map +1 -0
- package/dist/store/empty-manifest.js +59 -0
- package/dist/store/empty-manifest.js.map +1 -0
- package/dist/store/git-host.d.ts +120 -0
- package/dist/store/git-host.d.ts.map +1 -0
- package/dist/store/git-host.js +796 -0
- package/dist/store/git-host.js.map +1 -0
- package/dist/store/index.d.ts +5 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +4 -0
- package/dist/store/index.js.map +1 -0
- package/dist/store/interface.d.ts +322 -0
- package/dist/store/interface.d.ts.map +1 -0
- package/dist/store/interface.js +100 -0
- package/dist/store/interface.js.map +1 -0
- package/dist/store/kit-files.d.ts +88 -0
- package/dist/store/kit-files.d.ts.map +1 -0
- package/dist/store/kit-files.js +199 -0
- package/dist/store/kit-files.js.map +1 -0
- package/dist/store/local.d.ts +70 -0
- package/dist/store/local.d.ts.map +1 -0
- package/dist/store/local.js +651 -0
- package/dist/store/local.js.map +1 -0
- package/dist/store/manifest.d.ts +44 -0
- package/dist/store/manifest.d.ts.map +1 -0
- package/dist/store/manifest.js +116 -0
- package/dist/store/manifest.js.map +1 -0
- package/dist/store/viewer-assets.d.ts +20 -0
- package/dist/store/viewer-assets.d.ts.map +1 -0
- package/dist/store/viewer-assets.js +118 -0
- package/dist/store/viewer-assets.js.map +1 -0
- package/dist/sync/anchor.d.ts +125 -0
- package/dist/sync/anchor.d.ts.map +1 -0
- package/dist/sync/anchor.js +210 -0
- package/dist/sync/anchor.js.map +1 -0
- package/dist/sync/index.d.ts +15 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +15 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/orchestrator.d.ts +160 -0
- package/dist/sync/orchestrator.d.ts.map +1 -0
- package/dist/sync/orchestrator.js +288 -0
- package/dist/sync/orchestrator.js.map +1 -0
- package/dist/tools/bind_kit.d.ts +29 -0
- package/dist/tools/bind_kit.d.ts.map +1 -0
- package/dist/tools/bind_kit.js +70 -0
- package/dist/tools/bind_kit.js.map +1 -0
- package/dist/tools/conjure.d.ts +141 -0
- package/dist/tools/conjure.d.ts.map +1 -0
- package/dist/tools/conjure.js +655 -0
- package/dist/tools/conjure.js.map +1 -0
- package/dist/tools/conjure_screen.d.ts +174 -0
- package/dist/tools/conjure_screen.d.ts.map +1 -0
- package/dist/tools/conjure_screen.js +458 -0
- package/dist/tools/conjure_screen.js.map +1 -0
- package/dist/tools/create_kit.d.ts +32 -0
- package/dist/tools/create_kit.d.ts.map +1 -0
- package/dist/tools/create_kit.js +131 -0
- package/dist/tools/create_kit.js.map +1 -0
- package/dist/tools/create_project.d.ts +267 -0
- package/dist/tools/create_project.d.ts.map +1 -0
- package/dist/tools/create_project.js +546 -0
- package/dist/tools/create_project.js.map +1 -0
- package/dist/tools/delete_files.d.ts +93 -0
- package/dist/tools/delete_files.d.ts.map +1 -0
- package/dist/tools/delete_files.js +276 -0
- package/dist/tools/delete_files.js.map +1 -0
- package/dist/tools/delete_project.d.ts +43 -0
- package/dist/tools/delete_project.d.ts.map +1 -0
- package/dist/tools/delete_project.js +118 -0
- package/dist/tools/delete_project.js.map +1 -0
- package/dist/tools/get_kit.d.ts +28 -0
- package/dist/tools/get_kit.d.ts.map +1 -0
- package/dist/tools/get_kit.js +95 -0
- package/dist/tools/get_kit.js.map +1 -0
- package/dist/tools/get_project.d.ts +37 -0
- package/dist/tools/get_project.d.ts.map +1 -0
- package/dist/tools/get_project.js +71 -0
- package/dist/tools/get_project.js.map +1 -0
- package/dist/tools/list_components.d.ts +42 -0
- package/dist/tools/list_components.d.ts.map +1 -0
- package/dist/tools/list_components.js +125 -0
- package/dist/tools/list_components.js.map +1 -0
- package/dist/tools/list_files.d.ts +32 -0
- package/dist/tools/list_files.d.ts.map +1 -0
- package/dist/tools/list_files.js +109 -0
- package/dist/tools/list_files.js.map +1 -0
- package/dist/tools/list_kits.d.ts +14 -0
- package/dist/tools/list_kits.d.ts.map +1 -0
- package/dist/tools/list_kits.js +41 -0
- package/dist/tools/list_kits.js.map +1 -0
- package/dist/tools/list_projects.d.ts +23 -0
- package/dist/tools/list_projects.d.ts.map +1 -0
- package/dist/tools/list_projects.js +66 -0
- package/dist/tools/list_projects.js.map +1 -0
- package/dist/tools/plan.d.ts +16 -0
- package/dist/tools/plan.d.ts.map +1 -0
- package/dist/tools/plan.js +146 -0
- package/dist/tools/plan.js.map +1 -0
- package/dist/tools/preview.d.ts +289 -0
- package/dist/tools/preview.d.ts.map +1 -0
- package/dist/tools/preview.js +672 -0
- package/dist/tools/preview.js.map +1 -0
- package/dist/tools/read_file.d.ts +40 -0
- package/dist/tools/read_file.d.ts.map +1 -0
- package/dist/tools/read_file.js +114 -0
- package/dist/tools/read_file.js.map +1 -0
- package/dist/tools/refine.d.ts +220 -0
- package/dist/tools/refine.d.ts.map +1 -0
- package/dist/tools/refine.js +501 -0
- package/dist/tools/refine.js.map +1 -0
- package/dist/tools/validate.d.ts +74 -0
- package/dist/tools/validate.d.ts.map +1 -0
- package/dist/tools/validate.js +224 -0
- package/dist/tools/validate.js.map +1 -0
- package/dist/tools/write_files.d.ts +158 -0
- package/dist/tools/write_files.d.ts.map +1 -0
- package/dist/tools/write_files.js +541 -0
- package/dist/tools/write_files.js.map +1 -0
- package/dist/transport.d.ts +77 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +448 -0
- package/dist/transport.js.map +1 -0
- package/dist/ui/card-asset-broker.d.ts +42 -0
- package/dist/ui/card-asset-broker.d.ts.map +1 -0
- package/dist/ui/card-asset-broker.js +479 -0
- package/dist/ui/card-asset-broker.js.map +1 -0
- package/dist/ui/grid-resource.d.ts +252 -0
- package/dist/ui/grid-resource.d.ts.map +1 -0
- package/dist/ui/grid-resource.js +680 -0
- package/dist/ui/grid-resource.js.map +1 -0
- package/dist/ui/viewer-static/index.html +95 -0
- package/dist/ui/viewer-static/viewer.css +451 -0
- package/dist/ui/viewer-static/viewer.js +1405 -0
- package/dist/validate/full-scan.d.ts +87 -0
- package/dist/validate/full-scan.d.ts.map +1 -0
- package/dist/validate/full-scan.js +299 -0
- package/dist/validate/full-scan.js.map +1 -0
- package/dist/validate/index.d.ts +15 -0
- package/dist/validate/index.d.ts.map +1 -0
- package/dist/validate/index.js +18 -0
- package/dist/validate/index.js.map +1 -0
- package/dist/validate/marker.d.ts +89 -0
- package/dist/validate/marker.d.ts.map +1 -0
- package/dist/validate/marker.js +87 -0
- package/dist/validate/marker.js.map +1 -0
- package/dist/validate/phash.d.ts +135 -0
- package/dist/validate/phash.d.ts.map +1 -0
- package/dist/validate/phash.js +307 -0
- package/dist/validate/phash.js.map +1 -0
- package/dist/validate/render.d.ts +38 -0
- package/dist/validate/render.d.ts.map +1 -0
- package/dist/validate/render.js +155 -0
- package/dist/validate/render.js.map +1 -0
- package/dist/watch/index.d.ts +12 -0
- package/dist/watch/index.d.ts.map +1 -0
- package/dist/watch/index.js +12 -0
- package/dist/watch/index.js.map +1 -0
- package/dist/watch/watcher.d.ts +65 -0
- package/dist/watch/watcher.d.ts.map +1 -0
- package/dist/watch/watcher.js +238 -0
- package/dist/watch/watcher.js.map +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry/backoff middleware (M2-06, DRO-253) — wraps an async LLM handler and
|
|
3
|
+
* retries transient failures with exponential-jittered backoff, honouring
|
|
4
|
+
* `Retry-After`, capped by `GENIE_LLM_RETRY_MAX`, surfacing a typed
|
|
5
|
+
* `RateLimitedError | TransientError` on exhaustion, and logging one line
|
|
6
|
+
* per retry to **stderr** (never stdout — on the stdio MCP transport, stdout
|
|
7
|
+
* IS the JSON-RPC protocol stream, and any stray line there corrupts every
|
|
8
|
+
* client's message framing; see `client.ts` / `plans/index.ts` /
|
|
9
|
+
* `middleware/plan-guard.ts` for the same convention).
|
|
10
|
+
*
|
|
11
|
+
* Composed as `withRetry(createChatCompletion)` at the call sites of every
|
|
12
|
+
* future generation verb (`conjure` / `refine`, M2-03 / M2-04). Kept as a
|
|
13
|
+
* generic HOF rather than baked into `client.ts` for two reasons:
|
|
14
|
+
* 1. `client.ts` deliberately makes exactly one attempt per call (see the
|
|
15
|
+
* comment on `maxRetries: 0` there) so retry policy lives in exactly
|
|
16
|
+
* one place — this file — where its "max N attempts" and "each attempt
|
|
17
|
+
* logged" guarantees can't be silently double-counted by SDK-internal
|
|
18
|
+
* retries happening a layer below.
|
|
19
|
+
* 2. A future non-chat handler (say, an embedding call) can be wrapped
|
|
20
|
+
* the same way without knowing anything about the chat completions
|
|
21
|
+
* surface.
|
|
22
|
+
*
|
|
23
|
+
* Retryability discriminates by the openai SDK's real error class hierarchy
|
|
24
|
+
* (`RateLimitError` / `InternalServerError` / `APIConnectionError` /
|
|
25
|
+
* `APIConnectionTimeoutError`) rather than by string-matching on `err.code`
|
|
26
|
+
* or `err.message`: global `fetch` wraps a socket-reset root cause as
|
|
27
|
+
* `TypeError('fetch failed')` and the SDK re-wraps that as an
|
|
28
|
+
* `APIConnectionError` — meaning the ECONNRESET code lives two levels deep
|
|
29
|
+
* at `err.cause.cause.code` and cannot be found by looking at the outermost
|
|
30
|
+
* error's own properties. The instanceof checks are the primary discriminator;
|
|
31
|
+
* the deep-cause code check is a belt-and-braces fallback for any transport
|
|
32
|
+
* that surfaces a raw node error without the SDK wrapping (e.g. a pre-request
|
|
33
|
+
* DNS failure, or a future alternate transport).
|
|
34
|
+
*/
|
|
35
|
+
import { APIConnectionError, APIConnectionTimeoutError, APIError, InternalServerError, RateLimitError, } from "openai";
|
|
36
|
+
// ─── Constants ───────────────────────────────────────────────────────────
|
|
37
|
+
/** Env var that overrides the default retry ceiling (AC5). */
|
|
38
|
+
export const RETRY_MAX_ENV = "GENIE_LLM_RETRY_MAX";
|
|
39
|
+
/** Default retry ceiling when the env var is unset / invalid (AC5). */
|
|
40
|
+
export const DEFAULT_RETRY_MAX = 3;
|
|
41
|
+
/** Base backoff delay: 1 s (AC4). */
|
|
42
|
+
export const BASE_DELAY_MS = 1_000;
|
|
43
|
+
/** Backoff cap: 30 s (AC4). */
|
|
44
|
+
export const MAX_DELAY_MS = 30_000;
|
|
45
|
+
/** Jitter multiplier applied symmetrically around the exponential value (AC4: ±20 %). */
|
|
46
|
+
export const JITTER_RATIO = 0.2;
|
|
47
|
+
/**
|
|
48
|
+
* Hard ceiling on an upstream-honoured `Retry-After` value once it's used as
|
|
49
|
+
* an actual `sleep()` duration (Copilot review on PR #126 / DRO-253).
|
|
50
|
+
* `MAX_DELAY_MS` only bounds OUR OWN computed exponential backoff — a value
|
|
51
|
+
* entirely within this module's control — but an honoured `Retry-After` is
|
|
52
|
+
* trusted verbatim by `parseRetryAfter`. A misconfigured or hostile upstream
|
|
53
|
+
* sending e.g. `Retry-After: 86400` (one day) would otherwise stall the
|
|
54
|
+
* in-flight call — and on the stdio MCP transport, the whole in-flight
|
|
55
|
+
* request — for that entire duration: `GENIE_LLM_REQUEST_TIMEOUT_MS` only
|
|
56
|
+
* bounds each individual HTTP request, not the sleep between retries.
|
|
57
|
+
*
|
|
58
|
+
* Set to 60 s — double `MAX_DELAY_MS` — so an explicit upstream signal is
|
|
59
|
+
* still granted more deference than our own guessed-at exponential
|
|
60
|
+
* schedule, while remaining bounded to a duration reasonable for a
|
|
61
|
+
* synchronous MCP tool call. (The reviewer described this figure as the
|
|
62
|
+
* `openai` SDK's own clamp; the SDK actually applies no such clamp — see
|
|
63
|
+
* `retryRequest`/`calculateDefaultRetryTimeoutMillis` in
|
|
64
|
+
* `openai/client.js`, which honours an unclamped `Retry-After` exactly as
|
|
65
|
+
* this module did before this fix. 60 s is picked on its own merits here,
|
|
66
|
+
* not inherited from upstream SDK behaviour.)
|
|
67
|
+
*
|
|
68
|
+
* Only the `sleep()` duration is clamped. `RateLimitedError.retryAfterMs`
|
|
69
|
+
* (surfaced to the caller on exhaustion, see `withRetry` below)
|
|
70
|
+
* deliberately keeps the TRUE, unclamped upstream value — that field's
|
|
71
|
+
* contract is "what did the upstream ask for" (e.g. so a UI layer can show
|
|
72
|
+
* "come back tomorrow"), not "how long did we actually wait".
|
|
73
|
+
*/
|
|
74
|
+
export const MAX_RETRY_AFTER_MS = 60_000;
|
|
75
|
+
/**
|
|
76
|
+
* Node socket / DNS error codes that indicate a transient network failure
|
|
77
|
+
* worth retrying (AC2), when found on a RAW node error that bypasses the
|
|
78
|
+
* openai SDK's own error classes entirely (see the fallback branch at the
|
|
79
|
+
* bottom of `classifyError`). Kept intentionally narrow.
|
|
80
|
+
*/
|
|
81
|
+
const RETRYABLE_NETWORK_CODES = new Set(["ECONNRESET", "ETIMEDOUT"]);
|
|
82
|
+
/**
|
|
83
|
+
* Node error codes that indicate a PERMANENT connection failure on an
|
|
84
|
+
* `APIConnectionError` — retrying won't help because the target
|
|
85
|
+
* fundamentally isn't reachable, as opposed to a transient peer reset or
|
|
86
|
+
* timeout (Copilot review on PR #126 / DRO-253). Used as a DENYLIST
|
|
87
|
+
* (exclude these, retry everything else) rather than an allowlist,
|
|
88
|
+
* deliberately:
|
|
89
|
+
*
|
|
90
|
+
* An earlier version of this fix tried the allowlist shape — only retry
|
|
91
|
+
* `APIConnectionError` when its cause code is in `RETRYABLE_NETWORK_CODES`
|
|
92
|
+
* — and it broke this file's OWN real-socket-destroy integration test
|
|
93
|
+
* (`recovers from a mid-flight socket destroy`, see `retry.test.ts`): a
|
|
94
|
+
* genuine peer-reset under Node's real `fetch`/undici stack surfaces as
|
|
95
|
+
* `SocketError` with code `UND_ERR_SOCKET`, one cause-level shallower than
|
|
96
|
+
* — and spelled entirely differently from — the `ECONNRESET` the mocked
|
|
97
|
+
* unit tests assume. An allowlist keyed on the wrong/incomplete code set
|
|
98
|
+
* would silently stop retrying the exact real-world failure this
|
|
99
|
+
* middleware exists to recover from. A denylist degrades safely instead:
|
|
100
|
+
* an unrecognised or future code still retries (matching this module's
|
|
101
|
+
* original, evidence-backed default), while the two well-understood
|
|
102
|
+
* permanent cases below stop burning the retry budget on a call that will
|
|
103
|
+
* never succeed:
|
|
104
|
+
* - `ECONNREFUSED` — nothing is listening at the target address; a
|
|
105
|
+
* retry a second later won't change that.
|
|
106
|
+
* - `ENOTFOUND` — DNS has no record for the hostname at all (distinct
|
|
107
|
+
* from `EAI_AGAIN`, "temporary failure in name resolution", which IS
|
|
108
|
+
* transient by definition and deliberately stays off this list).
|
|
109
|
+
*
|
|
110
|
+
* TLS handshake rejection is NOT enumerated here: unlike the two codes
|
|
111
|
+
* above, there's no single stable, version-independent error code to key
|
|
112
|
+
* on across Node/undici/OpenSSL versions — guessing one wrong would give
|
|
113
|
+
* false confidence rather than real protection. It falls through to the
|
|
114
|
+
* safe "retry by default" behaviour like any other unrecognised code.
|
|
115
|
+
*/
|
|
116
|
+
const PERMANENT_CONNECTION_CODES = new Set(["ECONNREFUSED", "ENOTFOUND"]);
|
|
117
|
+
// ─── Errors ──────────────────────────────────────────────────────────────
|
|
118
|
+
/**
|
|
119
|
+
* Thrown when a 429 rate-limit response was retried the full budget and the
|
|
120
|
+
* upstream never gave a non-429 answer (AC6). Kept distinct from
|
|
121
|
+
* `TransientError` so callers that want to surface `ERR_RATE_LIMITED`
|
|
122
|
+
* separately from generic-transient-failure can branch on the type — matches
|
|
123
|
+
* the FR-046 PRD note about surfacing rate-limits distinctly.
|
|
124
|
+
*
|
|
125
|
+
* `retryAfterMs` carries the last `Retry-After` value (in milliseconds) the
|
|
126
|
+
* upstream sent, so a UI layer can show "come back in ~30 s" without having
|
|
127
|
+
* to unwrap the cause itself. Undefined if the final response carried no
|
|
128
|
+
* `Retry-After` header.
|
|
129
|
+
*/
|
|
130
|
+
export class RateLimitedError extends Error {
|
|
131
|
+
attempts;
|
|
132
|
+
cause;
|
|
133
|
+
retryAfterMs;
|
|
134
|
+
name = "RateLimitedError";
|
|
135
|
+
constructor(message, attempts, cause, retryAfterMs) {
|
|
136
|
+
super(message, { cause });
|
|
137
|
+
this.attempts = attempts;
|
|
138
|
+
this.cause = cause;
|
|
139
|
+
this.retryAfterMs = retryAfterMs;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Thrown when a 5xx / network error was retried the full budget and the
|
|
144
|
+
* upstream never recovered (AC6). Callers that don't care about the
|
|
145
|
+
* distinction from `RateLimitedError` should catch `TransientError |
|
|
146
|
+
* RateLimitedError` as a union; both extend `Error` and both preserve the
|
|
147
|
+
* originating cause on `.cause`.
|
|
148
|
+
*/
|
|
149
|
+
export class TransientError extends Error {
|
|
150
|
+
attempts;
|
|
151
|
+
cause;
|
|
152
|
+
name = "TransientError";
|
|
153
|
+
constructor(message, attempts, cause) {
|
|
154
|
+
super(message, { cause });
|
|
155
|
+
this.attempts = attempts;
|
|
156
|
+
this.cause = cause;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// ─── Env resolution (AC5) ────────────────────────────────────────────────
|
|
160
|
+
/**
|
|
161
|
+
* Resolve `GENIE_LLM_RETRY_MAX`, falling back to `DEFAULT_RETRY_MAX` when
|
|
162
|
+
* unset, blank, non-numeric, or negative. Mirrors the established fallback
|
|
163
|
+
* shape of `resolveTimeoutMs` in `client.ts` and `getPlanTTL` in
|
|
164
|
+
* `plans/index.ts`.
|
|
165
|
+
*
|
|
166
|
+
* A concrete `"0"` is a legitimate operator choice — one attempt, no retries
|
|
167
|
+
* — and must NOT be collapsed with "unset". An operator running against a
|
|
168
|
+
* test lab where they own the retry policy externally sets 0 and expects
|
|
169
|
+
* exactly 1 attempt, not 4.
|
|
170
|
+
*
|
|
171
|
+
* Requires the trimmed value to be ENTIRELY digits (Copilot review on PR
|
|
172
|
+
* #126 / DRO-253): `parseInt` alone silently accepts a leading-numeric
|
|
173
|
+
* prefix — `parseInt("5s", 10)` and `parseInt("3.5", 10)` both parse as
|
|
174
|
+
* valid (5 and 3 respectively) despite neither being a clean integer,
|
|
175
|
+
* contradicting this function's own "non-numeric → default" contract and
|
|
176
|
+
* silently misconfiguring an operator who fat-fingered the env var (e.g.
|
|
177
|
+
* copy-pasted a value with a trailing unit). The regex guard rejects
|
|
178
|
+
* anything that isn't purely `[0-9]+` before `parseInt` ever sees it.
|
|
179
|
+
*/
|
|
180
|
+
export function resolveRetryMax(env = process.env) {
|
|
181
|
+
const raw = env[RETRY_MAX_ENV];
|
|
182
|
+
if (raw !== undefined) {
|
|
183
|
+
const trimmed = raw.trim();
|
|
184
|
+
if (/^\d+$/.test(trimmed)) {
|
|
185
|
+
const parsed = parseInt(trimmed, 10);
|
|
186
|
+
if (!isNaN(parsed) && parsed >= 0) {
|
|
187
|
+
return parsed;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return DEFAULT_RETRY_MAX;
|
|
192
|
+
}
|
|
193
|
+
// ─── Retry-After parsing (AC3) ───────────────────────────────────────────
|
|
194
|
+
/**
|
|
195
|
+
* Parse an HTTP `Retry-After` header (or the non-standard `retry-after-ms`)
|
|
196
|
+
* into milliseconds (AC3).
|
|
197
|
+
*
|
|
198
|
+
* Precedence:
|
|
199
|
+
* 1. `retry-after-ms` (millisecond precision, non-standard but common)
|
|
200
|
+
* 2. `retry-after` as fractional seconds (RFC 9110 delay-seconds form)
|
|
201
|
+
* 3. `retry-after` as HTTP-date (RFC 9110 date form) → delta from now,
|
|
202
|
+
* clamped to 0 if the date is in the past.
|
|
203
|
+
*
|
|
204
|
+
* Returns `null` for absent, empty, or unparseable values — the caller then
|
|
205
|
+
* falls back to the jittered exponential schedule (AC4).
|
|
206
|
+
*
|
|
207
|
+
* The two-level parse (seconds first, HTTP-date fallback) matches the openai
|
|
208
|
+
* SDK's own `retryRequest` logic in `openai/client.js` so anyone already
|
|
209
|
+
* relying on either header form gets consistent behaviour whether they hit
|
|
210
|
+
* this wrapper or the SDK's own internal retry.
|
|
211
|
+
*/
|
|
212
|
+
export function parseRetryAfter(headers) {
|
|
213
|
+
if (!headers)
|
|
214
|
+
return null;
|
|
215
|
+
// 1. Millisecond-precision (non-standard, but the openai SDK recognises it).
|
|
216
|
+
const ms = headers.get("retry-after-ms");
|
|
217
|
+
if (ms) {
|
|
218
|
+
const parsed = parseFloat(ms);
|
|
219
|
+
if (!Number.isNaN(parsed) && parsed >= 0) {
|
|
220
|
+
return parsed;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// 2/3. Standard `retry-after`.
|
|
224
|
+
const raw = headers.get("retry-after");
|
|
225
|
+
if (!raw)
|
|
226
|
+
return null;
|
|
227
|
+
// Try delay-seconds form first — RFC 9110 §10.2.3 permits an integer
|
|
228
|
+
// (technically the ABNF is non-negative-integer, but many servers send
|
|
229
|
+
// fractional values and the openai SDK accepts them via parseFloat, so we
|
|
230
|
+
// match that lenient posture).
|
|
231
|
+
//
|
|
232
|
+
// Use a regex to distinguish "genuinely numeric" from "starts with a
|
|
233
|
+
// number then something else": `parseFloat("Wed 21 Oct")` returns
|
|
234
|
+
// `NaN`, but `parseFloat("21 Oct 2025")` returns `21`, which would
|
|
235
|
+
// silently interpret an HTTP-date as "21 seconds" if the two branches
|
|
236
|
+
// weren't strictly ordered. Round-trip stringification (`` `${n}` ===
|
|
237
|
+
// raw ``) is subtly wrong for values like `"7.0"` → `parseFloat` → `7`
|
|
238
|
+
// → `"7"`, which doesn't match; the regex sidesteps that whole class of
|
|
239
|
+
// bug by only recognising strings that ARE the numeric literal.
|
|
240
|
+
const trimmed = raw.trim();
|
|
241
|
+
if (/^\d+(\.\d+)?$/.test(trimmed)) {
|
|
242
|
+
const seconds = parseFloat(trimmed);
|
|
243
|
+
if (!Number.isNaN(seconds)) {
|
|
244
|
+
return Math.max(0, seconds * 1_000);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
// Fall back to HTTP-date form.
|
|
248
|
+
const dateMs = Date.parse(raw);
|
|
249
|
+
if (!Number.isNaN(dateMs)) {
|
|
250
|
+
return Math.max(0, dateMs - Date.now());
|
|
251
|
+
}
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
// ─── Jittered exponential backoff (AC4) ──────────────────────────────────
|
|
255
|
+
/**
|
|
256
|
+
* Compute the sleep duration for the given retry `attempt` (1-indexed —
|
|
257
|
+
* `attempt=1` is the delay before the first retry, i.e. after the initial
|
|
258
|
+
* attempt failed). Base 1 s, doubles per attempt, capped at 30 s, then a
|
|
259
|
+
* uniform ±20 % jitter applied on top (AC4).
|
|
260
|
+
*
|
|
261
|
+
* The cap is applied to the exponential value BEFORE the jitter multiplier,
|
|
262
|
+
* so at high attempt counts the delay is still bounded by
|
|
263
|
+
* `MAX_DELAY_MS * (1 + JITTER_RATIO)` on paper — but then clamped back to
|
|
264
|
+
* `MAX_DELAY_MS` a second time, so the effective ceiling is exactly 30 s
|
|
265
|
+
* (a strict interpretation of "cap 30 s" in the spec).
|
|
266
|
+
*
|
|
267
|
+
* `random` is injectable so tests can drive deterministic values without
|
|
268
|
+
* needing to stub `Math.random` globally.
|
|
269
|
+
*/
|
|
270
|
+
export function jitteredBackoff(attempt, random = Math.random) {
|
|
271
|
+
const exponential = BASE_DELAY_MS * Math.pow(2, attempt - 1);
|
|
272
|
+
const capped = Math.min(exponential, MAX_DELAY_MS);
|
|
273
|
+
const jitterMultiplier = 1 - JITTER_RATIO + random() * (JITTER_RATIO * 2);
|
|
274
|
+
return Math.min(Math.round(capped * jitterMultiplier), MAX_DELAY_MS);
|
|
275
|
+
}
|
|
276
|
+
// ─── Retryability discrimination (AC2) ───────────────────────────────────
|
|
277
|
+
/**
|
|
278
|
+
* Walk `error.cause` down to `MAX_DEPTH` levels, looking for a node-style
|
|
279
|
+
* `.code` property (`ECONNRESET`, `ETIMEDOUT`, …). Global `fetch` wraps a
|
|
280
|
+
* socket-reset root cause as `TypeError('fetch failed', { cause: rootErr })`
|
|
281
|
+
* and the openai SDK re-wraps that as `APIConnectionError`, meaning the real
|
|
282
|
+
* code lives at `err.cause.cause.code` — two levels below where a naive
|
|
283
|
+
* `err.code` check would look. Depth-limited to prevent a pathological
|
|
284
|
+
* self-referential cause chain from looping.
|
|
285
|
+
*/
|
|
286
|
+
function findCauseCode(error) {
|
|
287
|
+
const MAX_DEPTH = 5;
|
|
288
|
+
let current = error;
|
|
289
|
+
for (let i = 0; i < MAX_DEPTH && current instanceof Error; i++) {
|
|
290
|
+
const code = current.code;
|
|
291
|
+
if (typeof code === "string")
|
|
292
|
+
return code;
|
|
293
|
+
current = current.cause;
|
|
294
|
+
}
|
|
295
|
+
return undefined;
|
|
296
|
+
}
|
|
297
|
+
function classifyError(error) {
|
|
298
|
+
if (error instanceof RateLimitError)
|
|
299
|
+
return "rateLimit";
|
|
300
|
+
if (error instanceof InternalServerError)
|
|
301
|
+
return "transient";
|
|
302
|
+
// Checked BEFORE the general APIConnectionError branch below because
|
|
303
|
+
// APIConnectionTimeoutError extends APIConnectionError — a client-side
|
|
304
|
+
// timeout has no wire-response context to inspect a cause code from, and
|
|
305
|
+
// maps directly to AC2's "ETIMEDOUT" bucket, so it's unconditionally
|
|
306
|
+
// retryable without needing the narrower cause-code check the plain
|
|
307
|
+
// APIConnectionError branch below applies.
|
|
308
|
+
if (error instanceof APIConnectionTimeoutError)
|
|
309
|
+
return "transient";
|
|
310
|
+
if (error instanceof APIConnectionError) {
|
|
311
|
+
// Deny-list, not allow-list (Copilot review on PR #126 / DRO-253 asked
|
|
312
|
+
// for *some* narrowing here — see `PERMANENT_CONNECTION_CODES`'s
|
|
313
|
+
// docstring for why an allowlist keyed on `RETRYABLE_NETWORK_CODES`
|
|
314
|
+
// broke this file's own real-socket-destroy integration test). Retry
|
|
315
|
+
// by default — matching this module's original, evidence-backed
|
|
316
|
+
// stance that the SDK only raises this class for wire-level failures
|
|
317
|
+
// where the request never reached a productive response — UNLESS the
|
|
318
|
+
// cause chain names one of the two well-understood permanent codes,
|
|
319
|
+
// in which case stop burning the retry budget on a call that will
|
|
320
|
+
// never succeed.
|
|
321
|
+
const networkCode = findCauseCode(error);
|
|
322
|
+
return networkCode && PERMANENT_CONNECTION_CODES.has(networkCode) ? "nonRetryable" : "transient";
|
|
323
|
+
}
|
|
324
|
+
// Non-openai APIError with a 5xx status (defensive — future SDK versions
|
|
325
|
+
// may add subclasses we haven't enumerated).
|
|
326
|
+
if (error instanceof APIError && typeof error.status === "number" && error.status >= 500) {
|
|
327
|
+
return "transient";
|
|
328
|
+
}
|
|
329
|
+
// Deep-cause network code check — catches raw node errors that bypass the
|
|
330
|
+
// openai SDK entirely (e.g. a pre-request rejection from a custom fetch).
|
|
331
|
+
const code = findCauseCode(error);
|
|
332
|
+
if (code && RETRYABLE_NETWORK_CODES.has(code))
|
|
333
|
+
return "transient";
|
|
334
|
+
return "nonRetryable";
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Pull an HTTP status out of an openai APIError for logging. Non-openai
|
|
338
|
+
* errors and network-connection errors don't have one, so return null (AC7).
|
|
339
|
+
*/
|
|
340
|
+
function statusOf(error) {
|
|
341
|
+
if (error instanceof APIError) {
|
|
342
|
+
const status = error.status;
|
|
343
|
+
return typeof status === "number" ? status : null;
|
|
344
|
+
}
|
|
345
|
+
return null;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Pull `Retry-After` out of an openai APIError's response headers (AC3).
|
|
349
|
+
* Only present on errors carrying wire-response context (i.e. not on
|
|
350
|
+
* `APIConnectionError` / `APIConnectionTimeoutError`, which never received
|
|
351
|
+
* a response). Returns null when absent or unparseable.
|
|
352
|
+
*/
|
|
353
|
+
function retryAfterOf(error) {
|
|
354
|
+
if (error instanceof APIError && error.headers) {
|
|
355
|
+
return parseRetryAfter(error.headers);
|
|
356
|
+
}
|
|
357
|
+
return null;
|
|
358
|
+
}
|
|
359
|
+
// ─── Structured audit log (AC7) ──────────────────────────────────────────
|
|
360
|
+
/**
|
|
361
|
+
* Emit one `llm.retry` audit line per retry attempt to stderr — never
|
|
362
|
+
* stdout. On the stdio MCP transport stdout IS the JSON-RPC protocol
|
|
363
|
+
* stream, and one stray line there corrupts every client's message framing
|
|
364
|
+
* (see the same-convention comment in `client.ts` on the sibling
|
|
365
|
+
* `llm.chat_completion` log).
|
|
366
|
+
*
|
|
367
|
+
* Includes `attempt` (1-indexed, matches AC7's contract), `status` (HTTP
|
|
368
|
+
* status if the error was an APIError; null for network failures), and
|
|
369
|
+
* `retryAfter` (milliseconds honoured, null if none). Deliberately does NOT
|
|
370
|
+
* include the request body, prompt, or completion text — the log is what an
|
|
371
|
+
* operator inspects afterwards, and leaking prompt content across every
|
|
372
|
+
* transient retry would be a real audit-log data leak.
|
|
373
|
+
*/
|
|
374
|
+
function logRetry(attempt, status, retryAfterMs) {
|
|
375
|
+
const line = JSON.stringify({
|
|
376
|
+
event: "llm.retry",
|
|
377
|
+
attempt,
|
|
378
|
+
status,
|
|
379
|
+
retryAfter: retryAfterMs,
|
|
380
|
+
});
|
|
381
|
+
process.stderr.write(line + "\n");
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Default `setTimeout`-backed sleep. Extracted so both the default option
|
|
385
|
+
* value and any explicit `sleep` injection route through a single named
|
|
386
|
+
* function — makes stack traces in fake-timer tests self-explanatory.
|
|
387
|
+
*/
|
|
388
|
+
function defaultSleep(ms) {
|
|
389
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Wrap an async LLM handler with retry/backoff (AC1). Returns a function
|
|
393
|
+
* with the SAME argument list and return type as the input handler, so
|
|
394
|
+
* `withRetry(createChatCompletion)` is a drop-in replacement for
|
|
395
|
+
* `createChatCompletion` at every call site.
|
|
396
|
+
*
|
|
397
|
+
* Order of operations per attempt:
|
|
398
|
+
* 1. Call the handler.
|
|
399
|
+
* 2. On success → return the result immediately.
|
|
400
|
+
* 3. On failure → classify (AC2). Non-retryable errors propagate as-is
|
|
401
|
+
* (the original error object, not a wrapped one — a caller that
|
|
402
|
+
* already has instanceof/status handling for BadRequestError et al.
|
|
403
|
+
* keeps working unchanged).
|
|
404
|
+
* 4. On retryable failure with remaining budget → emit an `llm.retry`
|
|
405
|
+
* log line (AC7), sleep for `Retry-After` (AC3) or the jittered
|
|
406
|
+
* backoff (AC4), then loop.
|
|
407
|
+
* 5. On retryable failure with exhausted budget → throw
|
|
408
|
+
* `RateLimitedError` (if the tail-cause was a 429) or `TransientError`
|
|
409
|
+
* (otherwise), preserving the original error on `.cause` (AC6).
|
|
410
|
+
*
|
|
411
|
+
* A handler that transitions from one retryable kind to another across
|
|
412
|
+
* attempts (say 429 → 503 → 500) throws whichever kind matches the LAST
|
|
413
|
+
* failing attempt, on the reasoning that "what the caller sees now" is the
|
|
414
|
+
* most useful discriminator for their fallback branch — a 429-turned-503
|
|
415
|
+
* indicates a genuinely degraded upstream, not a persistent rate-limit.
|
|
416
|
+
*/
|
|
417
|
+
export function withRetry(handler, opts = {}) {
|
|
418
|
+
const sleep = opts.sleep ?? defaultSleep;
|
|
419
|
+
const random = opts.random ?? Math.random;
|
|
420
|
+
const maxRetries = opts.maxRetries ?? resolveRetryMax(opts.env ?? process.env);
|
|
421
|
+
return async (...args) => {
|
|
422
|
+
let lastError;
|
|
423
|
+
// attempt 0 is the initial attempt; attempts 1..maxRetries are retries.
|
|
424
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
425
|
+
try {
|
|
426
|
+
return await handler(...args);
|
|
427
|
+
}
|
|
428
|
+
catch (error) {
|
|
429
|
+
lastError = error;
|
|
430
|
+
const kind = classifyError(error);
|
|
431
|
+
if (kind === "nonRetryable") {
|
|
432
|
+
throw error;
|
|
433
|
+
}
|
|
434
|
+
const remainingRetries = maxRetries - attempt;
|
|
435
|
+
if (remainingRetries <= 0) {
|
|
436
|
+
// Budget exhausted — surface a typed error. Kind is derived from
|
|
437
|
+
// the LAST failing attempt, on the reasoning that "what the
|
|
438
|
+
// caller sees now" is the most useful discriminator (see the
|
|
439
|
+
// JSDoc above).
|
|
440
|
+
const err = error;
|
|
441
|
+
const attemptsTotal = attempt + 1;
|
|
442
|
+
if (kind === "rateLimit") {
|
|
443
|
+
const retryAfterMs = retryAfterOf(error);
|
|
444
|
+
throw new RateLimitedError(`LLM call exhausted ${attemptsTotal} attempts on rate-limit (429).`, attemptsTotal, err, retryAfterMs ?? undefined);
|
|
445
|
+
}
|
|
446
|
+
throw new TransientError(`LLM call exhausted ${attemptsTotal} attempts on transient failure.`, attemptsTotal, err);
|
|
447
|
+
}
|
|
448
|
+
// Retry — log, sleep, loop.
|
|
449
|
+
const status = statusOf(error);
|
|
450
|
+
const retryAfterMs = retryAfterOf(error);
|
|
451
|
+
// 1-indexed attempt number for the audit log (attempt=1 is the
|
|
452
|
+
// first retry, i.e. the second call overall). Logged verbatim
|
|
453
|
+
// (unclamped) — the log's `retryAfter` field records what the
|
|
454
|
+
// upstream actually asked for, same contract as
|
|
455
|
+
// `RateLimitedError.retryAfterMs` below.
|
|
456
|
+
logRetry(attempt + 1, status, retryAfterMs);
|
|
457
|
+
// Clamp only the value actually slept on (MAX_RETRY_AFTER_MS):
|
|
458
|
+
// `retryAfterMs` is trusted verbatim from the upstream response, so
|
|
459
|
+
// an unclamped sleep here would let a misconfigured or hostile
|
|
460
|
+
// upstream (e.g. `Retry-After: 86400`) stall this call — and, on
|
|
461
|
+
// the stdio MCP transport, the whole in-flight request — for that
|
|
462
|
+
// full duration. `jitteredBackoff` is already self-capping at
|
|
463
|
+
// MAX_DELAY_MS, so the clamp below is a no-op on that branch.
|
|
464
|
+
const delayMs = Math.min(retryAfterMs ?? jitteredBackoff(attempt + 1, random), MAX_RETRY_AFTER_MS);
|
|
465
|
+
await sleep(delayMs);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
// Unreachable: the loop either returns on success or throws on the
|
|
469
|
+
// budget-exhaustion branch. TypeScript wants an explicit throw here
|
|
470
|
+
// because it can't prove the loop always terminates via one of those
|
|
471
|
+
// two branches — `throw` on the final `lastError` closes the type.
|
|
472
|
+
throw lastError;
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/llm/retry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,OAAO,EACL,kBAAkB,EAClB,yBAAyB,EACzB,QAAQ,EACR,mBAAmB,EACnB,cAAc,GACf,MAAM,QAAQ,CAAC;AAEhB,4EAA4E;AAE5E,8DAA8D;AAC9D,MAAM,CAAC,MAAM,aAAa,GAAG,qBAAqB,CAAC;AACnD,uEAAuE;AACvE,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC;AACnC,qCAAqC;AACrC,MAAM,CAAC,MAAM,aAAa,GAAG,KAAK,CAAC;AACnC,+BAA+B;AAC/B,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC;AACnC,yFAAyF;AACzF,MAAM,CAAC,MAAM,YAAY,GAAG,GAAG,CAAC;AAEhC;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAEzC;;;;;GAKG;AACH,MAAM,uBAAuB,GAAwB,IAAI,GAAG,CAAC,CAAC,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC;AAE1F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,0BAA0B,GAAwB,IAAI,GAAG,CAAC,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC,CAAC;AAE/F,4EAA4E;AAE5E;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAIvB;IACA;IACA;IALT,IAAI,GAAG,kBAAkB,CAAC;IACnC,YACE,OAAe,EACC,QAAgB,EAChB,KAAY,EACZ,YAAqB;QAErC,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAJV,aAAQ,GAAR,QAAQ,CAAQ;QAChB,UAAK,GAAL,KAAK,CAAO;QACZ,iBAAY,GAAZ,YAAY,CAAS;IAGvC,CAAC;CACF;AAED;;;;;;GAMG;AACH,MAAM,OAAO,cAAe,SAAQ,KAAK;IAIrB;IACA;IAJT,IAAI,GAAG,gBAAgB,CAAC;IACjC,YACE,OAAe,EACC,QAAgB,EAChB,KAAY;QAE5B,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;QAHV,aAAQ,GAAR,QAAQ,CAAQ;QAChB,UAAK,GAAL,KAAK,CAAO;IAG9B,CAAC;CACF;AAED,4EAA4E;AAE5E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,eAAe,CAAC,MAAyB,OAAO,CAAC,GAAG;IAClE,MAAM,GAAG,GAAG,GAAG,CAAC,aAAa,CAAC,CAAC;IAC/B,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;QAC3B,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAC1B,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YACrC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;gBAClC,OAAO,MAAM,CAAC;YAChB,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,iBAAiB,CAAC;AAC3B,CAAC;AAED,4EAA4E;AAE5E;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,eAAe,CAAC,OAA4B;IAC1D,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAE1B,6EAA6E;IAC7E,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IACzC,IAAI,EAAE,EAAE,CAAC;QACP,MAAM,MAAM,GAAG,UAAU,CAAC,EAAE,CAAC,CAAC;QAC9B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;YACzC,OAAO,MAAM,CAAC;QAChB,CAAC;IACH,CAAC;IAED,+BAA+B;IAC/B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IACvC,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IAEtB,qEAAqE;IACrE,uEAAuE;IACvE,0EAA0E;IAC1E,+BAA+B;IAC/B,EAAE;IACF,qEAAqE;IACrE,kEAAkE;IAClE,mEAAmE;IACnE,sEAAsE;IACtE,sEAAsE;IACtE,uEAAuE;IACvE,wEAAwE;IACxE,gEAAgE;IAChE,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;IAC3B,IAAI,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAClC,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;QACpC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3B,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,KAAK,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAED,+BAA+B;IAC/B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,4EAA4E;AAE5E;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,eAAe,CAAC,OAAe,EAAE,SAAuB,IAAI,CAAC,MAAM;IACjF,MAAM,WAAW,GAAG,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;IACnD,MAAM,gBAAgB,GAAG,CAAC,GAAG,YAAY,GAAG,MAAM,EAAE,GAAG,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC;IAC1E,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,gBAAgB,CAAC,EAAE,YAAY,CAAC,CAAC;AACvE,CAAC;AAED,4EAA4E;AAE5E;;;;;;;;GAQG;AACH,SAAS,aAAa,CAAC,KAAc;IACnC,MAAM,SAAS,GAAG,CAAC,CAAC;IACpB,IAAI,OAAO,GAAY,KAAK,CAAC;IAC7B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,IAAI,OAAO,YAAY,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/D,MAAM,IAAI,GAAI,OAA8B,CAAC,IAAI,CAAC;QAClD,IAAI,OAAO,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QAC1C,OAAO,GAAI,OAA+B,CAAC,KAAK,CAAC;IACnD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAYD,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,KAAK,YAAY,cAAc;QAAE,OAAO,WAAW,CAAC;IACxD,IAAI,KAAK,YAAY,mBAAmB;QAAE,OAAO,WAAW,CAAC;IAC7D,qEAAqE;IACrE,uEAAuE;IACvE,yEAAyE;IACzE,qEAAqE;IACrE,oEAAoE;IACpE,2CAA2C;IAC3C,IAAI,KAAK,YAAY,yBAAyB;QAAE,OAAO,WAAW,CAAC;IACnE,IAAI,KAAK,YAAY,kBAAkB,EAAE,CAAC;QACxC,uEAAuE;QACvE,iEAAiE;QACjE,oEAAoE;QACpE,qEAAqE;QACrE,gEAAgE;QAChE,qEAAqE;QACrE,qEAAqE;QACrE,oEAAoE;QACpE,kEAAkE;QAClE,iBAAiB;QACjB,MAAM,WAAW,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;QACzC,OAAO,WAAW,IAAI,0BAA0B,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,WAAW,CAAC;IACnG,CAAC;IACD,yEAAyE;IACzE,6CAA6C;IAC7C,IAAI,KAAK,YAAY,QAAQ,IAAI,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;QACzF,OAAO,WAAW,CAAC;IACrB,CAAC;IACD,0EAA0E;IAC1E,0EAA0E;IAC1E,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAClC,IAAI,IAAI,IAAI,uBAAuB,CAAC,GAAG,CAAC,IAAI,CAAC;QAAE,OAAO,WAAW,CAAC;IAClE,OAAO,cAAc,CAAC;AACxB,CAAC;AAED;;;GAGG;AACH,SAAS,QAAQ,CAAC,KAAc;IAC9B,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;QAC9B,MAAM,MAAM,GAAI,KAAkB,CAAC,MAAM,CAAC;QAC1C,OAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;IACpD,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,SAAS,YAAY,CAAC,KAAc;IAClC,IAAI,KAAK,YAAY,QAAQ,IAAK,KAAkB,CAAC,OAAO,EAAE,CAAC;QAC7D,OAAO,eAAe,CAAE,KAAkB,CAAC,OAAkB,CAAC,CAAC;IACjE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,4EAA4E;AAE5E;;;;;;;;;;;;;GAaG;AACH,SAAS,QAAQ,CAAC,OAAe,EAAE,MAAqB,EAAE,YAA2B;IACnF,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC;QAC1B,KAAK,EAAE,WAAW;QAClB,OAAO;QACP,MAAM;QACN,UAAU,EAAE,YAAY;KACzB,CAAC,CAAC;IACH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;AACpC,CAAC;AAkCD;;;;GAIG;AACH,SAAS,YAAY,CAAC,EAAU;IAC9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,SAAS,CACvB,OAAmC,EACnC,OAAyB,EAAE;IAE3B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,YAAY,CAAC;IACzC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC;IAC1C,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,eAAe,CAAC,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC;IAE/E,OAAO,KAAK,EAAE,GAAG,IAAO,EAAc,EAAE;QACtC,IAAI,SAAkB,CAAC;QACvB,wEAAwE;QACxE,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;YACvD,IAAI,CAAC;gBACH,OAAO,MAAM,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;YAChC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,SAAS,GAAG,KAAK,CAAC;gBAClB,MAAM,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;gBAClC,IAAI,IAAI,KAAK,cAAc,EAAE,CAAC;oBAC5B,MAAM,KAAK,CAAC;gBACd,CAAC;gBACD,MAAM,gBAAgB,GAAG,UAAU,GAAG,OAAO,CAAC;gBAC9C,IAAI,gBAAgB,IAAI,CAAC,EAAE,CAAC;oBAC1B,iEAAiE;oBACjE,4DAA4D;oBAC5D,6DAA6D;oBAC7D,gBAAgB;oBAChB,MAAM,GAAG,GAAG,KAAc,CAAC;oBAC3B,MAAM,aAAa,GAAG,OAAO,GAAG,CAAC,CAAC;oBAClC,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;wBACzB,MAAM,YAAY,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;wBACzC,MAAM,IAAI,gBAAgB,CACxB,sBAAsB,aAAa,gCAAgC,EACnE,aAAa,EACb,GAAG,EACH,YAAY,IAAI,SAAS,CAC1B,CAAC;oBACJ,CAAC;oBACD,MAAM,IAAI,cAAc,CACtB,sBAAsB,aAAa,iCAAiC,EACpE,aAAa,EACb,GAAG,CACJ,CAAC;gBACJ,CAAC;gBACD,4BAA4B;gBAC5B,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;gBAC/B,MAAM,YAAY,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;gBACzC,+DAA+D;gBAC/D,8DAA8D;gBAC9D,8DAA8D;gBAC9D,gDAAgD;gBAChD,yCAAyC;gBACzC,QAAQ,CAAC,OAAO,GAAG,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC;gBAC5C,+DAA+D;gBAC/D,oEAAoE;gBACpE,+DAA+D;gBAC/D,iEAAiE;gBACjE,kEAAkE;gBAClE,8DAA8D;gBAC9D,8DAA8D;gBAC9D,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CACtB,YAAY,IAAI,eAAe,CAAC,OAAO,GAAG,CAAC,EAAE,MAAM,CAAC,EACpD,kBAAkB,CACnB,CAAC;gBACF,MAAM,KAAK,CAAC,OAAO,CAAC,CAAC;YACvB,CAAC;QACH,CAAC;QACD,mEAAmE;QACnE,oEAAoE;QACpE,qEAAqE;QACrE,mEAAmE;QACnE,MAAM,SAAkB,CAAC;IAC3B,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `COMPONENT_SCHEMA` (M2-02) — the JSON Schema `conjure`/`refine` (M2-03/M2-04)
|
|
3
|
+
* demand from the model via `response_format: { type: "json_schema",
|
|
4
|
+
* json_schema: COMPONENT_SCHEMA }`, and that M2-07's Ajv validation pass
|
|
5
|
+
* (`validateComponent`) compiles against.
|
|
6
|
+
*
|
|
7
|
+
* Draft 7 only (AC2): no `anyOf`/`oneOf` discriminator pattern, and the only
|
|
8
|
+
* `$ref` in the whole schema is a single level deep (`manifestEntry.viewport`
|
|
9
|
+
* → `#/definitions/Viewport`, mirroring the RFC §9.20 shared-`$defs` shape).
|
|
10
|
+
* LiteLLM's structured-output passthrough to Anthropic (via tool-use) is the
|
|
11
|
+
* reason to keep nesting shallow — a deep `$ref` chain is exactly the shape
|
|
12
|
+
* that passthrough has been observed not to handle reliably.
|
|
13
|
+
*
|
|
14
|
+
* `as const satisfies JSONSchema7` (AC1) does two jobs at once: `satisfies`
|
|
15
|
+
* runs the object through `@types/json-schema`'s `JSONSchema7` shape so a
|
|
16
|
+
* typo'd keyword (e.g. `additionalProperty` instead of `additionalProperties`)
|
|
17
|
+
* is a compile error, while `as const` preserves the literal types `FromSchema`
|
|
18
|
+
* (AC7) needs to actually infer `ValidatedComponent` below rather than widening
|
|
19
|
+
* every string field to `string`.
|
|
20
|
+
*/
|
|
21
|
+
import type { FromSchema } from "json-schema-to-ts";
|
|
22
|
+
export declare const COMPONENT_SCHEMA: {
|
|
23
|
+
readonly $schema: "http://json-schema.org/draft-07/schema#";
|
|
24
|
+
readonly $id: "https://genie.dev/schema/component.schema.json";
|
|
25
|
+
readonly title: "GenieComponent";
|
|
26
|
+
readonly description: string;
|
|
27
|
+
readonly type: "object";
|
|
28
|
+
readonly additionalProperties: false;
|
|
29
|
+
readonly required: ["componentName", "group", "files", "manifestEntry"];
|
|
30
|
+
readonly definitions: {
|
|
31
|
+
readonly Viewport: {
|
|
32
|
+
readonly type: "object";
|
|
33
|
+
readonly additionalProperties: false;
|
|
34
|
+
readonly required: ["width", "height"];
|
|
35
|
+
readonly properties: {
|
|
36
|
+
readonly width: {
|
|
37
|
+
readonly type: "integer";
|
|
38
|
+
readonly minimum: 1;
|
|
39
|
+
readonly maximum: 4096;
|
|
40
|
+
};
|
|
41
|
+
readonly height: {
|
|
42
|
+
readonly type: "integer";
|
|
43
|
+
readonly minimum: 1;
|
|
44
|
+
readonly maximum: 4096;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
readonly properties: {
|
|
50
|
+
readonly componentName: {
|
|
51
|
+
readonly type: "string";
|
|
52
|
+
readonly description: "PascalCase component name, e.g. \"Button\".";
|
|
53
|
+
readonly pattern: "^[A-Z][A-Za-z0-9]{1,63}$";
|
|
54
|
+
};
|
|
55
|
+
readonly group: {
|
|
56
|
+
readonly type: "string";
|
|
57
|
+
readonly description: "Kebab-case UI-kit group/category, e.g. \"actions\".";
|
|
58
|
+
readonly pattern: "^[a-z0-9-]{1,32}$";
|
|
59
|
+
};
|
|
60
|
+
readonly files: {
|
|
61
|
+
readonly type: "array";
|
|
62
|
+
readonly description: string;
|
|
63
|
+
readonly minItems: 1;
|
|
64
|
+
readonly maxItems: 12;
|
|
65
|
+
readonly contains: {
|
|
66
|
+
readonly type: "object";
|
|
67
|
+
readonly properties: {
|
|
68
|
+
readonly path: {
|
|
69
|
+
readonly type: "string";
|
|
70
|
+
readonly pattern: "^components/[a-z0-9-]+/([A-Z][A-Za-z0-9]{1,63})/\\1\\.html$";
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
readonly items: {
|
|
75
|
+
readonly type: "object";
|
|
76
|
+
readonly additionalProperties: false;
|
|
77
|
+
readonly required: ["path", "content", "mimeType"];
|
|
78
|
+
readonly properties: {
|
|
79
|
+
readonly path: {
|
|
80
|
+
readonly type: "string";
|
|
81
|
+
readonly description: "Kit-relative path under components/<group>/<Name>/.";
|
|
82
|
+
readonly pattern: "^components/[a-z0-9-]+/[A-Z][A-Za-z0-9]+/[A-Za-z0-9._-]+$";
|
|
83
|
+
};
|
|
84
|
+
readonly content: {
|
|
85
|
+
readonly type: "string";
|
|
86
|
+
readonly description: string;
|
|
87
|
+
readonly minLength: 1;
|
|
88
|
+
readonly maxLength: 65536;
|
|
89
|
+
};
|
|
90
|
+
readonly mimeType: {
|
|
91
|
+
readonly type: "string";
|
|
92
|
+
readonly description: "MIME type of `content`, e.g. \"text/html\".";
|
|
93
|
+
readonly pattern: "^[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]*/[A-Za-z0-9][A-Za-z0-9!#$&^_.+-]*$";
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
readonly manifestEntry: {
|
|
99
|
+
readonly type: "object";
|
|
100
|
+
readonly description: string;
|
|
101
|
+
readonly additionalProperties: false;
|
|
102
|
+
readonly required: ["viewport"];
|
|
103
|
+
readonly properties: {
|
|
104
|
+
readonly viewport: {
|
|
105
|
+
readonly $ref: "#/definitions/Viewport";
|
|
106
|
+
};
|
|
107
|
+
readonly subtitle: {
|
|
108
|
+
readonly type: "string";
|
|
109
|
+
readonly maxLength: 256;
|
|
110
|
+
};
|
|
111
|
+
readonly tags: {
|
|
112
|
+
readonly type: "array";
|
|
113
|
+
readonly items: {
|
|
114
|
+
readonly type: "string";
|
|
115
|
+
};
|
|
116
|
+
readonly maxItems: 16;
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* AC7 — TypeScript type inferred from {@link COMPONENT_SCHEMA} via
|
|
124
|
+
* `json-schema-to-ts`, so the schema (not a hand-maintained interface) is the
|
|
125
|
+
* single source of truth `conjure`/`refine`/`validateComponent` (M2-03/04/07)
|
|
126
|
+
* import against.
|
|
127
|
+
*/
|
|
128
|
+
export type ValidatedComponent = FromSchema<typeof COMPONENT_SCHEMA>;
|
|
129
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/llm/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAwCpD,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6FG,CAAC;AAEjC;;;;;GAKG;AACH,MAAM,MAAM,kBAAkB,GAAG,UAAU,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
|