@gethelio/proxy 0.12.0 → 0.13.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.
package/dist/index.d.ts CHANGED
@@ -51,6 +51,7 @@ declare const policiesSchema: z.ZodObject<{
51
51
  contains: z.ZodOptional<z.ZodString>;
52
52
  regex: z.ZodOptional<z.ZodString>;
53
53
  }, z.core.$strict>]>>>;
54
+ upstreams: z.ZodOptional<z.ZodArray<z.ZodString>>;
54
55
  }, z.core.$strict>;
55
56
  action: z.ZodEnum<{
56
57
  deny: "deny";
@@ -171,6 +172,7 @@ declare const budgetSchema: z.ZodObject<{
171
172
  contains: z.ZodOptional<z.ZodString>;
172
173
  regex: z.ZodOptional<z.ZodString>;
173
174
  }, z.core.$strict>>>;
175
+ upstreams: z.ZodOptional<z.ZodArray<z.ZodString>>;
174
176
  }, z.core.$strict>;
175
177
  field: z.ZodString;
176
178
  }, z.core.$strict>>>;
@@ -190,11 +192,237 @@ declare const approvalChannelSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
190
192
  type: z.ZodLiteral<"dashboard">;
191
193
  name: z.ZodOptional<z.ZodString>;
192
194
  }, z.core.$strict>], "type">;
