@data-club/ai-hub-server 0.0.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/dist/auth/argon2.d.ts +72 -0
- package/dist/auth/argon2.d.ts.map +1 -0
- package/dist/auth/argon2.js +145 -0
- package/dist/auth/argon2.js.map +1 -0
- package/dist/auth/encryption.d.ts +52 -0
- package/dist/auth/encryption.d.ts.map +1 -0
- package/dist/auth/encryption.js +98 -0
- package/dist/auth/encryption.js.map +1 -0
- package/dist/auth/jwt.d.ts +133 -0
- package/dist/auth/jwt.d.ts.map +1 -0
- package/dist/auth/jwt.js +147 -0
- package/dist/auth/jwt.js.map +1 -0
- package/dist/auth/middleware.d.ts +101 -0
- package/dist/auth/middleware.d.ts.map +1 -0
- package/dist/auth/middleware.js +161 -0
- package/dist/auth/middleware.js.map +1 -0
- package/dist/auth/passwordPolicy.d.ts +72 -0
- package/dist/auth/passwordPolicy.d.ts.map +1 -0
- package/dist/auth/passwordPolicy.js +88 -0
- package/dist/auth/passwordPolicy.js.map +1 -0
- package/dist/auth/roleRules.d.ts +200 -0
- package/dist/auth/roleRules.d.ts.map +1 -0
- package/dist/auth/roleRules.js +365 -0
- package/dist/auth/roleRules.js.map +1 -0
- package/dist/auth/userView.d.ts +55 -0
- package/dist/auth/userView.d.ts.map +1 -0
- package/dist/auth/userView.js +77 -0
- package/dist/auth/userView.js.map +1 -0
- package/dist/bootstrap.d.ts +104 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +160 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/db/connection.d.ts +39 -0
- package/dist/db/connection.d.ts.map +1 -0
- package/dist/db/connection.js +62 -0
- package/dist/db/connection.js.map +1 -0
- package/dist/db/migrate.d.ts +31 -0
- package/dist/db/migrate.d.ts.map +1 -0
- package/dist/db/migrate.js +197 -0
- package/dist/db/migrate.js.map +1 -0
- package/dist/db/schema.d.ts +1902 -0
- package/dist/db/schema.d.ts.map +1 -0
- package/dist/db/schema.js +662 -0
- package/dist/db/schema.js.map +1 -0
- package/dist/db/seed.d.ts +92 -0
- package/dist/db/seed.d.ts.map +1 -0
- package/dist/db/seed.js +160 -0
- package/dist/db/seed.js.map +1 -0
- package/dist/handlers/fetch.d.ts +89 -0
- package/dist/handlers/fetch.d.ts.map +1 -0
- package/dist/handlers/fetch.js +173 -0
- package/dist/handlers/fetch.js.map +1 -0
- package/dist/handlers/nextjs.d.ts +40 -0
- package/dist/handlers/nextjs.d.ts.map +1 -0
- package/dist/handlers/nextjs.js +42 -0
- package/dist/handlers/nextjs.js.map +1 -0
- package/dist/handlers/responses.d.ts +46 -0
- package/dist/handlers/responses.d.ts.map +1 -0
- package/dist/handlers/responses.js +74 -0
- package/dist/handlers/responses.js.map +1 -0
- package/dist/handlers/routeTable.d.ts +21 -0
- package/dist/handlers/routeTable.d.ts.map +1 -0
- package/dist/handlers/routeTable.js +133 -0
- package/dist/handlers/routeTable.js.map +1 -0
- package/dist/handlers/router.d.ts +37 -0
- package/dist/handlers/router.d.ts.map +1 -0
- package/dist/handlers/router.js +82 -0
- package/dist/handlers/router.js.map +1 -0
- package/dist/handlers/sse.d.ts +38 -0
- package/dist/handlers/sse.d.ts.map +1 -0
- package/dist/handlers/sse.js +52 -0
- package/dist/handlers/sse.js.map +1 -0
- package/dist/handlers/types.d.ts +96 -0
- package/dist/handlers/types.d.ts.map +1 -0
- package/dist/handlers/types.js +14 -0
- package/dist/handlers/types.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/letta/client.d.ts +355 -0
- package/dist/letta/client.d.ts.map +1 -0
- package/dist/letta/client.js +529 -0
- package/dist/letta/client.js.map +1 -0
- package/dist/letta/eventTranslator.d.ts +166 -0
- package/dist/letta/eventTranslator.d.ts.map +1 -0
- package/dist/letta/eventTranslator.js +532 -0
- package/dist/letta/eventTranslator.js.map +1 -0
- package/dist/letta/index.d.ts +13 -0
- package/dist/letta/index.d.ts.map +1 -0
- package/dist/letta/index.js +12 -0
- package/dist/letta/index.js.map +1 -0
- package/dist/letta/modelSettings.d.ts +176 -0
- package/dist/letta/modelSettings.d.ts.map +1 -0
- package/dist/letta/modelSettings.js +314 -0
- package/dist/letta/modelSettings.js.map +1 -0
- package/dist/letta/promptWrapping.d.ts +141 -0
- package/dist/letta/promptWrapping.d.ts.map +1 -0
- package/dist/letta/promptWrapping.js +213 -0
- package/dist/letta/promptWrapping.js.map +1 -0
- package/dist/letta/sandbox.d.ts +100 -0
- package/dist/letta/sandbox.d.ts.map +1 -0
- package/dist/letta/sandbox.js +151 -0
- package/dist/letta/sandbox.js.map +1 -0
- package/dist/letta/settingsFileSeed.d.ts +146 -0
- package/dist/letta/settingsFileSeed.d.ts.map +1 -0
- package/dist/letta/settingsFileSeed.js +177 -0
- package/dist/letta/settingsFileSeed.js.map +1 -0
- package/dist/letta/subprocessThrottle.d.ts +151 -0
- package/dist/letta/subprocessThrottle.d.ts.map +1 -0
- package/dist/letta/subprocessThrottle.js +195 -0
- package/dist/letta/subprocessThrottle.js.map +1 -0
- package/dist/letta/tpmRecovery.d.ts +202 -0
- package/dist/letta/tpmRecovery.d.ts.map +1 -0
- package/dist/letta/tpmRecovery.js +314 -0
- package/dist/letta/tpmRecovery.js.map +1 -0
- package/dist/letta/types.d.ts +54 -0
- package/dist/letta/types.d.ts.map +1 -0
- package/dist/letta/types.js +16 -0
- package/dist/letta/types.js.map +1 -0
- package/dist/observability/logger.d.ts +48 -0
- package/dist/observability/logger.d.ts.map +1 -0
- package/dist/observability/logger.js +81 -0
- package/dist/observability/logger.js.map +1 -0
- package/dist/observability/metrics.d.ts +53 -0
- package/dist/observability/metrics.d.ts.map +1 -0
- package/dist/observability/metrics.js +92 -0
- package/dist/observability/metrics.js.map +1 -0
- package/dist/routes/admin/agents.d.ts +63 -0
- package/dist/routes/admin/agents.d.ts.map +1 -0
- package/dist/routes/admin/agents.js +461 -0
- package/dist/routes/admin/agents.js.map +1 -0
- package/dist/routes/admin/rotatingMessages.d.ts +51 -0
- package/dist/routes/admin/rotatingMessages.d.ts.map +1 -0
- package/dist/routes/admin/rotatingMessages.js +292 -0
- package/dist/routes/admin/rotatingMessages.js.map +1 -0
- package/dist/routes/admin/sessions.d.ts +61 -0
- package/dist/routes/admin/sessions.d.ts.map +1 -0
- package/dist/routes/admin/sessions.js +232 -0
- package/dist/routes/admin/sessions.js.map +1 -0
- package/dist/routes/admin/settings.d.ts +110 -0
- package/dist/routes/admin/settings.d.ts.map +1 -0
- package/dist/routes/admin/settings.js +401 -0
- package/dist/routes/admin/settings.js.map +1 -0
- package/dist/routes/admin/users.d.ts +115 -0
- package/dist/routes/admin/users.d.ts.map +1 -0
- package/dist/routes/admin/users.js +374 -0
- package/dist/routes/admin/users.js.map +1 -0
- package/dist/routes/agents.d.ts +30 -0
- package/dist/routes/agents.d.ts.map +1 -0
- package/dist/routes/agents.js +55 -0
- package/dist/routes/agents.js.map +1 -0
- package/dist/routes/auth.d.ts +121 -0
- package/dist/routes/auth.d.ts.map +1 -0
- package/dist/routes/auth.js +422 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/chat.d.ts +97 -0
- package/dist/routes/chat.d.ts.map +1 -0
- package/dist/routes/chat.js +484 -0
- package/dist/routes/chat.js.map +1 -0
- package/dist/routes/featureToggles.d.ts +34 -0
- package/dist/routes/featureToggles.d.ts.map +1 -0
- package/dist/routes/featureToggles.js +47 -0
- package/dist/routes/featureToggles.js.map +1 -0
- package/dist/routes/health.d.ts +24 -0
- package/dist/routes/health.d.ts.map +1 -0
- package/dist/routes/health.js +37 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/metrics.d.ts +35 -0
- package/dist/routes/metrics.d.ts.map +1 -0
- package/dist/routes/metrics.js +106 -0
- package/dist/routes/metrics.js.map +1 -0
- package/dist/routes/sessions.d.ts +161 -0
- package/dist/routes/sessions.d.ts.map +1 -0
- package/dist/routes/sessions.js +1501 -0
- package/dist/routes/sessions.js.map +1 -0
- package/dist/routes/stubs.d.ts +25 -0
- package/dist/routes/stubs.d.ts.map +1 -0
- package/dist/routes/stubs.js +25 -0
- package/dist/routes/stubs.js.map +1 -0
- package/dist/state/cron.d.ts +58 -0
- package/dist/state/cron.d.ts.map +1 -0
- package/dist/state/cron.js +160 -0
- package/dist/state/cron.js.map +1 -0
- package/dist/state/rotationRunner.d.ts +96 -0
- package/dist/state/rotationRunner.d.ts.map +1 -0
- package/dist/state/rotationRunner.js +246 -0
- package/dist/state/rotationRunner.js.map +1 -0
- package/dist/state/rotationScheduler.d.ts +76 -0
- package/dist/state/rotationScheduler.d.ts.map +1 -0
- package/dist/state/rotationScheduler.js +124 -0
- package/dist/state/rotationScheduler.js.map +1 -0
- package/dist/state/turnRegistry.d.ts +95 -0
- package/dist/state/turnRegistry.d.ts.map +1 -0
- package/dist/state/turnRegistry.js +149 -0
- package/dist/state/turnRegistry.js.map +1 -0
- package/dist/state/turnRunner.d.ts +134 -0
- package/dist/state/turnRunner.d.ts.map +1 -0
- package/dist/state/turnRunner.js +476 -0
- package/dist/state/turnRunner.js.map +1 -0
- package/package.json +47 -0
- package/src/auth/argon2.ts +152 -0
- package/src/auth/encryption.ts +110 -0
- package/src/auth/jwt.ts +217 -0
- package/src/auth/middleware.ts +250 -0
- package/src/auth/passwordPolicy.ts +99 -0
- package/src/auth/roleRules.ts +497 -0
- package/src/auth/userView.ts +88 -0
- package/src/bootstrap.ts +254 -0
- package/src/db/connection.ts +75 -0
- package/src/db/migrate.ts +212 -0
- package/src/db/schema.ts +750 -0
- package/src/db/seed.ts +236 -0
- package/src/handlers/fetch.ts +251 -0
- package/src/handlers/nextjs.ts +49 -0
- package/src/handlers/responses.ts +95 -0
- package/src/handlers/routeTable.ts +188 -0
- package/src/handlers/router.ts +96 -0
- package/src/handlers/sse.ts +55 -0
- package/src/handlers/types.ts +104 -0
- package/src/index.ts +60 -0
- package/src/letta/client.ts +860 -0
- package/src/letta/eventTranslator.ts +667 -0
- package/src/letta/index.ts +46 -0
- package/src/letta/lettaCliShim.mjs +40 -0
- package/src/letta/sandbox.ts +167 -0
- package/src/letta/settingsFileSeed.ts +254 -0
- package/src/letta/subprocessThrottle.ts +251 -0
- package/src/letta/types.ts +65 -0
- package/src/observability/logger.ts +103 -0
- package/src/observability/metrics.ts +114 -0
- package/src/routes/admin/agents.ts +574 -0
- package/src/routes/admin/rotatingMessages.ts +351 -0
- package/src/routes/admin/sessions.ts +283 -0
- package/src/routes/admin/settings.ts +463 -0
- package/src/routes/admin/users.ts +455 -0
- package/src/routes/agents.ts +66 -0
- package/src/routes/auth.ts +513 -0
- package/src/routes/chat.ts +534 -0
- package/src/routes/featureToggles.ts +52 -0
- package/src/routes/health.ts +44 -0
- package/src/routes/metrics.ts +111 -0
- package/src/routes/sessions.ts +1878 -0
- package/src/state/cron.ts +179 -0
- package/src/state/rotationRunner.ts +326 -0
- package/src/state/rotationScheduler.ts +164 -0
- package/src/state/turnRegistry.ts +210 -0
- package/src/state/turnRunner.ts +588 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TPM-recovery primitives — provider-429 retry/continuation loop (§6.5).
|
|
3
|
+
*
|
|
4
|
+
* The orchestration lives in `state/turnRunner.ts`. This file contains the
|
|
5
|
+
* pure helpers + the per-agent config resolver + the continuation prompt
|
|
6
|
+
* text + a cancellable sleep, in one place so the runner stays focused on
|
|
7
|
+
* the state-machine narrative and the helpers stay unit-testable in
|
|
8
|
+
* isolation.
|
|
9
|
+
*
|
|
10
|
+
* ## Detection (§17 question 3)
|
|
11
|
+
*
|
|
12
|
+
* The Letta TypeScript SDK exposes a typed `RateLimitError extends APIError<429, Headers>`
|
|
13
|
+
* (see `@letta-ai/letta-client/core/error`). When a provider 429 surfaces
|
|
14
|
+
* through `messages.create`, the SDK throws an error whose `status === 429`
|
|
15
|
+
* — either before the iterator yields its first event (pre-stream) or
|
|
16
|
+
* mid-iteration (mid-stream). `is429(err)` is the single detection
|
|
17
|
+
* predicate; it tolerates both the canonical `RateLimitError` instance and
|
|
18
|
+
* plain `{ status: 429, headers }` objects so tests can craft fakes
|
|
19
|
+
* without importing the SDK class directly.
|
|
20
|
+
*
|
|
21
|
+
* `readRetryAfterSeconds(err, fallback)` consults `err.headers.get("retry-after")`
|
|
22
|
+
* — the spec calls for "Honor the `Retry-After` header when present;
|
|
23
|
+
* otherwise use `default_retry_after_seconds`" (§6.5). Per RFC 9110 the
|
|
24
|
+
* header value is either delta-seconds (e.g. `"60"`) or an HTTP-date; we
|
|
25
|
+
* parse both. Anything malformed falls back to the per-agent default.
|
|
26
|
+
*
|
|
27
|
+
* ## The continuation prompt
|
|
28
|
+
*
|
|
29
|
+
* `CONTINUATION_PROMPT` is the exact text §6.5 specifies for mid-stream
|
|
30
|
+
* recovery. Wording is load-bearing — changing it would change how the
|
|
31
|
+
* agent resumes — so the constant lives here, not as a magic string in the
|
|
32
|
+
* runner.
|
|
33
|
+
*
|
|
34
|
+
* ## Cancellation safety (§6.9)
|
|
35
|
+
*
|
|
36
|
+
* `cancelableSleep(ms, signal)` resolves on either the timer firing OR the
|
|
37
|
+
* signal aborting. On abort the pending `setTimeout` is cleared so the
|
|
38
|
+
* Node event loop doesn't keep the process alive past cancel. The runner's
|
|
39
|
+
* AbortController is composed with `signal` so a `POST /chat/:id/cancel`
|
|
40
|
+
* mid-wait clears the pending recovery in one step.
|
|
41
|
+
*
|
|
42
|
+
* @see SPECIFICATIONS.md §6.5 — TPM recovery
|
|
43
|
+
* @see SPECIFICATIONS.md §6.9 — cancellation
|
|
44
|
+
* @see SPECIFICATIONS.md §17 question 3 — 429 surface
|
|
45
|
+
*/
|
|
46
|
+
import type { DrizzleDB } from "../db/connection.js";
|
|
47
|
+
/**
|
|
48
|
+
* Per-agent recovery config as resolved at the top of a turn run.
|
|
49
|
+
*
|
|
50
|
+
* Carried into the runner without re-reading the DB on every retry so a
|
|
51
|
+
* concurrent `PATCH /admin/agents/:id` doesn't change the contract
|
|
52
|
+
* mid-recovery (operationally surprising; the in-flight turn keeps the
|
|
53
|
+
* settings it started with).
|
|
54
|
+
*/
|
|
55
|
+
export interface TpmRecoveryConfig {
|
|
56
|
+
/** Master toggle. When `false`, the runner never sleeps + retries on 429 — the turn fails fast with `tpm_exhausted`. */
|
|
57
|
+
enabled: boolean;
|
|
58
|
+
/** Effective ceiling on the *recovery* attempts (the double-failure abort condition in §6.5). Default 2. */
|
|
59
|
+
maxRecoveryAttempts: number;
|
|
60
|
+
/** Fallback when a 429 carries no `Retry-After`. Default 65 (§6.5 — Anthropic's TPM window + buffer). */
|
|
61
|
+
defaultRetryAfterSeconds: number;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The continuation message the runner sends to Letta after a successful
|
|
65
|
+
* mid-stream sleep. Wording verbatim from §6.5; **do not edit** without
|
|
66
|
+
* updating the spec.
|
|
67
|
+
*
|
|
68
|
+
* @see SPECIFICATIONS.md §6.5
|
|
69
|
+
*/
|
|
70
|
+
export declare const CONTINUATION_PROMPT = "The previous response was interrupted by an API rate limit. Continue exactly where you left off \u2014 do not restart, do not summarize, just continue.";
|
|
71
|
+
/**
|
|
72
|
+
* Resolves the per-agent TPM-recovery config for the agent that owns
|
|
73
|
+
* `sessionId`. The runner calls this once before opening the Letta stream
|
|
74
|
+
* — settings are pinned for the duration of the turn (see file-level
|
|
75
|
+
* comment).
|
|
76
|
+
*
|
|
77
|
+
* Returns the disabled-default config if the session or agent row can't be
|
|
78
|
+
* found (defensive — handlers above already validate ownership, so a miss
|
|
79
|
+
* here means concurrent deletion, which is impossible in v1 since neither
|
|
80
|
+
* agent nor session can be hard-deleted (§6.3.1 / §6.3.4)).
|
|
81
|
+
*
|
|
82
|
+
* @param db - Drizzle handle.
|
|
83
|
+
* @param sessionId - the session's id (from the request body / runner opts).
|
|
84
|
+
* @returns the per-agent recovery config.
|
|
85
|
+
*
|
|
86
|
+
* @see SPECIFICATIONS.md §11 — `agent` table fields
|
|
87
|
+
*/
|
|
88
|
+
export declare function resolveTpmConfig(db: DrizzleDB, sessionId: string): TpmRecoveryConfig;
|
|
89
|
+
/**
|
|
90
|
+
* Resolves the per-agent `stream_idle_timeout_seconds` for the agent
|
|
91
|
+
* owning `sessionId`. The runner calls this once before opening the
|
|
92
|
+
* Letta stream — settings are pinned for the duration of the turn (same
|
|
93
|
+
* pin-at-start contract as `resolveTpmConfig`).
|
|
94
|
+
*
|
|
95
|
+
* Returns the v1 default when no row is found (defensive).
|
|
96
|
+
*
|
|
97
|
+
* @param db - Drizzle handle.
|
|
98
|
+
* @param sessionId - the session's id.
|
|
99
|
+
* @returns the idle timeout in seconds.
|
|
100
|
+
*/
|
|
101
|
+
export declare function resolveStreamIdleTimeout(db: DrizzleDB, sessionId: string): number;
|
|
102
|
+
/**
|
|
103
|
+
* Detects a provider 429 surfaced through the Letta SDK.
|
|
104
|
+
*
|
|
105
|
+
* Tolerates three shapes, in order of decreasing specificity:
|
|
106
|
+
*
|
|
107
|
+
* 1. **Canonical** — `RateLimitError` instance from
|
|
108
|
+
* `@letta-ai/letta-client/core/error`. The SDK-typed path; matches
|
|
109
|
+
* when Letta forwards the upstream 429 as an HTTP-level 429 on the
|
|
110
|
+
* `messages.create` connection.
|
|
111
|
+
* 2. **Status-shaped** — plain `{ status: 429, ... }` objects. Tests
|
|
112
|
+
* craft these via `makeFake429`; production also sees them when the
|
|
113
|
+
* SDK has serialized a typed error into a plain object boundary.
|
|
114
|
+
* 3. **Message-shaped fallback** — plain `Error` instances whose
|
|
115
|
+
* `.message` contains "rate limit" / "429" / "too many requests".
|
|
116
|
+
* This catches the [letta-ai/letta#3310](https://github.com/letta-ai/letta/issues/3310)
|
|
117
|
+
* wrapped surface that the **live investigation** observed against
|
|
118
|
+
* `letta/letta:latest` (server 0.16.7) + Anthropic Claude Haiku 4.5:
|
|
119
|
+
* Letta threw a plain Error with the upstream message text and no
|
|
120
|
+
* `status` field. The SDK-source-of-truth `RateLimitError` shape
|
|
121
|
+
* exists in `.d.ts` but is not the surface that actually fires.
|
|
122
|
+
*
|
|
123
|
+
* The match excludes context-window-style messages to avoid the obvious
|
|
124
|
+
* "limit" false-positive — see `looksLikeProviderRateLimitMessage`.
|
|
125
|
+
*
|
|
126
|
+
* @param err - the thrown value.
|
|
127
|
+
* @returns `true` iff the error represents a provider 429.
|
|
128
|
+
*
|
|
129
|
+
* @see SPECIFICATIONS.md §17 question 3
|
|
130
|
+
*/
|
|
131
|
+
export declare function is429(err: unknown): boolean;
|
|
132
|
+
/**
|
|
133
|
+
* Predicate that matches a string containing a provider-rate-limit
|
|
134
|
+
* indicator while excluding common false-positives.
|
|
135
|
+
*
|
|
136
|
+
* Used by both `is429` (caught Error → "is this a 429?") and the
|
|
137
|
+
* translator's `error_message` handler (Letta wrapped event → "is this a
|
|
138
|
+
* 429?"). Centralized here so both detection paths apply the same
|
|
139
|
+
* matching rules.
|
|
140
|
+
*
|
|
141
|
+
* **Matches** (case-insensitive substring): `rate limit`, `rate-limit`,
|
|
142
|
+
* `rate_limit`, `ratelimit`, `429`, `too many requests`.
|
|
143
|
+
*
|
|
144
|
+
* **Excludes**: messages containing `context_window` / `context window`
|
|
145
|
+
* (those are size-limit errors, not rate limits) and `usage limit` /
|
|
146
|
+
* `usage_limit` (those are billing-cap exhaustions, not transient rate
|
|
147
|
+
* limits — recovering doesn't help).
|
|
148
|
+
*
|
|
149
|
+
* @param text - the message text to inspect.
|
|
150
|
+
* @returns `true` when `text` looks like a provider rate limit.
|
|
151
|
+
*
|
|
152
|
+
* @see SPECIFICATIONS.md §17 question 3
|
|
153
|
+
*/
|
|
154
|
+
export declare function looksLikeProviderRateLimitMessage(text: string): boolean;
|
|
155
|
+
/**
|
|
156
|
+
* Reads the `Retry-After` header off a 429-shaped error.
|
|
157
|
+
*
|
|
158
|
+
* Per RFC 9110 §10.2.3 the header is either delta-seconds (`"60"`) or an
|
|
159
|
+
* `IMF-fixdate`; we handle both. Malformed / missing → `fallbackSeconds`.
|
|
160
|
+
* A `Date.now()`-relative date in the past clamps to 0 (the server is
|
|
161
|
+
* already past the suggested retry window).
|
|
162
|
+
*
|
|
163
|
+
* @param err - the 429-shaped error.
|
|
164
|
+
* @param fallbackSeconds - what to return when no usable header is found.
|
|
165
|
+
* @returns the suggested wait in *seconds* (not ms — the caller multiplies).
|
|
166
|
+
*
|
|
167
|
+
* @see https://datatracker.ietf.org/doc/html/rfc9110#section-10.2.3
|
|
168
|
+
*/
|
|
169
|
+
export declare function readRetryAfterSeconds(err: unknown, fallbackSeconds: number): number;
|
|
170
|
+
/**
|
|
171
|
+
* Promise wrapper around `setTimeout` that resolves when the timer fires
|
|
172
|
+
* OR rejects with the supplied `AbortError` shape when the signal fires.
|
|
173
|
+
* Clears the pending `setTimeout` on abort so the Node event loop doesn't
|
|
174
|
+
* keep the process alive past the cancel.
|
|
175
|
+
*
|
|
176
|
+
* Cancellation-safety is the load-bearing property: §6.9 calls for "any
|
|
177
|
+
* in-flight `setTimeout` for TPM recovery is cleared" on cancel, and the
|
|
178
|
+
* Step-6 acceptance criteria has a test that asserts no continuation
|
|
179
|
+
* request fires after a cancel during the wait.
|
|
180
|
+
*
|
|
181
|
+
* @param ms - the delay in milliseconds.
|
|
182
|
+
* @param signal - the abort signal to honor.
|
|
183
|
+
* @returns a promise resolved on timer fire; rejected with `AbortError` on abort.
|
|
184
|
+
*
|
|
185
|
+
* @see SPECIFICATIONS.md §6.9 — cancellation
|
|
186
|
+
*/
|
|
187
|
+
export declare function cancelableSleep(ms: number, signal: AbortSignal | undefined): Promise<void>;
|
|
188
|
+
/**
|
|
189
|
+
* Adds a small jitter (0..200 ms) to the suggested wait in seconds so two
|
|
190
|
+
* recovering turns don't both wake up at the exact same millisecond and
|
|
191
|
+
* thunder against the provider again.
|
|
192
|
+
*
|
|
193
|
+
* The jitter is *added*, not subtracted, so we never come in earlier than
|
|
194
|
+
* the provider's suggested window. v1 keeps it deterministic across tests
|
|
195
|
+
* by reading from `Math.random()` — fake-timer tests assert behavior at
|
|
196
|
+
* the boundary, not the exact jitter offset.
|
|
197
|
+
*
|
|
198
|
+
* @param baseSeconds - the value returned by `readRetryAfterSeconds`.
|
|
199
|
+
* @returns the actual delay in milliseconds.
|
|
200
|
+
*/
|
|
201
|
+
export declare function withJitter(baseSeconds: number): number;
|
|
202
|
+
//# sourceMappingURL=tpmRecovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tpmRecovery.d.ts","sourceRoot":"","sources":["../../src/letta/tpmRecovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAOH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC,wHAAwH;IACxH,OAAO,EAAE,OAAO,CAAC;IACjB,4GAA4G;IAC5G,mBAAmB,EAAE,MAAM,CAAC;IAC5B,yGAAyG;IACzG,wBAAwB,EAAE,MAAM,CAAC;CAClC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,4JACsH,CAAC;AAEvJ;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAC9B,EAAE,EAAE,SAAS,EACb,SAAS,EAAE,MAAM,GAChB,iBAAiB,CAuBnB;AAYD;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CACtC,EAAE,EAAE,SAAS,EACb,SAAS,EAAE,MAAM,GAChB,MAAM,CAQR;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAU3C;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,iCAAiC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAYvE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,OAAO,EACZ,eAAe,EAAE,MAAM,GACtB,MAAM,CAaR;AAkBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAC7B,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,WAAW,GAAG,SAAS,GAC9B,OAAO,CAAC,IAAI,CAAC,CAgBf;AASD;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAGtD"}
|
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TPM-recovery primitives — provider-429 retry/continuation loop (§6.5).
|
|
3
|
+
*
|
|
4
|
+
* The orchestration lives in `state/turnRunner.ts`. This file contains the
|
|
5
|
+
* pure helpers + the per-agent config resolver + the continuation prompt
|
|
6
|
+
* text + a cancellable sleep, in one place so the runner stays focused on
|
|
7
|
+
* the state-machine narrative and the helpers stay unit-testable in
|
|
8
|
+
* isolation.
|
|
9
|
+
*
|
|
10
|
+
* ## Detection (§17 question 3)
|
|
11
|
+
*
|
|
12
|
+
* The Letta TypeScript SDK exposes a typed `RateLimitError extends APIError<429, Headers>`
|
|
13
|
+
* (see `@letta-ai/letta-client/core/error`). When a provider 429 surfaces
|
|
14
|
+
* through `messages.create`, the SDK throws an error whose `status === 429`
|
|
15
|
+
* — either before the iterator yields its first event (pre-stream) or
|
|
16
|
+
* mid-iteration (mid-stream). `is429(err)` is the single detection
|
|
17
|
+
* predicate; it tolerates both the canonical `RateLimitError` instance and
|
|
18
|
+
* plain `{ status: 429, headers }` objects so tests can craft fakes
|
|
19
|
+
* without importing the SDK class directly.
|
|
20
|
+
*
|
|
21
|
+
* `readRetryAfterSeconds(err, fallback)` consults `err.headers.get("retry-after")`
|
|
22
|
+
* — the spec calls for "Honor the `Retry-After` header when present;
|
|
23
|
+
* otherwise use `default_retry_after_seconds`" (§6.5). Per RFC 9110 the
|
|
24
|
+
* header value is either delta-seconds (e.g. `"60"`) or an HTTP-date; we
|
|
25
|
+
* parse both. Anything malformed falls back to the per-agent default.
|
|
26
|
+
*
|
|
27
|
+
* ## The continuation prompt
|
|
28
|
+
*
|
|
29
|
+
* `CONTINUATION_PROMPT` is the exact text §6.5 specifies for mid-stream
|
|
30
|
+
* recovery. Wording is load-bearing — changing it would change how the
|
|
31
|
+
* agent resumes — so the constant lives here, not as a magic string in the
|
|
32
|
+
* runner.
|
|
33
|
+
*
|
|
34
|
+
* ## Cancellation safety (§6.9)
|
|
35
|
+
*
|
|
36
|
+
* `cancelableSleep(ms, signal)` resolves on either the timer firing OR the
|
|
37
|
+
* signal aborting. On abort the pending `setTimeout` is cleared so the
|
|
38
|
+
* Node event loop doesn't keep the process alive past cancel. The runner's
|
|
39
|
+
* AbortController is composed with `signal` so a `POST /chat/:id/cancel`
|
|
40
|
+
* mid-wait clears the pending recovery in one step.
|
|
41
|
+
*
|
|
42
|
+
* @see SPECIFICATIONS.md §6.5 — TPM recovery
|
|
43
|
+
* @see SPECIFICATIONS.md §6.9 — cancellation
|
|
44
|
+
* @see SPECIFICATIONS.md §17 question 3 — 429 surface
|
|
45
|
+
*/
|
|
46
|
+
import { eq } from "drizzle-orm";
|
|
47
|
+
import { RateLimitError } from "@letta-ai/letta-client";
|
|
48
|
+
import { agent, session } from "../db/schema.js";
|
|
49
|
+
/**
|
|
50
|
+
* The continuation message the runner sends to Letta after a successful
|
|
51
|
+
* mid-stream sleep. Wording verbatim from §6.5; **do not edit** without
|
|
52
|
+
* updating the spec.
|
|
53
|
+
*
|
|
54
|
+
* @see SPECIFICATIONS.md §6.5
|
|
55
|
+
*/
|
|
56
|
+
export const CONTINUATION_PROMPT = "The previous response was interrupted by an API rate limit. Continue exactly where you left off — do not restart, do not summarize, just continue.";
|
|
57
|
+
/**
|
|
58
|
+
* Resolves the per-agent TPM-recovery config for the agent that owns
|
|
59
|
+
* `sessionId`. The runner calls this once before opening the Letta stream
|
|
60
|
+
* — settings are pinned for the duration of the turn (see file-level
|
|
61
|
+
* comment).
|
|
62
|
+
*
|
|
63
|
+
* Returns the disabled-default config if the session or agent row can't be
|
|
64
|
+
* found (defensive — handlers above already validate ownership, so a miss
|
|
65
|
+
* here means concurrent deletion, which is impossible in v1 since neither
|
|
66
|
+
* agent nor session can be hard-deleted (§6.3.1 / §6.3.4)).
|
|
67
|
+
*
|
|
68
|
+
* @param db - Drizzle handle.
|
|
69
|
+
* @param sessionId - the session's id (from the request body / runner opts).
|
|
70
|
+
* @returns the per-agent recovery config.
|
|
71
|
+
*
|
|
72
|
+
* @see SPECIFICATIONS.md §11 — `agent` table fields
|
|
73
|
+
*/
|
|
74
|
+
export function resolveTpmConfig(db, sessionId) {
|
|
75
|
+
const row = db
|
|
76
|
+
.select({
|
|
77
|
+
enabled: agent.tpmRecoveryEnabled,
|
|
78
|
+
max: agent.maxRecoveryAttempts,
|
|
79
|
+
retryAfter: agent.defaultRetryAfterSeconds,
|
|
80
|
+
})
|
|
81
|
+
.from(session)
|
|
82
|
+
.innerJoin(agent, eq(session.agentId, agent.id))
|
|
83
|
+
.where(eq(session.id, sessionId))
|
|
84
|
+
.get();
|
|
85
|
+
if (!row) {
|
|
86
|
+
return {
|
|
87
|
+
enabled: false,
|
|
88
|
+
maxRecoveryAttempts: 2,
|
|
89
|
+
defaultRetryAfterSeconds: 65,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
enabled: row.enabled,
|
|
94
|
+
maxRecoveryAttempts: row.max,
|
|
95
|
+
defaultRetryAfterSeconds: row.retryAfter,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Default `streamIdleTimeoutSeconds` when no agent row can be found (e.g.
|
|
100
|
+
* concurrent deletion — impossible in v1 since agents are archive-only,
|
|
101
|
+
* but the defensive default keeps the runner well-behaved either way).
|
|
102
|
+
* Matches the column default in `agent` (Step-10 closeout). Independent
|
|
103
|
+
* of TPM defaults — TPM has its own 65s timer that doesn't share with
|
|
104
|
+
* this idle watchdog.
|
|
105
|
+
*/
|
|
106
|
+
const DEFAULT_STREAM_IDLE_TIMEOUT_SECONDS = 60;
|
|
107
|
+
/**
|
|
108
|
+
* Resolves the per-agent `stream_idle_timeout_seconds` for the agent
|
|
109
|
+
* owning `sessionId`. The runner calls this once before opening the
|
|
110
|
+
* Letta stream — settings are pinned for the duration of the turn (same
|
|
111
|
+
* pin-at-start contract as `resolveTpmConfig`).
|
|
112
|
+
*
|
|
113
|
+
* Returns the v1 default when no row is found (defensive).
|
|
114
|
+
*
|
|
115
|
+
* @param db - Drizzle handle.
|
|
116
|
+
* @param sessionId - the session's id.
|
|
117
|
+
* @returns the idle timeout in seconds.
|
|
118
|
+
*/
|
|
119
|
+
export function resolveStreamIdleTimeout(db, sessionId) {
|
|
120
|
+
const row = db
|
|
121
|
+
.select({ idleTimeout: agent.streamIdleTimeoutSeconds })
|
|
122
|
+
.from(session)
|
|
123
|
+
.innerJoin(agent, eq(session.agentId, agent.id))
|
|
124
|
+
.where(eq(session.id, sessionId))
|
|
125
|
+
.get();
|
|
126
|
+
return row?.idleTimeout ?? DEFAULT_STREAM_IDLE_TIMEOUT_SECONDS;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Detects a provider 429 surfaced through the Letta SDK.
|
|
130
|
+
*
|
|
131
|
+
* Tolerates three shapes, in order of decreasing specificity:
|
|
132
|
+
*
|
|
133
|
+
* 1. **Canonical** — `RateLimitError` instance from
|
|
134
|
+
* `@letta-ai/letta-client/core/error`. The SDK-typed path; matches
|
|
135
|
+
* when Letta forwards the upstream 429 as an HTTP-level 429 on the
|
|
136
|
+
* `messages.create` connection.
|
|
137
|
+
* 2. **Status-shaped** — plain `{ status: 429, ... }` objects. Tests
|
|
138
|
+
* craft these via `makeFake429`; production also sees them when the
|
|
139
|
+
* SDK has serialized a typed error into a plain object boundary.
|
|
140
|
+
* 3. **Message-shaped fallback** — plain `Error` instances whose
|
|
141
|
+
* `.message` contains "rate limit" / "429" / "too many requests".
|
|
142
|
+
* This catches the [letta-ai/letta#3310](https://github.com/letta-ai/letta/issues/3310)
|
|
143
|
+
* wrapped surface that the **live investigation** observed against
|
|
144
|
+
* `letta/letta:latest` (server 0.16.7) + Anthropic Claude Haiku 4.5:
|
|
145
|
+
* Letta threw a plain Error with the upstream message text and no
|
|
146
|
+
* `status` field. The SDK-source-of-truth `RateLimitError` shape
|
|
147
|
+
* exists in `.d.ts` but is not the surface that actually fires.
|
|
148
|
+
*
|
|
149
|
+
* The match excludes context-window-style messages to avoid the obvious
|
|
150
|
+
* "limit" false-positive — see `looksLikeProviderRateLimitMessage`.
|
|
151
|
+
*
|
|
152
|
+
* @param err - the thrown value.
|
|
153
|
+
* @returns `true` iff the error represents a provider 429.
|
|
154
|
+
*
|
|
155
|
+
* @see SPECIFICATIONS.md §17 question 3
|
|
156
|
+
*/
|
|
157
|
+
export function is429(err) {
|
|
158
|
+
if (err instanceof RateLimitError)
|
|
159
|
+
return true;
|
|
160
|
+
if (err && typeof err === "object") {
|
|
161
|
+
const e = err;
|
|
162
|
+
if (e.status === 429)
|
|
163
|
+
return true;
|
|
164
|
+
if (typeof e.message === "string" && looksLikeProviderRateLimitMessage(e.message)) {
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Predicate that matches a string containing a provider-rate-limit
|
|
172
|
+
* indicator while excluding common false-positives.
|
|
173
|
+
*
|
|
174
|
+
* Used by both `is429` (caught Error → "is this a 429?") and the
|
|
175
|
+
* translator's `error_message` handler (Letta wrapped event → "is this a
|
|
176
|
+
* 429?"). Centralized here so both detection paths apply the same
|
|
177
|
+
* matching rules.
|
|
178
|
+
*
|
|
179
|
+
* **Matches** (case-insensitive substring): `rate limit`, `rate-limit`,
|
|
180
|
+
* `rate_limit`, `ratelimit`, `429`, `too many requests`.
|
|
181
|
+
*
|
|
182
|
+
* **Excludes**: messages containing `context_window` / `context window`
|
|
183
|
+
* (those are size-limit errors, not rate limits) and `usage limit` /
|
|
184
|
+
* `usage_limit` (those are billing-cap exhaustions, not transient rate
|
|
185
|
+
* limits — recovering doesn't help).
|
|
186
|
+
*
|
|
187
|
+
* @param text - the message text to inspect.
|
|
188
|
+
* @returns `true` when `text` looks like a provider rate limit.
|
|
189
|
+
*
|
|
190
|
+
* @see SPECIFICATIONS.md §17 question 3
|
|
191
|
+
*/
|
|
192
|
+
export function looksLikeProviderRateLimitMessage(text) {
|
|
193
|
+
const l = text.toLowerCase();
|
|
194
|
+
if (l.includes("context_window") || l.includes("context window"))
|
|
195
|
+
return false;
|
|
196
|
+
if (l.includes("usage limit") || l.includes("usage_limit"))
|
|
197
|
+
return false;
|
|
198
|
+
return (l.includes("rate limit") ||
|
|
199
|
+
l.includes("rate-limit") ||
|
|
200
|
+
l.includes("rate_limit") ||
|
|
201
|
+
l.includes("ratelimit") ||
|
|
202
|
+
l.includes("429") ||
|
|
203
|
+
l.includes("too many requests"));
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Reads the `Retry-After` header off a 429-shaped error.
|
|
207
|
+
*
|
|
208
|
+
* Per RFC 9110 §10.2.3 the header is either delta-seconds (`"60"`) or an
|
|
209
|
+
* `IMF-fixdate`; we handle both. Malformed / missing → `fallbackSeconds`.
|
|
210
|
+
* A `Date.now()`-relative date in the past clamps to 0 (the server is
|
|
211
|
+
* already past the suggested retry window).
|
|
212
|
+
*
|
|
213
|
+
* @param err - the 429-shaped error.
|
|
214
|
+
* @param fallbackSeconds - what to return when no usable header is found.
|
|
215
|
+
* @returns the suggested wait in *seconds* (not ms — the caller multiplies).
|
|
216
|
+
*
|
|
217
|
+
* @see https://datatracker.ietf.org/doc/html/rfc9110#section-10.2.3
|
|
218
|
+
*/
|
|
219
|
+
export function readRetryAfterSeconds(err, fallbackSeconds) {
|
|
220
|
+
const headers = extractHeaders(err);
|
|
221
|
+
if (!headers)
|
|
222
|
+
return fallbackSeconds;
|
|
223
|
+
const raw = headers.get("retry-after");
|
|
224
|
+
if (!raw)
|
|
225
|
+
return fallbackSeconds;
|
|
226
|
+
const asNumber = Number(raw);
|
|
227
|
+
if (Number.isFinite(asNumber) && asNumber >= 0)
|
|
228
|
+
return Math.round(asNumber);
|
|
229
|
+
const asDate = Date.parse(raw);
|
|
230
|
+
if (!Number.isNaN(asDate)) {
|
|
231
|
+
const delta = Math.round((asDate - Date.now()) / 1000);
|
|
232
|
+
return delta > 0 ? delta : 0;
|
|
233
|
+
}
|
|
234
|
+
return fallbackSeconds;
|
|
235
|
+
}
|
|
236
|
+
/** Pulls the `Headers` off either the typed SDK error or a plain mock. */
|
|
237
|
+
function extractHeaders(err) {
|
|
238
|
+
if (!err || typeof err !== "object")
|
|
239
|
+
return null;
|
|
240
|
+
const e = err;
|
|
241
|
+
if (e.headers instanceof Headers)
|
|
242
|
+
return e.headers;
|
|
243
|
+
// Plain object support — tests build a `{ "retry-after": "1" }`-style record.
|
|
244
|
+
if (e.headers && typeof e.headers === "object") {
|
|
245
|
+
const h = new Headers();
|
|
246
|
+
for (const [k, v] of Object.entries(e.headers)) {
|
|
247
|
+
if (typeof v === "string")
|
|
248
|
+
h.set(k, v);
|
|
249
|
+
}
|
|
250
|
+
return h;
|
|
251
|
+
}
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Promise wrapper around `setTimeout` that resolves when the timer fires
|
|
256
|
+
* OR rejects with the supplied `AbortError` shape when the signal fires.
|
|
257
|
+
* Clears the pending `setTimeout` on abort so the Node event loop doesn't
|
|
258
|
+
* keep the process alive past the cancel.
|
|
259
|
+
*
|
|
260
|
+
* Cancellation-safety is the load-bearing property: §6.9 calls for "any
|
|
261
|
+
* in-flight `setTimeout` for TPM recovery is cleared" on cancel, and the
|
|
262
|
+
* Step-6 acceptance criteria has a test that asserts no continuation
|
|
263
|
+
* request fires after a cancel during the wait.
|
|
264
|
+
*
|
|
265
|
+
* @param ms - the delay in milliseconds.
|
|
266
|
+
* @param signal - the abort signal to honor.
|
|
267
|
+
* @returns a promise resolved on timer fire; rejected with `AbortError` on abort.
|
|
268
|
+
*
|
|
269
|
+
* @see SPECIFICATIONS.md §6.9 — cancellation
|
|
270
|
+
*/
|
|
271
|
+
export function cancelableSleep(ms, signal) {
|
|
272
|
+
if (signal?.aborted) {
|
|
273
|
+
return Promise.reject(makeAbortError());
|
|
274
|
+
}
|
|
275
|
+
return new Promise((resolve, reject) => {
|
|
276
|
+
const timer = setTimeout(() => {
|
|
277
|
+
if (signal)
|
|
278
|
+
signal.removeEventListener("abort", onAbort);
|
|
279
|
+
resolve();
|
|
280
|
+
}, ms);
|
|
281
|
+
const onAbort = () => {
|
|
282
|
+
clearTimeout(timer);
|
|
283
|
+
if (signal)
|
|
284
|
+
signal.removeEventListener("abort", onAbort);
|
|
285
|
+
reject(makeAbortError());
|
|
286
|
+
};
|
|
287
|
+
if (signal)
|
|
288
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
/** Constructs an `AbortError`-shaped Error consistent with the runner's existing sentinel. */
|
|
292
|
+
function makeAbortError() {
|
|
293
|
+
const e = new Error("aborted");
|
|
294
|
+
e.name = "AbortError";
|
|
295
|
+
return e;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Adds a small jitter (0..200 ms) to the suggested wait in seconds so two
|
|
299
|
+
* recovering turns don't both wake up at the exact same millisecond and
|
|
300
|
+
* thunder against the provider again.
|
|
301
|
+
*
|
|
302
|
+
* The jitter is *added*, not subtracted, so we never come in earlier than
|
|
303
|
+
* the provider's suggested window. v1 keeps it deterministic across tests
|
|
304
|
+
* by reading from `Math.random()` — fake-timer tests assert behavior at
|
|
305
|
+
* the boundary, not the exact jitter offset.
|
|
306
|
+
*
|
|
307
|
+
* @param baseSeconds - the value returned by `readRetryAfterSeconds`.
|
|
308
|
+
* @returns the actual delay in milliseconds.
|
|
309
|
+
*/
|
|
310
|
+
export function withJitter(baseSeconds) {
|
|
311
|
+
const jitter = Math.floor(Math.random() * 200);
|
|
312
|
+
return Math.max(0, baseSeconds * 1000 + jitter);
|
|
313
|
+
}
|
|
314
|
+
//# sourceMappingURL=tpmRecovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tpmRecovery.js","sourceRoot":"","sources":["../../src/letta/tpmRecovery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAEjC,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAoBjD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAC9B,oJAAoJ,CAAC;AAEvJ;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,gBAAgB,CAC9B,EAAa,EACb,SAAiB;IAEjB,MAAM,GAAG,GAAG,EAAE;SACX,MAAM,CAAC;QACN,OAAO,EAAE,KAAK,CAAC,kBAAkB;QACjC,GAAG,EAAE,KAAK,CAAC,mBAAmB;QAC9B,UAAU,EAAE,KAAK,CAAC,wBAAwB;KAC3C,CAAC;SACD,IAAI,CAAC,OAAO,CAAC;SACb,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;SAC/C,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;SAChC,GAAG,EAAE,CAAC;IACT,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO;YACL,OAAO,EAAE,KAAK;YACd,mBAAmB,EAAE,CAAC;YACtB,wBAAwB,EAAE,EAAE;SAC7B,CAAC;IACJ,CAAC;IACD,OAAO;QACL,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,mBAAmB,EAAE,GAAG,CAAC,GAAG;QAC5B,wBAAwB,EAAE,GAAG,CAAC,UAAU;KACzC,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,mCAAmC,GAAG,EAAE,CAAC;AAE/C;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,wBAAwB,CACtC,EAAa,EACb,SAAiB;IAEjB,MAAM,GAAG,GAAG,EAAE;SACX,MAAM,CAAC,EAAE,WAAW,EAAE,KAAK,CAAC,wBAAwB,EAAE,CAAC;SACvD,IAAI,CAAC,OAAO,CAAC;SACb,SAAS,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;SAC/C,KAAK,CAAC,EAAE,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,CAAC,CAAC;SAChC,GAAG,EAAE,CAAC;IACT,OAAO,GAAG,EAAE,WAAW,IAAI,mCAAmC,CAAC;AACjE,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,UAAU,KAAK,CAAC,GAAY;IAChC,IAAI,GAAG,YAAY,cAAc;QAAE,OAAO,IAAI,CAAC;IAC/C,IAAI,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QACnC,MAAM,CAAC,GAAG,GAA8C,CAAC;QACzD,IAAI,CAAC,CAAC,MAAM,KAAK,GAAG;YAAE,OAAO,IAAI,CAAC;QAClC,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,IAAI,iCAAiC,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;YAClF,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,iCAAiC,CAAC,IAAY;IAC5D,MAAM,CAAC,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAC7B,IAAI,CAAC,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,gBAAgB,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/E,IAAI,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC;QAAE,OAAO,KAAK,CAAC;IACzE,OAAO,CACL,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC;QACxB,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC;QACxB,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC;QACxB,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;QACvB,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC;QACjB,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAChC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,qBAAqB,CACnC,GAAY,EACZ,eAAuB;IAEvB,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,CAAC,OAAO;QAAE,OAAO,eAAe,CAAC;IACrC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IACvC,IAAI,CAAC,GAAG;QAAE,OAAO,eAAe,CAAC;IACjC,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IAC5E,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;QACvD,OAAO,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,CAAC;IACD,OAAO,eAAe,CAAC;AACzB,CAAC;AAED,0EAA0E;AAC1E,SAAS,cAAc,CAAC,GAAY;IAClC,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACjD,MAAM,CAAC,GAAG,GAA4B,CAAC;IACvC,IAAI,CAAC,CAAC,OAAO,YAAY,OAAO;QAAE,OAAO,CAAC,CAAC,OAAO,CAAC;IACnD,8EAA8E;IAC9E,IAAI,CAAC,CAAC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC/C,MAAM,CAAC,GAAG,IAAI,OAAO,EAAE,CAAC;QACxB,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,OAAkC,CAAC,EAAE,CAAC;YAC1E,IAAI,OAAO,CAAC,KAAK,QAAQ;gBAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACzC,CAAC;QACD,OAAO,CAAC,CAAC;IACX,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,eAAe,CAC7B,EAAU,EACV,MAA+B;IAE/B,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;QACpB,OAAO,OAAO,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC3C,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;YAC5B,IAAI,MAAM;gBAAE,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACzD,OAAO,EAAE,CAAC;QACZ,CAAC,EAAE,EAAE,CAAC,CAAC;QACP,MAAM,OAAO,GAAG,GAAG,EAAE;YACnB,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,IAAI,MAAM;gBAAE,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;YACzD,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC;QAC3B,CAAC,CAAC;QACF,IAAI,MAAM;YAAE,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;AACL,CAAC;AAED,8FAA8F;AAC9F,SAAS,cAAc;IACrB,MAAM,CAAC,GAAG,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC;IAC/B,CAAC,CAAC,IAAI,GAAG,YAAY,CAAC;IACtB,OAAO,CAAC,CAAC;AACX,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,UAAU,CAAC,WAAmB;IAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,CAAC;IAC/C,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,GAAG,IAAI,GAAG,MAAM,CAAC,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project-shaped Letta types.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports only the SDK surfaces ai-hub uses, so the rest of the server
|
|
5
|
+
* code never has to traverse `@letta-ai/letta-client`'s deep namespace
|
|
6
|
+
* structure. **No raw HTTP, ever** — these are aliases over the SDK's own
|
|
7
|
+
* types, not parallel inventions (§6.4).
|
|
8
|
+
*
|
|
9
|
+
* If the SDK rev removes or renames one of these, the typechecker flags it
|
|
10
|
+
* at upgrade time and `letta/client.ts` is the single place to adapt.
|
|
11
|
+
*
|
|
12
|
+
* @see SPECIFICATIONS.md §6.4 — SDK-only access pattern
|
|
13
|
+
* @see SPECIFICATIONS.md §6.4.1 — first-class surfaces we depend on
|
|
14
|
+
*/
|
|
15
|
+
import type Letta from "@letta-ai/letta-client";
|
|
16
|
+
import type { AssistantMessage, HiddenReasoningMessage, Message as LettaSdkMessage, ReasoningMessage, ToolCallMessage, UserMessage } from "@letta-ai/letta-client/resources/agents/messages.js";
|
|
17
|
+
import type { LettaStreamingResponse } from "@letta-ai/letta-client/resources/agents/messages.js";
|
|
18
|
+
import type { ToolReturnMessage } from "@letta-ai/letta-client/resources/tools.js";
|
|
19
|
+
/**
|
|
20
|
+
* The underlying SDK client. We `import type` everywhere except in
|
|
21
|
+
* `letta/client.ts` (where the constructor actually runs) so the rest of the
|
|
22
|
+
* codebase stays SDK-version-agnostic in shape.
|
|
23
|
+
*/
|
|
24
|
+
export type LettaSdk = Letta;
|
|
25
|
+
/**
|
|
26
|
+
* One streaming event from `client.conversations.messages.create({ streaming: true })`.
|
|
27
|
+
*
|
|
28
|
+
* The union spans every variant Letta emits in an SSE stream — assistant
|
|
29
|
+
* text, reasoning, tool calls, tool returns, errors, pings, stop reasons,
|
|
30
|
+
* usage stats. `letta/eventTranslator.ts` is the pure function from this
|
|
31
|
+
* union to ai-hub's wire-protocol events (§9).
|
|
32
|
+
*
|
|
33
|
+
* @see SPECIFICATIONS.md §9 — wire protocol
|
|
34
|
+
*/
|
|
35
|
+
export type LettaStreamEvent = LettaStreamingResponse;
|
|
36
|
+
/**
|
|
37
|
+
* Alias for `AssistantMessage` — used by the translator + tests. See
|
|
38
|
+
* `letta/eventTranslator.ts` for the streamed-delta vs. cumulative-content
|
|
39
|
+
* note.
|
|
40
|
+
*/
|
|
41
|
+
export type LettaAssistantMessage = AssistantMessage;
|
|
42
|
+
/** Alias for `ReasoningMessage` — see `eventTranslator.ts`. */
|
|
43
|
+
export type LettaReasoningMessage = ReasoningMessage;
|
|
44
|
+
/** Alias for `HiddenReasoningMessage` — see `eventTranslator.ts`. */
|
|
45
|
+
export type LettaHiddenReasoningMessage = HiddenReasoningMessage;
|
|
46
|
+
/** Alias for `ToolCallMessage` — see `eventTranslator.ts`. */
|
|
47
|
+
export type LettaToolCallMessage = ToolCallMessage;
|
|
48
|
+
/** Alias for `ToolReturnMessage` — see `eventTranslator.ts`. */
|
|
49
|
+
export type LettaToolReturnMessage = ToolReturnMessage;
|
|
50
|
+
/** Alias for `UserMessage` — only appears in history reads, not in streams. */
|
|
51
|
+
export type LettaUserMessage = UserMessage;
|
|
52
|
+
/** Full assembled Letta message (used by `messages.list`). */
|
|
53
|
+
export type LettaMessage = LettaSdkMessage;
|
|
54
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/letta/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,KAAK,MAAM,wBAAwB,CAAC;AAChD,OAAO,KAAK,EACV,gBAAgB,EAChB,sBAAsB,EACtB,OAAO,IAAI,eAAe,EAC1B,gBAAgB,EAChB,eAAe,EACf,WAAW,EACZ,MAAM,qDAAqD,CAAC;AAC7D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qDAAqD,CAAC;AAClG,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAC;AAEnF;;;;GAIG;AACH,MAAM,MAAM,QAAQ,GAAG,KAAK,CAAC;AAE7B;;;;;;;;;GASG;AACH,MAAM,MAAM,gBAAgB,GAAG,sBAAsB,CAAC;AAEtD;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAAG,gBAAgB,CAAC;AACrD,+DAA+D;AAC/D,MAAM,MAAM,qBAAqB,GAAG,gBAAgB,CAAC;AACrD,qEAAqE;AACrE,MAAM,MAAM,2BAA2B,GAAG,sBAAsB,CAAC;AACjE,8DAA8D;AAC9D,MAAM,MAAM,oBAAoB,GAAG,eAAe,CAAC;AACnD,gEAAgE;AAChE,MAAM,MAAM,sBAAsB,GAAG,iBAAiB,CAAC;AACvD,+EAA+E;AAC/E,MAAM,MAAM,gBAAgB,GAAG,WAAW,CAAC;AAE3C,8DAA8D;AAC9D,MAAM,MAAM,YAAY,GAAG,eAAe,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Project-shaped Letta types.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports only the SDK surfaces ai-hub uses, so the rest of the server
|
|
5
|
+
* code never has to traverse `@letta-ai/letta-client`'s deep namespace
|
|
6
|
+
* structure. **No raw HTTP, ever** — these are aliases over the SDK's own
|
|
7
|
+
* types, not parallel inventions (§6.4).
|
|
8
|
+
*
|
|
9
|
+
* If the SDK rev removes or renames one of these, the typechecker flags it
|
|
10
|
+
* at upgrade time and `letta/client.ts` is the single place to adapt.
|
|
11
|
+
*
|
|
12
|
+
* @see SPECIFICATIONS.md §6.4 — SDK-only access pattern
|
|
13
|
+
* @see SPECIFICATIONS.md §6.4.1 — first-class surfaces we depend on
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/letta/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured logging via pino.
|
|
3
|
+
*
|
|
4
|
+
* Production: raw JSON to stdout, one line per record (§6.10 — twelve-factor
|
|
5
|
+
* pattern). Dev: piped through `pino-pretty` for human-readable output, gated
|
|
6
|
+
* on `NODE_ENV !== "production"` or `LOG_LEVEL=debug`.
|
|
7
|
+
*
|
|
8
|
+
* **Redaction is a load-bearing security control** (§13.3). pino's built-in
|
|
9
|
+
* `redact` option is the single point of truth — every log call goes through
|
|
10
|
+
* the configured logger, and a regression test asserts a known-secret value
|
|
11
|
+
* never appears in captured stdout.
|
|
12
|
+
*
|
|
13
|
+
* @see SPECIFICATIONS.md §6.10 — observability
|
|
14
|
+
* @see SPECIFICATIONS.md §13.3 — secrets
|
|
15
|
+
*/
|
|
16
|
+
import { type Logger as PinoLogger } from "pino";
|
|
17
|
+
/** What the rest of the server consumes — keeps pino out of import surfaces. */
|
|
18
|
+
export type Logger = PinoLogger;
|
|
19
|
+
/**
|
|
20
|
+
* Optional shape passed to `createLogger`. The dev-mode pretty-print stream
|
|
21
|
+
* is set up by the caller (the dev/server entrypoint) — `createLogger` itself
|
|
22
|
+
* stays format-agnostic so tests can pass a writable stream.
|
|
23
|
+
*/
|
|
24
|
+
export interface CreateLoggerOptions {
|
|
25
|
+
/** pino level (`fatal | error | warn | info | debug | trace`). Default `info`. */
|
|
26
|
+
level?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Optional destination. Tests pass a memory stream so they can assert on
|
|
29
|
+
* the captured output. Production passes nothing — pino writes to stdout
|
|
30
|
+
* by default.
|
|
31
|
+
*/
|
|
32
|
+
destination?: NodeJS.WritableStream;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Builds a pino logger with the project's redaction policy applied.
|
|
36
|
+
*
|
|
37
|
+
* @param opts - level + optional destination override.
|
|
38
|
+
* @returns the pino logger instance.
|
|
39
|
+
*/
|
|
40
|
+
export declare function createLogger(opts?: CreateLoggerOptions): Logger;
|
|
41
|
+
/**
|
|
42
|
+
* The redaction-path list — exposed for tests so a new path added in
|
|
43
|
+
* `REDACT_PATHS` is automatically covered without test churn.
|
|
44
|
+
*
|
|
45
|
+
* @returns a fresh array of the redaction paths used by `createLogger`.
|
|
46
|
+
*/
|
|
47
|
+
export declare function getRedactPaths(): string[];
|
|
48
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/observability/logger.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAQ,KAAK,MAAM,IAAI,UAAU,EAAsB,MAAM,MAAM,CAAC;AAE3E,gFAAgF;AAChF,MAAM,MAAM,MAAM,GAAG,UAAU,CAAC;AAyChC;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,kFAAkF;IAClF,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,cAAc,CAAC;CACrC;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,GAAE,mBAAwB,GAAG,MAAM,CAUnE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,IAAI,MAAM,EAAE,CAEzC"}
|