@evomap/evolver-proxy 2.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/README.md +49 -0
  2. package/dist/bin/envFile.d.ts +10 -0
  3. package/dist/bin/envFile.js +68 -0
  4. package/dist/bin/evolver-llm-proxy.d.ts +2 -0
  5. package/dist/bin/evolver-llm-proxy.js +111 -0
  6. package/dist/bin/evolver-proxy.d.ts +83 -0
  7. package/dist/bin/evolver-proxy.js +511 -0
  8. package/dist/bin/proxySettings.d.ts +15 -0
  9. package/dist/bin/proxySettings.js +84 -0
  10. package/dist/bin/proxyStorePath.d.ts +1 -0
  11. package/dist/bin/proxyStorePath.js +16 -0
  12. package/dist/daemon/atpConsent.d.ts +13 -0
  13. package/dist/daemon/atpConsent.js +60 -0
  14. package/dist/daemon/ipcConfig.d.ts +1 -0
  15. package/dist/daemon/ipcConfig.js +13 -0
  16. package/dist/daemon/proxyDaemon.d.ts +191 -0
  17. package/dist/daemon/proxyDaemon.js +1015 -0
  18. package/dist/daemon/selectHub.d.ts +16 -0
  19. package/dist/daemon/selectHub.js +30 -0
  20. package/dist/index.d.ts +8 -0
  21. package/dist/index.js +8 -0
  22. package/dist/lifecycle/deployGuard.d.ts +46 -0
  23. package/dist/lifecycle/deployGuard.js +53 -0
  24. package/dist/lifecycle/legacyNodeId.d.ts +96 -0
  25. package/dist/lifecycle/legacyNodeId.js +163 -0
  26. package/dist/lifecycle/manager.d.ts +106 -0
  27. package/dist/lifecycle/manager.js +390 -0
  28. package/dist/llm/bodyCapture.d.ts +31 -0
  29. package/dist/llm/bodyCapture.js +293 -0
  30. package/dist/llm/index.d.ts +3 -0
  31. package/dist/llm/index.js +3 -0
  32. package/dist/llm/server.d.ts +35 -0
  33. package/dist/llm/server.js +359 -0
  34. package/dist/llm/traceBackfill.d.ts +53 -0
  35. package/dist/llm/traceBackfill.js +525 -0
  36. package/dist/llm/traceConfig.d.ts +7 -0
  37. package/dist/llm/traceConfig.js +44 -0
  38. package/dist/llm/traceControl.d.ts +16 -0
  39. package/dist/llm/traceControl.js +85 -0
  40. package/dist/llm/traceEnvelope.d.ts +75 -0
  41. package/dist/llm/traceEnvelope.js +286 -0
  42. package/dist/llm/traceSink.d.ts +61 -0
  43. package/dist/llm/traceSink.js +278 -0
  44. package/dist/llm/traceUploadPayload.d.ts +14 -0
  45. package/dist/llm/traceUploadPayload.js +27 -0
  46. package/dist/llm/upstream.d.ts +68 -0
  47. package/dist/llm/upstream.js +491 -0
  48. package/dist/private/adapterLoader.d.ts +46 -0
  49. package/dist/private/adapterLoader.js +62 -0
  50. package/dist/private/privateRuntimeSmokeOptions.d.ts +15 -0
  51. package/dist/private/privateRuntimeSmokeOptions.js +132 -0
  52. package/dist/router/cachePassthrough.d.ts +3 -0
  53. package/dist/router/cachePassthrough.js +13 -0
  54. package/dist/router/features.d.ts +9 -0
  55. package/dist/router/features.js +52 -0
  56. package/dist/router/index.d.ts +6 -0
  57. package/dist/router/index.js +6 -0
  58. package/dist/router/messagesRoute.d.ts +138 -0
  59. package/dist/router/messagesRoute.js +753 -0
  60. package/dist/router/modelRouter.d.ts +49 -0
  61. package/dist/router/modelRouter.js +66 -0
  62. package/dist/router/providerRoutes.d.ts +42 -0
  63. package/dist/router/providerRoutes.js +1579 -0
  64. package/dist/router/sseScan.d.ts +56 -0
  65. package/dist/router/sseScan.js +543 -0
  66. package/dist/selfUpdate/executor.d.ts +90 -0
  67. package/dist/selfUpdate/executor.js +179 -0
  68. package/dist/selfUpdate/failureCodes.d.ts +33 -0
  69. package/dist/selfUpdate/failureCodes.js +84 -0
  70. package/dist/selfUpdate/index.d.ts +5 -0
  71. package/dist/selfUpdate/index.js +5 -0
  72. package/dist/selfUpdate/lastUpdate.d.ts +43 -0
  73. package/dist/selfUpdate/lastUpdate.js +195 -0
  74. package/dist/selfUpdate/policy.d.ts +3 -0
  75. package/dist/selfUpdate/policy.js +9 -0
  76. package/dist/selfUpdate/releaseBinary.d.ts +56 -0
  77. package/dist/selfUpdate/releaseBinary.js +498 -0
  78. package/dist/selfUpdate/version.d.ts +2 -0
  79. package/dist/selfUpdate/version.js +14 -0
  80. package/dist/sync/engine.d.ts +65 -0
  81. package/dist/sync/engine.js +461 -0
  82. package/package.json +41 -0