193
- /** Zod schema for the complete `helio.yaml` configuration file. */
194
- declare const helioConfigSchema: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodObject<{
195
+ declare const singularConfigSchema: z.ZodObject<{
196
+ listen: z.ZodPrefault<z.ZodObject<{
197
+ port: z.ZodDefault<z.ZodNumber>;
198
+ host: z.ZodDefault<z.ZodString>;
199
+ allowed_origins: z.ZodDefault<z.ZodArray<z.ZodString>>;
200
+ }, z.core.$strict>>;
201
+ environment: z.ZodOptional<z.ZodString>;
202
+ session: z.ZodPrefault<z.ZodObject<{
203
+ identity: z.ZodDefault<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
204
+ source: z.ZodLiteral<"header">;
205
+ name: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<string, string>>;
206
+ }, z.core.$strict>, z.ZodObject<{
207
+ source: z.ZodLiteral<"meta">;
208
+ }, z.core.$strict>, z.ZodObject<{
209
+ source: z.ZodLiteral<"legacy_header">;
210
+ }, z.core.$strict>], "source">>>;
211
+ on_unresolved: z.ZodDefault<z.ZodEnum<{
212
+ deny: "deny";
213
+ anonymous: "anonymous";
214
+ }>>;
215
+ }, z.core.$strict>>;
216
+ policies: z.ZodPrefault<z.ZodObject<{
217
+ default: z.ZodDefault<z.ZodEnum<{
218
+ deny: "deny";
219
+ allow: "allow";
220
+ }>>;
221
+ flag_destructive: z.ZodOptional<z.ZodEnum<{
222
+ require_approval: "require_approval";
223
+ log: "log";
224
+ }>>;
225
+ dry_run: z.ZodDefault<z.ZodBoolean>;
226
+ rules: z.ZodDefault<z.ZodArray<z.ZodObject<{
227
+ name: z.ZodOptional<z.ZodString>;
228
+ match: z.ZodObject<{
229
+ tool: z.ZodOptional<z.ZodString>;
230
+ annotations: z.ZodOptional<z.ZodObject<{
231
+ readOnlyHint: z.ZodOptional<z.ZodBoolean>;
232
+ destructiveHint: z.ZodOptional<z.ZodBoolean>;
233
+ idempotentHint: z.ZodOptional<z.ZodBoolean>;
234
+ openWorldHint: z.ZodOptional<z.ZodBoolean>;
235
+ }, z.core.$strict>>;
236
+ input: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
237
+ eq: z.ZodOptional<z.ZodUnknown>;
238
+ neq: z.ZodOptional<z.ZodUnknown>;
239
+ gt: z.ZodOptional<z.ZodNumber>;
240
+ gte: z.ZodOptional<z.ZodNumber>;
241
+ lt: z.ZodOptional<z.ZodNumber>;
242
+ lte: z.ZodOptional<z.ZodNumber>;
243
+ contains: z.ZodOptional<z.ZodString>;
244
+ regex: z.ZodOptional<z.ZodString>;
245
+ }, z.core.$strict>>>;
246
+ environment: z.ZodOptional<z.ZodString>;
247
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
248
+ eq: z.ZodOptional<z.ZodString>;
249
+ neq: z.ZodOptional<z.ZodString>;
250
+ contains: z.ZodOptional<z.ZodString>;
251
+ regex: z.ZodOptional<z.ZodString>;
252
+ }, z.core.$strict>]>>>;
253
+ upstreams: z.ZodOptional<z.ZodArray<z.ZodString>>;
254
+ }, z.core.$strict>;
255
+ action: z.ZodEnum<{
256
+ deny: "deny";
257
+ allow: "allow";
258
+ require_approval: "require_approval";
259
+ rate_limit: "rate_limit";
260
+ spend_limit: "spend_limit";
261
+ dry_run: "dry_run";
262
+ }>;
263
+ approval: z.ZodOptional<z.ZodObject<{
264
+ channel: z.ZodString;
265
+ timeout: z.ZodOptional<z.ZodString>;
266
+ delegates: z.ZodOptional<z.ZodArray<z.ZodString>>;
267
+ escalation_after: z.ZodOptional<z.ZodString>;
268
+ }, z.core.$strict>>;
269
+ evidence: z.ZodOptional<z.ZodObject<{
270
+ requires: z.ZodArray<z.ZodString>;
271
+ }, z.core.$strict>>;
272
+ requires: z.ZodOptional<z.ZodArray<z.ZodString>>;
273
+ requires_success: z.ZodOptional<z.ZodBoolean>;
274
+ limits: z.ZodOptional<z.ZodObject<{
275
+ max_calls: z.ZodOptional<z.ZodNumber>;
276
+ window: z.ZodOptional<z.ZodString>;
277
+ key: z.ZodOptional<z.ZodEnum<{
278
+ tool: "tool";
279
+ agent: "agent";
280
+ session: "session";
281
+ sender_id: "sender_id";
282
+ }>>;
283
+ max_spend: z.ZodOptional<z.ZodObject<{
284
+ field: z.ZodString;
285
+ limit: z.ZodNumber;
286
+ currency: z.ZodString;
287
+ window: z.ZodString;
288
+ key: z.ZodOptional<z.ZodEnum<{
289
+ tool: "tool";
290
+ agent: "agent";
291
+ session: "session";
292
+ sender_id: "sender_id";
293
+ }>>;
294
+ }, z.core.$strict>>;
295
+ }, z.core.$strict>>;
296
+ feedback: z.ZodOptional<z.ZodObject<{
297
+ message: z.ZodString;
298
+ suggestion: z.ZodOptional<z.ZodString>;
299
+ }, z.core.$strict>>;
300
+ }, z.core.$strict>>>;
301
+ install: z.ZodOptional<z.ZodObject<{
302
+ default: z.ZodDefault<z.ZodEnum<{
303
+ deny: "deny";
304
+ allow: "allow";
305
+ }>>;
306
+ rules: z.ZodDefault<z.ZodArray<z.ZodObject<{
307
+ name: z.ZodOptional<z.ZodString>;
308
+ match: z.ZodObject<{
309
+ name: z.ZodOptional<z.ZodString>;
310
+ source: z.ZodOptional<z.ZodString>;
311
+ metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
312
+ eq: z.ZodOptional<z.ZodString>;
313
+ neq: z.ZodOptional<z.ZodString>;
314
+ contains: z.ZodOptional<z.ZodString>;
315
+ regex: z.ZodOptional<z.ZodString>;
316
+ }, z.core.$strict>]>>>;
317
+ }, z.core.$strict>;
318
+ action: z.ZodEnum<{
319
+ allow: "allow";
320
+ deny_install: "deny_install";
321
+ }>;
322
+ feedback: z.ZodOptional<z.ZodObject<{
323
+ message: z.ZodString;
324
+ suggestion: z.ZodOptional<z.ZodString>;
325
+ }, z.core.$strict>>;
326
+ }, z.core.$strict>>>;
327
+ }, z.core.$strict>>;
328
+ on_tool_drift: z.ZodOptional<z.ZodEnum<{
329
+ require_approval: "require_approval";
330
+ log: "log";
331
+ block: "block";
332
+ }>>;
333
+ tool_revalidation: z.ZodOptional<z.ZodObject<{
334
+ enabled: z.ZodDefault<z.ZodBoolean>;
335
+ interval: z.ZodDefault<z.ZodString>;
336
+ max_advertised_ttl: z.ZodOptional<z.ZodString>;
337
+ }, z.core.$strict>>;
338
+ hot_reload: z.ZodOptional<z.ZodBoolean>;
339
+ }, z.core.$strict>>;
340
+ budgets: z.ZodDefault<z.ZodArray<z.ZodObject<{
341
+ name: z.ZodString;
342
+ limit: z.ZodNumber;
343
+ currency: z.ZodString;
344
+ window: z.ZodUnion<readonly [z.ZodString, z.ZodLiteral<"session">]>;
345
+ key: z.ZodDefault<z.ZodEnum<{
346
+ session: "session";
347
+ sender_id: "sender_id";
348
+ global: "global";
349
+ }>>;
350
+ on_exceed: z.ZodDefault<z.ZodEnum<{
351
+ deny: "deny";
352
+ require_approval: "require_approval";
353
+ }>>;
354
+ approval: z.ZodOptional<z.ZodObject<{
355
+ channel: z.ZodString;
356
+ timeout: z.ZodOptional<z.ZodString>;
357
+ delegates: z.ZodOptional<z.ZodArray<z.ZodString>>;
358
+ escalation_after: z.ZodOptional<z.ZodString>;
359
+ }, z.core.$strict>>;
360
+ idle_ttl: z.ZodOptional<z.ZodString>;
361
+ contributors: z.ZodArray<z.ZodPipe<z.ZodUnknown, z.ZodObject<{
362
+ match: z.ZodObject<{
363
+ tool: z.ZodString;
364
+ input: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
365
+ eq: z.ZodOptional<z.ZodUnknown>;
366
+ neq: z.ZodOptional<z.ZodUnknown>;
367
+ gt: z.ZodOptional<z.ZodNumber>;
368
+ gte: z.ZodOptional<z.ZodNumber>;
369
+ lt: z.ZodOptional<z.ZodNumber>;
370
+ lte: z.ZodOptional<z.ZodNumber>;
371
+ contains: z.ZodOptional<z.ZodString>;
372
+ regex: z.ZodOptional<z.ZodString>;
373
+ }, z.core.$strict>>>;
374
+ upstreams: z.ZodOptional<z.ZodArray<z.ZodString>>;
375
+ }, z.core.$strict>;
376
+ field: z.ZodString;
377
+ }, z.core.$strict>>>;
378
+ }, z.core.$strict>>>;
379
+ approval: z.ZodPrefault<z.ZodObject<{
380
+ timeout: z.ZodDefault<z.ZodString>;
381
+ default_on_timeout: z.ZodDefault<z.ZodEnum<{
382
+ deny: "deny";
383
+ allow: "allow";
384
+ }>>;
385
+ channels: z.ZodDefault<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
386
+ type: z.ZodLiteral<"slack">;
387
+ name: z.ZodOptional<z.ZodString>;
388
+ bot_token: z.ZodString;
389
+ signing_secret: z.ZodString;
390
+ channel: z.ZodString;
391
+ }, z.core.$strict>, z.ZodObject<{
392
+ type: z.ZodLiteral<"webhook">;
393
+ name: z.ZodOptional<z.ZodString>;
394
+ url: z.ZodString;
395
+ secret: z.ZodOptional<z.ZodString>;
396
+ }, z.core.$strict>, z.ZodObject<{
397
+ type: z.ZodLiteral<"dashboard">;
398
+ name: z.ZodOptional<z.ZodString>;
399
+ }, z.core.$strict>], "type">>>;
400
+ }, z.core.$strict>>;
401
+ audit: z.ZodPrefault<z.ZodObject<{
402
+ storage: z.ZodDefault<z.ZodEnum<{
403
+ sqlite: "sqlite";
404
+ }>>;
405
+ path: z.ZodDefault<z.ZodString>;
406
+ retention: z.ZodDefault<z.ZodString>;
407
+ include_responses: z.ZodDefault<z.ZodBoolean>;
408
+ }, z.core.$strict>>;
409
+ dashboard: z.ZodPrefault<z.ZodObject<{
410
+ enabled: z.ZodDefault<z.ZodBoolean>;
411
+ port: z.ZodDefault<z.ZodNumber>;
412
+ host: z.ZodDefault<z.ZodString>;
413
+ api_secret: z.ZodOptional<z.ZodString>;
414
+ allow_open_mode: z.ZodDefault<z.ZodBoolean>;
415
+ sse_heartbeat_interval: z.ZodDefault<z.ZodString>;
416
+ }, z.core.$strict>>;
417
+ sdk: z.ZodPrefault<z.ZodObject<{
418
+ enabled: z.ZodDefault<z.ZodBoolean>;
419
+ port: z.ZodDefault<z.ZodNumber>;
420
+ host: z.ZodDefault<z.ZodString>;
421
+ evaluation_ttl: z.ZodDefault<z.ZodString>;
422
+ }, z.core.$strict>>;
195
423
  version: z.ZodLiteral<"1">;
196
424
  upstream: z.ZodObject<{
197
- url: z.ZodString;
425
+ url: z.ZodOptional<z.ZodString>;
198
426
  transport: z.ZodDefault<z.ZodEnum<{
199
427
  "streamable-http": "streamable-http";
200
428
  sse: "sse";
@@ -212,6 +440,8 @@ declare const helioConfigSchema: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.Z
212
440
  forward_headers: z.ZodDefault<z.ZodArray<z.ZodString>>;
213
441
  headers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
214
442
  }, z.core.$strict>;
443
+ }, z.core.$strict>;
444
+ declare const namedConfigSchema: z.ZodObject<{
215
445
  listen: z.ZodPrefault<z.ZodObject<{
216
446
  port: z.ZodDefault<z.ZodNumber>;
217
447
  host: z.ZodDefault<z.ZodString>;
@@ -269,6 +499,7 @@ declare const helioConfigSchema: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.Z
269
499
  contains: z.ZodOptional<z.ZodString>;
270
500
  regex: z.ZodOptional<z.ZodString>;
271
501
  }, z.core.$strict>]>>>;
502
+ upstreams: z.ZodOptional<z.ZodArray<z.ZodString>>;
272
503
  }, z.core.$strict>;
273
504
  action: z.ZodEnum<{
274
505
  deny: "deny";
@@ -389,6 +620,7 @@ declare const helioConfigSchema: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.Z
389
620
  contains: z.ZodOptional<z.ZodString>;
390
621
  regex: z.ZodOptional<z.ZodString>;
391
622
  }, z.core.$strict>>>;
623
+ upstreams: z.ZodOptional<z.ZodArray<z.ZodString>>;
392
624
  }, z.core.$strict>;
393
625
  field: z.ZodString;
394
626
  }, z.core.$strict>>>;
@@ -437,9 +669,38 @@ declare const helioConfigSchema: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.Z
437
669
  host: z.ZodDefault<z.ZodString>;
438
670
  evaluation_ttl: z.ZodDefault<z.ZodString>;
439
671
  }, z.core.$strict>>;
440
- }, z.core.$strict>>;
441
- /** Fully validated and defaulted Helio configuration. */
442
- type HelioConfig = z.infer<typeof helioConfigSchema>;
672
+ version: z.ZodLiteral<"1">;
673
+ upstreams: z.ZodArray<z.ZodObject<{
674
+ url: z.ZodOptional<z.ZodString>;
675
+ transport: z.ZodDefault<z.ZodEnum<{
676
+ "streamable-http": "streamable-http";
677
+ sse: "sse";
678
+ stdio: "stdio";
679
+ }>>;
680
+ protocol_version: z.ZodDefault<z.ZodEnum<{
681
+ auto: "auto";
682
+ "2025-06-18": "2025-06-18";
683
+ "2026-07-28": "2026-07-28";
684
+ }>>;
685
+ command: z.ZodOptional<z.ZodString>;
686
+ args: z.ZodOptional<z.ZodArray<z.ZodString>>;
687
+ connect_timeout: z.ZodDefault<z.ZodString>;
688
+ request_timeout: z.ZodDefault<z.ZodString>;
689
+ forward_headers: z.ZodDefault<z.ZodArray<z.ZodString>>;
690
+ headers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
691
+ name: z.ZodString;
692
+ }, z.core.$strict>>;
693
+ }, z.core.$strict>;
694
+ /** A fully validated and defaulted singular-mode (`upstream:`) configuration. */
695
+ type SingularHelioConfig = z.output<typeof singularConfigSchema>;
696
+ /** A fully validated and defaulted named-mode (`upstreams:`) configuration. */
697
+ type NamedHelioConfig = z.output<typeof namedConfigSchema>;
698
+ /** Fully validated and defaulted Helio configuration — one of the two mode arms. */
699
+ type HelioConfig = SingularHelioConfig | NamedHelioConfig;
700
+ /** Narrow a parsed config to the singular-mode (`upstream:`) arm. */
701
+ declare function isSingularConfig(config: HelioConfig): config is SingularHelioConfig;
702
+ /** Narrow a parsed config to the named-mode (`upstreams:`) arm. */
703
+ declare function isNamedConfig(config: HelioConfig): config is NamedHelioConfig;
443
704
  /** An approval channel configuration (slack, webhook, or dashboard). */
444
705
  type ApprovalChannel$1 = z.infer<typeof approvalChannelSchema>;
445
706
  /** The policies section of the config. */
@@ -530,6 +791,8 @@ interface CompiledMatch {
530
791
  readonly environment?: string;
531
792
  /** Flattened list of metadata conditions (one entry per key+operator pair). */
532
793
  readonly metadata?: readonly MetadataCondition[];
794
+ /** Configured upstream names the rule is scoped to, OR within the list (issue #293). */
795
+ readonly upstreams?: readonly string[];
533
796
  }
534
797
  /** Compiled approval configuration with durations as milliseconds. */
535
798
  interface CompiledApproval {
@@ -673,6 +936,12 @@ interface MatchContext {
673
936
  * request column) is merged in by the decision pipeline, not stored here twice.
674
937
  */
675
938
  readonly metadata?: Readonly<Record<string, unknown>>;
939
+ /**
940
+ * The governed door's configured upstream name (issue #295). A string only on
941
+ * the MCP path when a name is configured; undefined on the sideband and in
942
+ * singular mode, so upstream-scoped rules are inert there by construction.
943
+ */
944
+ readonly upstream?: string;
676
945
  }