@@ -0,0 +1,75 @@
1
+ interface HubKeyEnvelope {
2
+ algorithm: 'rsa-oaep-sha256';
3
+ key_id: string;
4
+ wrapped_key: string;
5
+ }
6
+ export interface HubTraceUsageSummary {
7
+ input_tokens?: number;
8
+ output_tokens?: number;
9
+ cache_creation_input_tokens?: number;
10
+ cache_read_input_tokens?: number;
11
+ }
12
+ export interface HubTracePlaintextSummary {
13
+ event: 'llm_trace_plaintext_summary';
14
+ payload_schema: 'llm_turn_summary';
15
+ ts: string;
16
+ route: string | null;
17
+ provider: string | null;
18
+ wire_api: string | null;
19
+ original_model: string | null;
20
+ chosen_model: string | null;
21
+ tier: string | null;
22
+ reason: string | null;
23
+ fallback: string | null;
24
+ router_enabled: boolean | null;
25
+ upstream_mode: string | null;
26
+ status: number | null;
27
+ stream: boolean | null;
28
+ ttfb_ms: number | null;
29
+ latency_ms: number | null;
30
+ usage?: HubTraceUsageSummary;
31
+ }
32
+ export interface HubDecryptableTraceEnvelope {
33
+ schema_version: 1;
34
+ event: 'llm_trace_envelope';
35
+ encrypted: true;
36
+ payload_schema: 'prism_trace_row';
37
+ algorithm: 'aes-256-gcm';
38
+ key_id: string;
39
+ secret_version?: number;
40
+ producer_generation?: string;
41
+ producer_version?: string;
42
+ producer_component?: string;
43
+ iv: string;
44
+ tag: string;
45
+ ciphertext: string;
46
+ hub_key_envelope?: HubKeyEnvelope;
47
+ plaintext_summary?: HubTracePlaintextSummary;
48
+ payload_complete?: false;
49
+ payload_incomplete_reason?: string;
50
+ hub_uploadable?: false;
51
+ hub_upload_blocked_reason?: string;
52
+ hub_upload_size_bytes?: number;
53
+ hub_upload_max_bytes?: number;
54
+ }
55
+ export interface TraceMaterializationOptions {
56
+ nodeSecretVersion?: unknown;
57
+ nodeSecret?: unknown;
58
+ hubPublicKey?: unknown;
59
+ profileAnalysisEnabled?: unknown;
60
+ producerGeneration?: string;
61
+ producerVersion?: string;
62
+ producerComponent?: string;
63
+ }
64
+ type TraceMaterialization = {
65
+ ok: true;
66
+ record: unknown;
67
+ } | {
68
+ ok: false;
69
+ reason: 'hub_public_key_missing' | 'hub_key_wrap_failed' | 'trace_encrypt_failed';
70
+ error?: string;
71
+ };
72
+ export declare function traceUploadMaxBytes(env?: NodeJS.ProcessEnv): number;
73
+ export declare function materializeTraceForStorage(record: unknown, env?: NodeJS.ProcessEnv, opts?: TraceMaterializationOptions): TraceMaterialization;
74
+ export declare function decryptHubTraceEnvelope(envelope: HubDecryptableTraceEnvelope, privateKey: string): unknown;
75
+ export {};
@@ -0,0 +1,286 @@
1
+ import { createCipheriv, createDecipheriv, createHash, privateDecrypt, publicEncrypt, randomBytes, constants, } from 'node:crypto';
2
+ import { captureBodiesEnabled, positiveIntegerFromEnv } from './bodyCapture.js';
3
+ import { proxyTraceUploadPayloadSizeBytes } from './traceUploadPayload.js';
4
+ function truthy(value) {
5
+ const raw = String(value ?? '').trim().toLowerCase();
6
+ return raw === '1' || raw === 'true' || raw === 'on' || raw === 'yes';
7
+ }
8
+ export function traceUploadMaxBytes(env = process.env) {
9
+ return positiveIntegerFromEnv(env, [
10
+ 'EVOLVER_LLM_TRACE_MAX_UPLOAD_BYTES',
11
+ 'EVOMAP_PROXY_TRACE_MAX_UPLOAD_BYTES',
12
+ 'EVOLVER_LLM_TRACE_ENVELOPE_MAX_CHARS',
13
+ 'EVOMAP_PROXY_TRACE_ENVELOPE_MAX_BYTES',
14
+ 'EVOLVER_HUB_MAILBOX_OUTBOUND_MAX_BODY_BYTES',
15
+ 'EVOMAP_OUTBOUND_SYNC_MAX_BODY_BYTES',
16
+ 'EVOMAP_MAILBOX_OUTBOUND_MAX_BODY_BYTES',
17
+ ], 4 * 1024 * 1024);
18
+ }
19
+ function parseNodeSecretVersion(value) {
20
+ if (typeof value === 'number' && Number.isInteger(value) && value > 0)
21
+ return value;
22
+ if (typeof value !== 'string')
23
+ return undefined;
24
+ const trimmed = value.trim();
25
+ if (!/^[1-9]\d*$/.test(trimmed))
26
+ return undefined;
27
+ const parsed = Number(trimmed);
28
+ return Number.isSafeInteger(parsed) ? parsed : undefined;
29
+ }
30
+ function parseNodeSecret(value) {
31
+ if (typeof value !== 'string')
32
+ return undefined;
33
+ const trimmed = value.trim();
34
+ return /^[a-f0-9]{64}$/i.test(trimmed) ? trimmed : undefined;
35
+ }
36
+ function deriveNodeSecretTraceKey(nodeSecret) {
37
+ return createHash('sha256').update(`evomap-proxy-trace-v1:${nodeSecret}`, 'utf8').digest();
38
+ }
39
+ function traceEnvelopeRequired(env = process.env, opts = {}) {
40
+ return truthy(env['EVOLVER_LLM_TRACE_ENCRYPTION'])
41
+ || truthy(env['EVOMAP_PROXY_TRACE_ENCRYPTION'])
42
+ || truthy(env['EVOLVER_LLM_TRACE_PROFILE_ANALYSIS'])
43
+ || truthy(env['EVOMAP_PROXY_TRACE_PROFILE_ANALYSIS'])
44
+ || opts.profileAnalysisEnabled === true
45
+ || captureBodiesEnabled(env);
46
+ }
47
+ function resolveTraceHubPublicKey(env = process.env, explicitPublicKey) {
48
+ const key = String(env['EVOLVER_LLM_TRACE_HUB_PUBLIC_KEY']
49
+ || env['EVOMAP_PROXY_TRACE_HUB_PUBLIC_KEY']
50
+ || (typeof explicitPublicKey === 'string' ? explicitPublicKey : '')
51
+ || '').trim();
52
+ return key || null;
53
+ }
54
+ function resolveNodeSecretVersion(opts) {
55
+ return parseNodeSecretVersion(opts.nodeSecretVersion);
56
+ }
57
+ function optionalShortString(value, max) {
58
+ const trimmed = String(value ?? '').trim();
59
+ return trimmed.length > 0 && trimmed.length <= max ? trimmed : undefined;
60
+ }
61
+ /**
62
+ * Producer-generation enum, kept byte-for-byte in lockstep with three consumers:
63
+ * - hub `normalizeProducerGeneration` (proxyTraceWarehouseService.js) — lenient: anything else → 'unknown'
64
+ * - v2 `isProducerGeneration` (traceBackfill.ts) — case-sensitive 'v1'|'v2'|'unknown'
65
+ * - v1 extractor `isProducerGeneration` (extractor.js)
66
+ * The default for unset is 'v2' (this is the v2 proxy). A non-empty value outside the enum is normalized to
67
+ * 'unknown' rather than passed through, so a future caller wiring e.g. 'v3' can never produce an envelope that
68
+ * fails our own backfill validation (isProducerGeneration). Output is ALWAYS within {'v1','v2','unknown'}.
69
+ */
70
+ function normalizeProducerGeneration(value) {
71
+ // Unset/empty → 'v2' (this is the v2 proxy). Any NON-EMPTY value outside the enum — including an
72
+ // over-length string — normalizes to 'unknown' (hub slices to 16 then maps non-v1/v2 → 'unknown'; we
73
+ // must not let a >16 junk value fall through to the 'v2' default). Output is ALWAYS within the enum.
74
+ const normalized = String(value ?? '').trim().toLowerCase();
75
+ if (normalized === '')
76
+ return 'v2';
77
+ return normalized === 'v1' || normalized === 'v2' || normalized === 'unknown' ? normalized : 'unknown';
78
+ }
79
+ function traceProducerMetadata(opts) {
80
+ const producerVersion = optionalShortString(opts.producerVersion, 32);
81
+ return {
82
+ producer_generation: normalizeProducerGeneration(opts.producerGeneration),
83
+ producer_component: optionalShortString(opts.producerComponent, 32) ?? 'proxy',
84
+ ...(producerVersion ? { producer_version: producerVersion } : {}),
85
+ };
86
+ }
87
+ function isRecord(value) {
88
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
89
+ }
90
+ function stringOrNull(value) {
91
+ return typeof value === 'string' ? value : null;
92
+ }
93
+ function booleanOrNull(value) {
94
+ return typeof value === 'boolean' ? value : null;
95
+ }
96
+ function numberOrNull(value) {
97
+ return typeof value === 'number' && Number.isFinite(value) ? value : null;
98
+ }
99
+ function traceUsageSummary(value) {
100
+ if (!isRecord(value))
101
+ return undefined;
102
+ const out = {};
103
+ for (const key of ['input_tokens', 'output_tokens', 'cache_creation_input_tokens', 'cache_read_input_tokens']) {
104
+ const n = value[key];
105
+ if (typeof n === 'number' && Number.isFinite(n))
106
+ out[key] = n;
107
+ }
108
+ return Object.keys(out).length > 0 ? out : undefined;
109
+ }
110
+ function plaintextSummary(record) {
111
+ if (!isRecord(record) || record['event'] !== 'llm_turn')
112
+ return undefined;
113
+ const usage = traceUsageSummary(record['usage']);
114
+ return {
115
+ event: 'llm_trace_plaintext_summary',
116
+ payload_schema: 'llm_turn_summary',
117
+ ts: stringOrNull(record['ts']) ?? '',
118
+ route: stringOrNull(record['route']),
119
+ provider: stringOrNull(record['provider']),
120
+ wire_api: stringOrNull(record['wire_api']),
121
+ original_model: stringOrNull(record['original_model']),
122
+ chosen_model: stringOrNull(record['chosen_model']),
123
+ tier: stringOrNull(record['tier']),
124
+ reason: stringOrNull(record['reason']),
125
+ fallback: stringOrNull(record['fallback']),
126
+ router_enabled: booleanOrNull(record['router_enabled']),
127
+ upstream_mode: stringOrNull(record['upstream_mode']),
128
+ status: numberOrNull(record['status']),
129
+ stream: booleanOrNull(record['stream']),
130
+ ttfb_ms: numberOrNull(record['ttfb_ms']),
131
+ latency_ms: numberOrNull(record['latency_ms']),
132
+ ...(usage ? { usage } : {}),
133
+ };
134
+ }
135
+ function incompleteBodyEnvelopeReason(value) {
136
+ if (typeof value !== 'string')
137
+ return undefined;
138
+ try {
139
+ const parsed = JSON.parse(value);
140
+ if (!isRecord(parsed))
141
+ return undefined;
142
+ if (parsed['capture_complete'] === false || parsed['body_omitted'] === true) {
143
+ return typeof parsed['reason'] === 'string' ? parsed['reason'].slice(0, 64) : 'body_capture_incomplete';
144
+ }
145
+ }
146
+ catch {
147
+ return undefined;
148
+ }
149
+ return undefined;
150
+ }
151
+ function traceRecordIncompleteReason(record) {
152
+ if (!isRecord(record))
153
+ return undefined;
154
+ if (record['body_truncated'] === true)
155
+ return 'body_truncated';
156
+ const requestReason = incompleteBodyEnvelopeReason(record['requestBody']);
157
+ if (requestReason)
158
+ return requestReason;
159
+ const responseReason = incompleteBodyEnvelopeReason(record['responseBody']);
160
+ if (responseReason)
161
+ return responseReason;
162
+ const attempts = record['attempts'];
163
+ if (!Array.isArray(attempts))
164
+ return undefined;
165
+ for (const attempt of attempts) {
166
+ if (!isRecord(attempt))
167
+ continue;
168
+ if (attempt['body_truncated'] === true)
169
+ return 'attempt_body_truncated';
170
+ const attemptRequestReason = incompleteBodyEnvelopeReason(attempt['requestBody']);
171
+ if (attemptRequestReason)
172
+ return attemptRequestReason;
173
+ const attemptResponseReason = incompleteBodyEnvelopeReason(attempt['responseBody']);
174
+ if (attemptResponseReason)
175
+ return attemptResponseReason;
176
+ }
177
+ return undefined;
178
+ }
179
+ function annotateUploadStatus(envelope, record, env) {
180
+ let out = envelope;
181
+ const incompleteReason = traceRecordIncompleteReason(record);
182
+ if (incompleteReason) {
183
+ out = {
184
+ ...out,
185
+ payload_complete: false,
186
+ payload_incomplete_reason: incompleteReason,
187
+ };
188
+ }
189
+ if (out.hub_uploadable === false)
190
+ return out;
191
+ const maxUploadBytes = traceUploadMaxBytes(env);
192
+ const sizeBytes = proxyTraceUploadPayloadSizeBytes(out);
193
+ if (sizeBytes <= maxUploadBytes)
194
+ return out;
195
+ let blocked = {
196
+ ...out,
197
+ hub_uploadable: false,
198
+ hub_upload_blocked_reason: 'max_upload_bytes',
199
+ hub_upload_max_bytes: maxUploadBytes,
200
+ };
201
+ for (let i = 0; i < 4; i += 1) {
202
+ const wrappedSizeBytes = proxyTraceUploadPayloadSizeBytes(blocked);
203
+ if (blocked.hub_upload_size_bytes === wrappedSizeBytes)
204
+ return blocked;
205
+ blocked = { ...blocked, hub_upload_size_bytes: wrappedSizeBytes };
206
+ }
207
+ return blocked;
208
+ }
209
+ export function materializeTraceForStorage(record, env = process.env, opts = {}) {
210
+ if (!traceEnvelopeRequired(env, opts))
211
+ return { ok: true, record };
212
+ const publicKey = resolveTraceHubPublicKey(env, opts.hubPublicKey);
213
+ try {
214
+ const nodeSecretVersion = resolveNodeSecretVersion(opts);
215
+ const nodeSecret = parseNodeSecret(opts.nodeSecret);
216
+ const nodeSecretKey = nodeSecret ? deriveNodeSecretTraceKey(nodeSecret) : undefined;
217
+ const dataKey = nodeSecretKey ?? randomBytes(32);
218
+ const iv = randomBytes(12);
219
+ // Pin authTagLength to 16 (#281): without it the decrypt side would accept a short/truncated GCM tag, which is
220
+ // easier to forge. Encrypt + decrypt both fix 16 so only a full 128-bit tag authenticates.
221
+ const cipher = createCipheriv('aes-256-gcm', dataKey, iv, { authTagLength: 16 });
222
+ const plaintext = Buffer.from(JSON.stringify(record), 'utf8');
223
+ const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
224
+ const tag = cipher.getAuthTag();
225
+ let hubKeyEnvelope;
226
+ try {
227
+ if (publicKey) {
228
+ const wrapped = publicEncrypt({
229
+ key: publicKey,
230
+ padding: constants.RSA_PKCS1_OAEP_PADDING,
231
+ oaepHash: 'sha256',
232
+ }, dataKey);
233
+ hubKeyEnvelope = {
234
+ algorithm: 'rsa-oaep-sha256',
235
+ key_id: createHash('sha256').update(publicKey).digest('hex').slice(0, 16),
236
+ wrapped_key: wrapped.toString('base64'),
237
+ };
238
+ }
239
+ else if (!nodeSecretKey) {
240
+ return { ok: false, reason: 'hub_public_key_missing' };
241
+ }
242
+ }
243
+ catch (err) {
244
+ return { ok: false, reason: 'hub_key_wrap_failed', error: err instanceof Error ? err.message : String(err) };
245
+ }
246
+ const summary = plaintextSummary(record);
247
+ const envelope = {
248
+ schema_version: 1,
249
+ event: 'llm_trace_envelope',
250
+ encrypted: true,
251
+ payload_schema: 'prism_trace_row',
252
+ algorithm: 'aes-256-gcm',
253
+ key_id: createHash('sha256').update(dataKey).digest('hex').slice(0, 16),
254
+ ...(nodeSecretVersion !== undefined ? { secret_version: nodeSecretVersion } : {}),
255
+ ...traceProducerMetadata(opts),
256
+ iv: iv.toString('base64'),
257
+ tag: tag.toString('base64'),
258
+ ciphertext: ciphertext.toString('base64'),
259
+ ...(hubKeyEnvelope ? { hub_key_envelope: hubKeyEnvelope } : {}),
260
+ ...(summary ? { plaintext_summary: summary } : {}),
261
+ };
262
+ return {
263
+ ok: true,
264
+ record: annotateUploadStatus(envelope, record, env),
265
+ };
266
+ }
267
+ catch (err) {
268
+ return { ok: false, reason: 'trace_encrypt_failed', error: err instanceof Error ? err.message : String(err) };
269
+ }
270
+ }
271
+ export function decryptHubTraceEnvelope(envelope, privateKey) {
272
+ if (!envelope.hub_key_envelope)
273
+ throw new Error('hub_key_envelope_required');
274
+ const dataKey = privateDecrypt({
275
+ key: privateKey,
276
+ padding: constants.RSA_PKCS1_OAEP_PADDING,
277
+ oaepHash: 'sha256',
278
+ }, Buffer.from(envelope.hub_key_envelope.wrapped_key, 'base64'));
279
+ const decipher = createDecipheriv('aes-256-gcm', dataKey, Buffer.from(envelope.iv, 'base64'), { authTagLength: 16 });
280
+ decipher.setAuthTag(Buffer.from(envelope.tag, 'base64'));
281
+ const plaintext = Buffer.concat([
282
+ decipher.update(Buffer.from(envelope.ciphertext, 'base64')),
283
+ decipher.final(),
284
+ ]);
285
+ return JSON.parse(plaintext.toString('utf8'));
286
+ }
@@ -0,0 +1,61 @@
1
+ import type { LlmTurnTrace, RouterLogger } from '../router/messagesRoute.js';
2
+ import { type TraceBackfillStats, type TraceUploadMailboxStore } from './traceBackfill.js';
3
+ export declare const DEFAULT_TRACE_FILE_CAP_BYTES: number;
4
+ export interface JsonlTraceSinkOptions {
5
+ dir: string;
6
+ /** Injected clock (testability + day-file naming). Default Date.now. */
7
+ now?: () => number;
8
+ /** Per-day-file size cap; beyond it the day's writes are dropped (warned once). Disk is a hard budget for
9
+ * an always-on proxy — dropping trace tails beats growing an unbounded JSONL until the daemon OOMs on it. */
10
+ fileCapBytes?: number;
11
+ logger?: RouterLogger;
12
+ env?: NodeJS.ProcessEnv;
13
+ /** Optional outbound mailbox path for Hub-decryptable trace envelopes. Failures are best-effort only. */
14
+ mailboxStore?: TraceUploadMailboxStore;
15
+ runtimeNamespace?: string;
16
+ sourceAgent?: string;
17
+ targetAgent?: string;
18
+ nodeSecretVersion?: unknown;
19
+ producerVersion?: string;
20
+ }
21
+ interface TraceNodeSecretMaterial {
22
+ nodeSecret?: string;
23
+ nodeSecretVersion?: number;
24
+ }
25
+ export declare function resolveTraceNodeSecretMaterial(env?: NodeJS.ProcessEnv, store?: Pick<TraceUploadMailboxStore, 'getState'>, explicitNodeSecretVersion?: unknown): TraceNodeSecretMaterial;
26
+ export declare function resolveTraceNodeSecretVersion(env?: NodeJS.ProcessEnv, store?: Pick<TraceUploadMailboxStore, 'getState'>, explicitNodeSecretVersion?: unknown): number | undefined;
27
+ /**
28
+ * Append-only NDJSON sink, one file per UTC day (`llm-trace-YYYYMMDD.jsonl`). Fire-and-forget writes; any
29
+ * failure (bad dir, full disk, cap reached) degrades to a single warn and NEVER propagates — trace capture
30
+ * must not be able to break serving.
31
+ */
32
+ export declare class JsonlTraceSink {
33
+ private readonly opts;
34
+ private readonly now;
35
+ private readonly cap;
36
+ private readonly log;
37
+ private warned;
38
+ private chmodWarned;
39
+ private envelopeWarned;
40
+ private plaintextWarned;
41
+ private mailboxWarned;
42
+ private cappedDay;
43
+ private securedFiles;
44
+ /** Appends are chained, not raced: two in-flight fs appends to one path have no ordering guarantee, and
45
+ * out-of-order trace lines would corrupt the day-file as a timeline. Still fire-and-forget to callers. */
46
+ private tail;
47
+ constructor(opts: JsonlTraceSinkOptions);
48
+ filePath(): string;
49
+ /**
50
+ * Await the appends queued so far. write() is fire-and-forget (it chains the fs append onto `tail` and returns),
51
+ * so a caller that needs the trace durable on disk — a graceful shutdown, or a test asserting file contents —
52
+ * must await this rather than guess a wall-clock delay. Never rejects: write() already swallows append errors
53
+ * into `tail`.
54
+ */
55
+ flush(): Promise<void>;
56
+ write: (record: LlmTurnTrace) => void;
57
+ private secureTraceFile;
58
+ private enqueueOutboundTrace;
59
+ backfillExistingTraceUploads(): TraceBackfillStats | null;
60
+ }
61
+ export {};