677
946
 
678
947
  /** One compiled contributor: which calls feed the budget, and from which field. */
@@ -682,6 +951,13 @@ interface CompiledBudgetContributor {
682
951
  /** Flattened conditions (one per path+operator); absent when unconditioned. */
683
952
  readonly input?: readonly InputCondition[];
684
953
  };
954
+ /**
955
+ * Configured upstream names the contributor is scoped to (issue #293);
956
+ * absent means every door contributes. A scoped contributor never
957
+ * participates when the charge context carries no upstream (sideband,
958
+ * singular mode).
959
+ */
960
+ readonly upstreams?: readonly string[];
685
961
  /** Dot-path into the tool arguments (e.g. "$.amount"), resolved per call. */
686
962
  readonly field: string;
687
963
  }
@@ -836,6 +1112,47 @@ interface CreateAppOptions {
836
1112
  * @param options - Optional sub-apps to mount.
837
1113
  */
838
1114
  declare function createApp(config: HelioConfig, forwarder: McpForwarder, options?: CreateAppOptions): Hono;
1115
+ /**
1116
+ * Optional sub-apps and callbacks for the multi-upstream composition.
1117
+ * The main-port posture is `createApp`'s (see CreateAppOptions): only the
1118
+ * Slack webhook callback belongs beside the MCP doors.
1119
+ */
1120
+ interface CreateMultiAppOptions {
1121
+ /** Slack interactive action handler (mounted at /slack/actions, global). */
1122
+ slackActionApp?: Hono;
1123
+ /**
1124
+ * Recorder for inbound header/body agreement rejections (issue #226),
1125
+ * called with the name of the door that rejected so the audit record can
1126
+ * attribute it. Enforcement does not depend on it.
1127
+ */
1128
+ onHeaderMismatch?: (rejection: HeaderMismatchRejection, upstreamName: string) => void;
1129
+ /**
1130
+ * @internal Test seam for the per-door concurrent-session cap on the
1131
+ * `/sse/<name>` mounts (issue #232's cap, per-route by construction), so
1132
+ * tests need not mint 1024 real streams per door. The same value goes to
1133
+ * every door's route; the session maps stay per-route-instance. Not wired
1134
+ * to config — absent means the hardcoded 1024.
1135
+ */
1136
+ sse?: {
1137
+ readonly maxConcurrentSessions?: number;
1138
+ };
1139
+ }
1140
+ /**
1141
+ * Create a Hono app serving every named upstream at its own pair of mounts,
1142
+ * `/mcp/<name>` and `/sse/<name>`, each a fresh route stack wrapping that
1143
+ * entry's forwarder. `/healthz` and `/slack/actions` stay global. There is
1144
+ * NO bare-path default: bare `/mcp`, `/sse`, and unknown names get an
1145
+ * explicit 404 catch-all whose JSON-RPC envelope states the path shape and
1146
+ * never enumerates configured names.
1147
+ *
1148
+ * @param config - The validated named-mode Helio configuration.
1149
+ * @param forwarders - One forwarder per configured entry, keyed by name.
1150
+ * Must cover the configured names exactly; missing or extra keys throw
1151
+ * (a missing key would silently mount a dead door, an extra one means the
1152
+ * record drifted from the config — fail at composition, not per request).
1153
+ * @param options - Optional sub-apps and callbacks.
1154
+ */
1155
+ declare function createMultiApp(config: HelioConfig, forwarders: Record<string, McpForwarder>, options?: CreateMultiAppOptions): Hono;
839
1156
  /**
840
1157
  * Start the HTTP server on the configured host and port.
841
1158
  *
@@ -866,6 +1183,11 @@ interface StreamableHttpForwarderOptions {
866
1183
  requestTimeoutMs?: number;
867
1184
  /** `upstream.protocol_version`: `auto` (default) probes; a dated pin skips it. */
868
1185
  protocolVersion?: UpstreamProtocolVersionPin;
1186
+ /**
1187
+ * The operator-chosen upstream entry name (issue #295), threaded to the
1188
+ * session manager's era lifecycle lines. Unset in singular mode.
1189
+ */
1190
+ upstreamName?: string;
869
1191
  }
870
1192
  /**
871
1193
  * Spec-compliant upstream MCP Streamable HTTP client.
@@ -984,6 +1306,11 @@ interface StdioForwarderOptions {
984
1306
  retryDelayMs?: number;
985
1307
  /** Timeout in milliseconds for individual requests. */
986
1308
  requestTimeoutMs?: number;
1309
+ /**
1310
+ * The operator-chosen upstream entry name (multi-upstream mode). Unset in
1311
+ * singular mode — the death line then keeps its bare `[helio]` tag.
1312
+ */
1313
+ upstreamName?: string;
987
1314
  }
988
1315
  /**
989
1316
  * Forward MCP requests to an upstream server via stdio.
@@ -997,6 +1324,7 @@ declare class StdioForwarder implements McpForwarder {
997
1324
  private readonly maxRetries;
998
1325
  private readonly retryDelayMs;
999
1326
  private readonly pending;
1327
+ private readonly logTag;
1000
1328
  private child;
1001
1329
  private buffer;
1002
1330
  private retryCount;
@@ -1156,6 +1484,14 @@ interface AuditRecord {
1156
1484
  * and on records with no MCP wire (drift events, sideband records).
1157
1485
  */
1158
1486
  readonly protocol_version: string | null;
1487
+ /**
1488
+ * Name of the upstream MCP server this record is attributed to (issue
1489
+ * #292). Null in singular mode (no named upstreams configured), on
1490
+ * sideband records (no MCP door), and on rows that predate the column.
1491
+ * Required-nullable like session_source/protocol_version: a producer that
1492
+ * could silently omit it would exempt that door from attribution.
1493
+ */
1494
+ readonly upstream: string | null;
1159
1495
  /** ISO 8601 timestamp of when the record was persisted. */
1160
1496
  readonly created_at: string;
1161
1497
  }
@@ -1171,6 +1507,18 @@ interface AuditQueryFilters {
1171
1507
  readonly blocked?: boolean;
1172
1508
  /** Filter by session ID (exact match). */
1173
1509
  readonly session_id?: string;
1510
+ /**
1511
+ * Filter by session identity source (exact match — the five-value
1512
+ * `header` / `meta` / `legacy_header` / `transport` / `sideband`
1513
+ * vocabulary, issue #250). SQL equality: rows whose session_source is
1514
+ * NULL never match any filter value.
1515
+ */
1516
+ readonly session_source?: string;
1517
+ /**
1518
+ * Filter by upstream name (exact match). SQL equality: rows whose
1519
+ * upstream is NULL never match any filter value.
1520
+ */
1521
+ readonly upstream?: string;
1174
1522
  /** Filter by agent ID (exact match). */
1175
1523
  readonly agent_id?: string;
1176
1524
  /** Include only records created at or after this ISO 8601 timestamp. */
@@ -1239,9 +1587,15 @@ interface AuditAggregateStats {
1239
1587
  readonly reason: string;
1240
1588
  readonly count: number;
1241
1589
  }>;
1242
- /** Top tools by call count (max 10). */
1590
+ /**
1591
+ * Top tools by call count (max 10 rows). Each row is a (tool, upstream)
1592
+ * pair (issue #297): same-named tools on different upstreams stay
1593
+ * distinct. Upstream is null in singular mode, so all-null groups reduce
1594
+ * to exactly the pre-#297 per-tool rows.
1595
+ */
1243
1596
  readonly top_tools: ReadonlyArray<{
1244
1597
  readonly tool_name: string;
1598
+ readonly upstream: string | null;
1245
1599
  readonly count: number;
1246
1600
  }>;
1247
1601
  /** Approval rate (approved / total require_approval decisions), or null if none. */
@@ -1364,8 +1718,15 @@ declare class AuditStore {
1364
1718
  private query;
1365
1719
  /** Count records matching the given filters. */
1366
1720
  count(filters?: AuditQueryFilters): number;
1367
- /** Get aggregate statistics for a time range. */
1368
- aggregate(from?: string, to?: string): AuditAggregateStats;
1721
+ /**
1722
+ * Get aggregate statistics for a time range. The optional upstream filter
1723
+ * scopes EVERY sub-aggregate (totals, by_decision, by_block_reason,
1724
+ * top_tools, approval_rate, per_hour) — "analytics for this door", not one
1725
+ * filtered chart. Exact match: null-upstream rows never match any value.
1726
+ */
1727
+ aggregate(from?: string, to?: string, filters?: {
1728
+ upstream?: string;
1729
+ }): AuditAggregateStats;
1369
1730
  /** Delete records older than the retention period. Returns the count of deleted records. */
1370
1731
  purgeExpired(): number;
1371
1732
  private purgeBefore;
@@ -1653,6 +2014,18 @@ interface ApprovalTicket {
1653
2014
  readonly rule_index: number | null;
1654
2015
  readonly channel_name: string;
1655
2016
  readonly session_id: string | null;
2017
+ /**
2018
+ * Which identity strategy produced session_id (issue #251): the MCP
2019
+ * resolver's vocabulary on proxy tickets, `sideband` on adapter tickets.
2020
+ * OPTIONAL-ABSENT, not nullable: the ticket is the wire object, and an
2021
+ * unattributed ticket omits the key rather than emitting null.
2022
+ */
2023
+ readonly session_source?: string;
2024
+ /**
2025
+ * Upstream attribution (issue #292). Absent in singular mode and on
2026
+ * native (sideband) tickets — same wire-additive optional spelling.
2027
+ */
2028
+ readonly upstream?: string;
1656
2029
  readonly requested_at: string;
1657
2030
  readonly timeout_at: string;
1658
2031
  readonly timeout_ms: number;
@@ -1748,6 +2121,10 @@ declare class ApprovalQueue {
1748
2121
  timeout_ms: number;
1749
2122
  /** Breached budget context on break-glass / merged tickets (issue #14). */
1750
2123
  breached_budgets?: readonly BudgetBreachContext[];
2124
+ /** Identity-strategy attribution for session_id (issue #251). */
2125
+ session_source?: string | null;
2126
+ /** Upstream attribution (issue #292); absent in singular mode. */
2127
+ upstream?: string | null;
1751
2128
  }): ApprovalTicket;
1752
2129
  /** Get a ticket by ID. Returns undefined if not found. */
1753
2130
  get(id: string): ApprovalTicket | undefined;
@@ -1803,6 +2180,13 @@ interface ApprovalSubmitParams {
1803
2180
  readonly tool_input: Record<string, unknown>;
1804
2181
  readonly matched_rule: CompiledPolicyRule | undefined;
1805
2182
  readonly session_id: string | null;
2183
+ /**
2184
+ * Identity-strategy attribution for session_id (issue #251).
2185
+ * Required-nullable so no submit site can silently forget it.
2186
+ */
2187
+ readonly session_source: string | null;
2188
+ /** Upstream attribution (issue #292); null in singular mode. */
2189
+ readonly upstream: string | null;
1806
2190
  /** Breached budget context; marks the ticket as break-glass (issue #14). */
1807
2191
  readonly breached_budgets?: readonly BudgetBreachContext[];
1808
2192
  /**
@@ -2010,12 +2394,21 @@ declare class RateLimiter {
2010
2394
  * Reconcile bucket state against a new policy's limit configuration.
2011
2395
  *
2012
2396
  * Walks every existing bucket and checks whether its last-seen
2013
- * `{ maxCalls, windowMs }` tuple still appears anywhere in `validConfigs`.
2397
+ * `{ maxCalls, windowMs }` tuple still appears in `validConfigs`.
2014
2398
  * Buckets whose config is still present are left untouched — counters and
2015
2399
  * elapsed-window progress are preserved across hot-reloads. Buckets whose
2016
2400
  * config is gone (rule changed or removed) are evicted so the next check
2017
2401
  * lazy-creates a fresh bucket under the new config.
2018
2402
  *
2403
+ * Keys built by `ruleBucketKey` (bucket-key.ts) carry the owning rule's
2404
+ * index, and for those the tuple must match at THAT index
2405
+ * (`config.ruleIndex`): a reorder that shifts a rate rule's index evicts
2406
+ * its old-index bucket instead of leaving an orphan no rule reads again —
2407
+ * or worse, letting whatever rule now sits at that index adopt another
2408
+ * rule's accrued calls. Un-suffixed keys keep the tuple-anywhere match,
2409
+ * but only against index-less configs — a caller that passes only indexed
2410
+ * configs (as the proxy does) evicts every un-suffixed bucket, fail-closed.
2411
+ *
2019
2412
  * This is the compare-and-evict semantic that replaces the old `reset()`
2020
2413
  * call on every hot-reload, which wiped all state even when the matching
2021
2414
  * rule was unchanged.
@@ -2023,6 +2416,7 @@ declare class RateLimiter {
2023
2416
  reconcile(validConfigs: Iterable<{
2024
2417
  maxCalls: number;
2025
2418
  windowMs: number;
2419
+ ruleIndex?: number;
2026
2420
  }>): void;
2027
2421
  /** Stop the cleanup timer and mark as closed. */
2028
2422
  /**
@@ -2151,12 +2545,14 @@ declare class SpendLimiter {
2151
2545
  * whose config is gone (rule changed or removed) are evicted so the next
2152
2546
  * check lazy-creates a fresh bucket under the new config.
2153
2547
  *
2154
- * Keys built by {@link spendBucketKey} carry the owning rule's index, and
2155
- * for those the tuple must match at THAT index (`config.ruleIndex`): a
2548
+ * Keys built by `ruleBucketKey` (bucket-key.ts) carry the owning rule's
2549
+ * index, and for those the tuple must match at THAT index (`config.ruleIndex`): a
2156
2550
  * reorder that shifts a spend rule's index evicts its old-index bucket
2157
2551
  * instead of leaving an orphan no rule reads again — or worse, letting
2158
2552
  * whatever rule now sits at that index adopt another rule's accrued spend.
2159
- * Un-suffixed keys keep the tuple-anywhere match.
2553
+ * Un-suffixed keys keep the tuple-anywhere match, but only against
2554
+ * index-less configs — a caller that passes only indexed configs (as the
2555
+ * proxy does) evicts every un-suffixed bucket, fail-closed.
2160
2556
  *
2161
2557
  * Currency is part of the tuple because a USD→EUR switch is a meaningful
2162
2558
  * policy change — the same numeric limit buys a different amount of real
@@ -2205,6 +2601,13 @@ interface BudgetChargeContext {
2205
2601
  readonly sessionId: GatedSession | null;
2206
2602
  /** Adapter-supplied sender id (sideband only); null on the MCP path. */
2207
2603
  readonly senderId: string | null;
2604
+ /**
2605
+ * The governed door's configured upstream name (issue #295); null on the
2606
+ * sideband and in singular mode. Required so every door states its
2607
+ * upstream: a silently-missing value would exempt that door's calls from
2608
+ * upstream-scoped contributors once those exist.
2609
+ */
2610
+ readonly upstream: string | null;
2208
2611
  }
2209
2612
  /** One budget's share of a call: which bucket, how much. */
2210
2613
  interface BudgetCharge {
@@ -2218,6 +2621,12 @@ interface BudgetCharge {
2218
2621
  * NOT repopulate the new pot with old-config spend — recordAll skips it.
2219
2622
  */
2220
2623
  readonly generation: number;
2624
+ /**
2625
+ * The charge context's upstream name, stamped only when the door set one
2626
+ * (issue #295). Optional is the honest type: `remintDeferredCharges`
2627
+ * rebuilds charges without it, so deferred commits snapshot null.
2628
+ */
2629
+ readonly upstream?: string;
2221
2630
  }
2222
2631
  /** A budget whose contributor matched but whose amount was unusable. */
2223
2632
  interface BudgetChargeFailure {
@@ -2242,6 +2651,11 @@ interface BudgetPeekEntry {
2242
2651
  readonly remaining: number;
2243
2652
  /** Epoch ms when the oldest entry ages out (duration); null for session pots. */
2244
2653
  readonly resetAtMs: number | null;
2654
+ /**
2655
+ * The charge's upstream label, or null: sideband calls, singular mode, and
2656
+ * reminted deferred commits all snapshot null (issue #295).
2657
+ */
2658
+ readonly upstream: string | null;
2245
2659
  /**
2246
2660
  * Set on recordAll snapshots for charges frozen before a tuple-changing
2247
2661
  * reload: the executed spend was ledgered under its evaluate-time
@@ -2306,6 +2720,12 @@ interface BudgetCommitEvent {
2306
2720
  readonly limit: number;
2307
2721
  readonly currency: string;
2308
2722
  readonly utilization: number;
2723
+ /**
2724
+ * Upstream attribution from the charge context (issue #292), or null.
2725
+ * Reminted frozen-plan charges drop the label by design, so deferred
2726
+ * sideband commits always emit null here.
2727
+ */
2728
+ readonly upstream: string | null;
2309
2729
  }
2310
2730
  /**
2311
2731
  * Payload for the per-budget breach callback (dashboard event bus). Fired
@@ -2322,6 +2742,8 @@ interface BudgetBreachEvent {
2322
2742
  readonly spent: number;
2323
2743
  readonly limit: number;
2324
2744
  readonly currency: string;
2745
+ /** Upstream attribution from the peek entry's context (issue #292), or null. */
2746
+ readonly upstream: string | null;
2325
2747
  }
2326
2748
  /** Wire-ready bucket state for `GET /api/budgets` (snake_case DTO). */
2327
2749
  interface BudgetBucketState {
@@ -2373,14 +2795,18 @@ declare class BudgetEngine {
2373
2795
  /**
2374
2796
  * Resolve which budgets a call feeds and how much it charges each.
2375
2797
  *
2376
- * A contributor participates when its tool glob matches the tool name AND
2377
- * every `match.input` condition holds (absent conditions means the glob
2378
- * alone decides); the FIRST participating contributor (config order, over
2379
- * that combined predicate) supplies the amount field. A call that matches
2380
- * the glob but not the conditions simply does not feed the budget — no
2381
- * charge, no failure. Once a contributor is selected, a missing,
2382
- * non-numeric, negative, or non-finite amount fails closed as a `failures`
2383
- * entry the caller must deny the call.
2798
+ * A contributor participates when its upstream scope admits the call's
2799
+ * door (absent scope admits every door; a scoped contributor never
2800
+ * participates when `ctx.upstream` is null sideband, singular mode) AND
2801
+ * its tool glob matches the tool name AND every `match.input` condition
2802
+ * holds (absent conditions means the glob alone decides); the FIRST
2803
+ * participating contributor (config order, over that combined predicate)
2804
+ * supplies the amount field. A call that matches the glob but not the
2805
+ * conditions or the scope simply does not feed the budget — no charge, no
2806
+ * failure — and a later contributor may still participate. Once a
2807
+ * contributor is selected, a missing, non-numeric, negative, or non-finite
2808
+ * amount fails closed as a `failures` entry — the caller must deny the
2809
+ * call.
2384
2810
  */
2385
2811
  resolveCharges(ctx: BudgetChargeContext): {
2386
2812
  charges: BudgetCharge[];
@@ -2533,6 +2959,13 @@ interface GovernedForwarderOptions {
2533
2959
  * default chain (deny mode) for direct/library construction.
2534
2960
  */
2535
2961
  session?: CompiledSessionIdentity;
2962
+ /**
2963
+ * The operator-chosen upstream entry name (issue #295, multi-upstream
2964
+ * substrate). Unset in singular mode — every governance surface then
2965
+ * behaves exactly as today. The multi-upstream composition loop (issue
2966
+ * #294) passes each entry's name.
2967
+ */
2968
+ upstreamName?: string;
2536
2969
  }
2537
2970
  /** Result of attempting to prime the tool annotation cache. */
2538
2971
  interface AnnotationCachePrimeResult {
@@ -2562,6 +2995,7 @@ declare class GovernedForwarder implements McpForwarder {
2562
2995
  private readonly rateLimiter;
2563
2996
  private readonly spendLimiter;
2564
2997
  private readonly budgetEngine;
2998
+ private readonly upstreamName;
2565
2999
  private readonly annotationCache;
2566
3000
  private agentKeyWarned;
2567
3001
  private senderKeyWarned;
@@ -2673,7 +3107,9 @@ declare class GovernedForwarder implements McpForwarder {
2673
3107
  */
2674
3108
  private handleDryRun;
2675
3109
  /**
2676
- * Construct a non-session limit bucket key. Session keys are deliberately
3110
+ * Construct a non-session limit bucket key. Tool-scope keys route through
3111
+ * the shared `toolLimitKey` leaf, which prefixes them with the configured
3112
+ * upstream name when one is set (issue #295). Session keys are deliberately
2677
3113
  * NOT built here: they come only from the gate module's `sessionLimitKey`,
2678
3114
  * whose `GatedSession` parameter makes skipping the identity gate a
2679
3115
  * compile error (issue #218) — call sites branch on `key === 'session'`.
@@ -2730,6 +3166,13 @@ interface BudgetEventRecord {
2730
3166
  readonly timestamp: string;
2731
3167
  readonly timestamp_ms: number;
2732
3168
  readonly created_at: string;
3169
+ /**
3170
+ * Upstream attribution read from the referenced audit record via LEFT
3171
+ * JOIN (issue #292) — never a stored ledger column. Null on singular-mode
3172
+ * records, dangling or null `audit_record_id`, and rows predating the
3173
+ * audit column; a null renders as empty/absent, never a made-up label.
3174
+ */
3175
+ readonly upstream: string | null;
2733
3176
  }
2734
3177
  /** One page of a budget's event history plus the unpaginated total. */
2735
3178
  interface BudgetEventsPage {
@@ -3020,7 +3463,7 @@ declare class GovernanceConfigError extends Error {
3020
3463
  * reserved `'<header_mismatch>'` sentinel — either way `top_tools` already
3021
3464
  * excludes rejected records, so these rows never pollute tool rankings.
3022
3465
  */
3023
- declare function buildHeaderMismatchAuditRecord(rejection: HeaderMismatchRejection, environment?: string): Omit<AuditRecord, 'id' | 'created_at'>;
3466
+ declare function buildHeaderMismatchAuditRecord(rejection: HeaderMismatchRejection, environment?: string, upstream?: string): Omit<AuditRecord, 'id' | 'created_at'>;
3024
3467
 
3025
3468
  /** @internal Exported for testing only. */
3026
3469
  declare class QueueChannel implements ApprovalChannel {
@@ -3164,6 +3607,8 @@ interface ActionEvent {
3164
3607
  readonly matched_rule_index: number | null;
3165
3608
  readonly record_kind: AuditRecord['record_kind'];
3166
3609
  readonly origin: string;
3610
+ /** Upstream attribution from the audit record (issue #292), or null. */
3611
+ readonly upstream: string | null;
3167
3612
  }
3168
3613
  /** Payload for an approval_requested event. */
3169
3614
  interface ApprovalRequestedEvent {
@@ -3171,6 +3616,8 @@ interface ApprovalRequestedEvent {
3171
3616
  readonly tool_name: string;
3172
3617
  readonly channel: string;
3173
3618
  readonly requested_at: string;
3619
+ /** Upstream attribution from the ticket (issue #292), or null. */
3620
+ readonly upstream: string | null;
3174
3621
  }
3175
3622
  /** Payload for an approval_resolved event. */
3176
3623
  interface ApprovalResolvedEvent {
@@ -3186,6 +3633,11 @@ interface LimitWarningEvent {
3186
3633
  readonly current: number;
3187
3634
  readonly limit: number;
3188
3635
  readonly utilization: number;
3636
+ /**
3637
+ * Upstream name parsed from a partitioned bucket key (issue #292), or
3638
+ * null — session keys, singular tool keys, and sideband keys have no door.
3639
+ */
3640
+ readonly upstream: string | null;
3189
3641
  }
3190
3642
  /** Payload for approval notification delivery failures. */
3191
3643
  interface ApprovalNotificationFailedEvent {
@@ -3289,6 +3741,20 @@ interface DashboardAppOptions {
3289
3741
  readonly staticDir?: string;
3290
3742
  /** SSE heartbeat interval in milliseconds. Defaults to 30 000 (30s). */
3291
3743
  readonly sseHeartbeatMs?: number;
3744
+ /**
3745
+ * @internal Test seam for the concurrent-connection cap (issue #285), so
3746
+ * tests need not mint 256 real streams. Not wired to config — the cap is
3747
+ * a hardcoded invariant (see MAX_SSE_CONNECTIONS).
3748
+ */
3749
+ readonly maxSseConnections?: number;
3750
+ /**
3751
+ * @internal Test seam for the stale-connection sweep cadence (issue
3752
+ * #327), so sweeper tests need not wait out the production floor. `0`
3753
+ * DISABLES the sweeper entirely (the repo-wide sweep-interval
3754
+ * convention). Not wired to config — the 10 s floor stays the
3755
+ * production default.
3756
+ */
3757
+ readonly sweepIntervalMs?: number;
3292
3758
  }
3293
3759
  /**
3294
3760
  * Create a Hono app for the dashboard REST + SSE API.
@@ -3298,4 +3764,4 @@ interface DashboardAppOptions {
3298
3764
  */
3299
3765
  declare function createDashboardApp(deps: DashboardAppDeps, options?: DashboardAppOptions): Hono;
3300
3766
 
3301
- export { type AdapterLivenessEntry, type ApprovalAppOptions, type ApprovalChannel, type ApprovalOutcome, ApprovalQueue, type ApprovalQueueOptions, ApprovalRouter, type ApprovalRouterOptions, type ApprovalStatus, type ApprovalTicket, type AuditAggregateStats, type AuditInput, type AuditListResult, type AuditPaginationOptions, type AuditQueryFilters, type AuditRecord, AuditStore, type AuditStoreOptions, type AuditTimeBucket, AuditWriter, type AuditWriterOptions, type BudgetBreachContext, type BudgetBreachEvent, type BudgetBucketState, type BudgetCommitEvent, BudgetEngine, type BudgetEventRecord, type BudgetEventsPage, type BudgetLedgerRow, type BudgetLedgerSink, BudgetParseError, type BudgetState, type CompilePoliciesResult, type CompiledBudget, type CompiledBudgetContributor, type CompiledBudgetWindow, type CompiledPolicy, type CompiledPolicyRule, ConfigError, type CreateAppOptions, type DashboardAppDeps, type DashboardAppOptions, DashboardEventBus, type DashboardEventType, type DashboardEvents, EXPORT_MAX_RECORDS, type EvaluateInput, type EvidenceEntry, EvidenceStore, type EvidenceStoreOptions, GovernanceConfigError, GovernanceService, type GovernanceServiceOptions, GovernedForwarder, type GovernedForwarderOptions, type HeaderMismatchRejection, type HelioConfig, type InstallScanInput, LIST_MAX_PAGE_SIZE, type MatchContext, type PolicyDecision, PolicyParseError, QueueChannel, type RateLimitCheckParams, type RateLimitKeyState, type RateLimitResult, RateLimiter, type RateLimiterOptions, type ResolveApprovalInput, type ServerHandle, type SessionState, type SlackActionAppOptions, SlackChannel, type SlackChannelOptions, type SpendLimitCheckParams, type SpendLimitKeyState, type SpendLimitResult, SpendLimiter, type SpendLimiterOptions, SseUpstreamForwarder, type SseUpstreamForwarderOptions, StdioForwarder, type StdioForwarderOptions, StreamableHttpForwarder, type StreamableHttpForwarderOptions, UpstreamForwarder, type UpstreamForwarderOptions, VERSION, WebhookChannel, type WebhookChannelOptions, type WireDecision, buildHeaderMismatchAuditRecord, compileBudgets, compilePolicies, createApp, createApprovalApp, createChannels, createDashboardApp, createSidebandApp, createSlackActionApp, evaluatePolicy, loadConfig, matchRule, startServer, startSidebandServer };
3767
+ export { type AdapterLivenessEntry, type ApprovalAppOptions, type ApprovalChannel, type ApprovalOutcome, ApprovalQueue, type ApprovalQueueOptions, ApprovalRouter, type ApprovalRouterOptions, type ApprovalStatus, type ApprovalTicket, type AuditAggregateStats, type AuditInput, type AuditListResult, type AuditPaginationOptions, type AuditQueryFilters, type AuditRecord, AuditStore, type AuditStoreOptions, type AuditTimeBucket, AuditWriter, type AuditWriterOptions, type BudgetBreachContext, type BudgetBreachEvent, type BudgetBucketState, type BudgetCommitEvent, BudgetEngine, type BudgetEventRecord, type BudgetEventsPage, type BudgetLedgerRow, type BudgetLedgerSink, BudgetParseError, type BudgetState, type CompilePoliciesResult, type CompiledBudget, type CompiledBudgetContributor, type CompiledBudgetWindow, type CompiledPolicy, type CompiledPolicyRule, ConfigError, type CreateAppOptions, type CreateMultiAppOptions, type DashboardAppDeps, type DashboardAppOptions, DashboardEventBus, type DashboardEventType, type DashboardEvents, EXPORT_MAX_RECORDS, type EvaluateInput, type EvidenceEntry, EvidenceStore, type EvidenceStoreOptions, GovernanceConfigError, GovernanceService, type GovernanceServiceOptions, GovernedForwarder, type GovernedForwarderOptions, type HeaderMismatchRejection, type HelioConfig, type InstallScanInput, LIST_MAX_PAGE_SIZE, type MatchContext, type NamedHelioConfig, type PolicyDecision, PolicyParseError, QueueChannel, type RateLimitCheckParams, type RateLimitKeyState, type RateLimitResult, RateLimiter, type RateLimiterOptions, type ResolveApprovalInput, type ServerHandle, type SessionState, type SingularHelioConfig, type SlackActionAppOptions, SlackChannel, type SlackChannelOptions, type SpendLimitCheckParams, type SpendLimitKeyState, type SpendLimitResult, SpendLimiter, type SpendLimiterOptions, SseUpstreamForwarder, type SseUpstreamForwarderOptions, StdioForwarder, type StdioForwarderOptions, StreamableHttpForwarder, type StreamableHttpForwarderOptions, UpstreamForwarder, type UpstreamForwarderOptions, VERSION, WebhookChannel, type WebhookChannelOptions, type WireDecision, buildHeaderMismatchAuditRecord, compileBudgets, compilePolicies, createApp, createApprovalApp, createChannels, createDashboardApp, createMultiApp, createSidebandApp, createSlackActionApp, evaluatePolicy, isNamedConfig, isSingularConfig, loadConfig, matchRule, startServer, startSidebandServer };