@gethelio/proxy 0.11.0 → 0.12.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/README.md +75 -45
- package/dist/cli.js +2351 -959
- package/dist/dashboard-assets/assets/index-BBYXsIig.css +1 -0
- package/dist/dashboard-assets/assets/index-uJng9NyO.js +128 -0
- package/dist/dashboard-assets/index.html +2 -2
- package/dist/index.d.ts +257 -20
- package/dist/index.js +2205 -874
- package/package.json +7 -7
- package/dist/dashboard-assets/assets/index-CPoQ6xns.css +0 -1
- package/dist/dashboard-assets/assets/index-D19fYKEH.js +0 -128
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
<meta name="referrer" content="no-referrer" />
|
|
7
7
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
8
8
|
<title>Helio Dashboard</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
10
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-uJng9NyO.js"></script>
|
|
10
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BBYXsIig.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
13
13
|
<div id="root"></div>
|
package/dist/index.d.ts
CHANGED
|
@@ -6,10 +6,18 @@ import { KnownBlock } from '@slack/web-api';
|
|
|
6
6
|
|
|
7
7
|
declare const VERSION: string;
|
|
8
8
|
|
|
9
|
+
declare const sessionIdentitySourceSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
10
|
+
source: z.ZodLiteral<"header">;
|
|
11
|
+
name: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<string, string>>;
|
|
12
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
13
|
+
source: z.ZodLiteral<"meta">;
|
|
14
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
15
|
+
source: z.ZodLiteral<"legacy_header">;
|
|
16
|
+
}, z.core.$strict>], "source">;
|
|
9
17
|
declare const policiesSchema: z.ZodObject<{
|
|
10
18
|
default: z.ZodDefault<z.ZodEnum<{
|
|
11
|
-
allow: "allow";
|
|
12
19
|
deny: "deny";
|
|
20
|
+
allow: "allow";
|
|
13
21
|
}>>;
|
|
14
22
|
flag_destructive: z.ZodOptional<z.ZodEnum<{
|
|
15
23
|
require_approval: "require_approval";
|
|
@@ -45,8 +53,8 @@ declare const policiesSchema: z.ZodObject<{
|
|
|
45
53
|
}, z.core.$strict>]>>>;
|
|
46
54
|
}, z.core.$strict>;
|
|
47
55
|
action: z.ZodEnum<{
|
|
48
|
-
allow: "allow";
|
|
49
56
|
deny: "deny";
|
|
57
|
+
allow: "allow";
|
|
50
58
|
require_approval: "require_approval";
|
|
51
59
|
rate_limit: "rate_limit";
|
|
52
60
|
spend_limit: "spend_limit";
|
|
@@ -92,8 +100,8 @@ declare const policiesSchema: z.ZodObject<{
|
|
|
92
100
|
}, z.core.$strict>>>;
|
|
93
101
|
install: z.ZodOptional<z.ZodObject<{
|
|
94
102
|
default: z.ZodDefault<z.ZodEnum<{
|
|
95
|
-
allow: "allow";
|
|
96
103
|
deny: "deny";
|
|
104
|
+
allow: "allow";
|
|
97
105
|
}>>;
|
|
98
106
|
rules: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
99
107
|
name: z.ZodOptional<z.ZodString>;
|
|
@@ -122,6 +130,11 @@ declare const policiesSchema: z.ZodObject<{
|
|
|
122
130
|
log: "log";
|
|
123
131
|
block: "block";
|
|
124
132
|
}>>;
|
|
133
|
+
tool_revalidation: z.ZodOptional<z.ZodObject<{
|
|
134
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
135
|
+
interval: z.ZodDefault<z.ZodString>;
|
|
136
|
+
max_advertised_ttl: z.ZodOptional<z.ZodString>;
|
|
137
|
+
}, z.core.$strict>>;
|
|
125
138
|
hot_reload: z.ZodOptional<z.ZodBoolean>;
|
|
126
139
|
}, z.core.$strict>;
|
|
127
140
|
declare const budgetSchema: z.ZodObject<{
|
|
@@ -187,6 +200,11 @@ declare const helioConfigSchema: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.Z
|
|
|
187
200
|
sse: "sse";
|
|
188
201
|
stdio: "stdio";
|
|
189
202
|
}>>;
|
|
203
|
+
protocol_version: z.ZodDefault<z.ZodEnum<{
|
|
204
|
+
auto: "auto";
|
|
205
|
+
"2025-06-18": "2025-06-18";
|
|
206
|
+
"2026-07-28": "2026-07-28";
|
|
207
|
+
}>>;
|
|
190
208
|
command: z.ZodOptional<z.ZodString>;
|
|
191
209
|
args: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
192
210
|
connect_timeout: z.ZodDefault<z.ZodString>;
|
|
@@ -197,12 +215,27 @@ declare const helioConfigSchema: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.Z
|
|
|
197
215
|
listen: z.ZodPrefault<z.ZodObject<{
|
|
198
216
|
port: z.ZodDefault<z.ZodNumber>;
|
|
199
217
|
host: z.ZodDefault<z.ZodString>;
|
|
218
|
+
allowed_origins: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
200
219
|
}, z.core.$strict>>;
|
|
201
220
|
environment: z.ZodOptional<z.ZodString>;
|
|
221
|
+
session: z.ZodPrefault<z.ZodObject<{
|
|
222
|
+
identity: z.ZodDefault<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
223
|
+
source: z.ZodLiteral<"header">;
|
|
224
|
+
name: z.ZodPipe<z.ZodDefault<z.ZodString>, z.ZodTransform<string, string>>;
|
|
225
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
226
|
+
source: z.ZodLiteral<"meta">;
|
|
227
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
228
|
+
source: z.ZodLiteral<"legacy_header">;
|
|
229
|
+
}, z.core.$strict>], "source">>>;
|
|
230
|
+
on_unresolved: z.ZodDefault<z.ZodEnum<{
|
|
231
|
+
deny: "deny";
|
|
232
|
+
anonymous: "anonymous";
|
|
233
|
+
}>>;
|
|
234
|
+
}, z.core.$strict>>;
|
|
202
235
|
policies: z.ZodPrefault<z.ZodObject<{
|
|
203
236
|
default: z.ZodDefault<z.ZodEnum<{
|
|
204
|
-
allow: "allow";
|
|
205
237
|
deny: "deny";
|
|
238
|
+
allow: "allow";
|
|
206
239
|
}>>;
|
|
207
240
|
flag_destructive: z.ZodOptional<z.ZodEnum<{
|
|
208
241
|
require_approval: "require_approval";
|
|
@@ -238,8 +271,8 @@ declare const helioConfigSchema: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.Z
|
|
|
238
271
|
}, z.core.$strict>]>>>;
|
|
239
272
|
}, z.core.$strict>;
|
|
240
273
|
action: z.ZodEnum<{
|
|
241
|
-
allow: "allow";
|
|
242
274
|
deny: "deny";
|
|
275
|
+
allow: "allow";
|
|
243
276
|
require_approval: "require_approval";
|
|
244
277
|
rate_limit: "rate_limit";
|
|
245
278
|
spend_limit: "spend_limit";
|
|
@@ -285,8 +318,8 @@ declare const helioConfigSchema: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.Z
|
|
|
285
318
|
}, z.core.$strict>>>;
|
|
286
319
|
install: z.ZodOptional<z.ZodObject<{
|
|
287
320
|
default: z.ZodDefault<z.ZodEnum<{
|
|
288
|
-
allow: "allow";
|
|
289
321
|
deny: "deny";
|
|
322
|
+
allow: "allow";
|
|
290
323
|
}>>;
|
|
291
324
|
rules: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
292
325
|
name: z.ZodOptional<z.ZodString>;
|
|
@@ -315,6 +348,11 @@ declare const helioConfigSchema: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.Z
|
|
|
315
348
|
log: "log";
|
|
316
349
|
block: "block";
|
|
317
350
|
}>>;
|
|
351
|
+
tool_revalidation: z.ZodOptional<z.ZodObject<{
|
|
352
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
353
|
+
interval: z.ZodDefault<z.ZodString>;
|
|
354
|
+
max_advertised_ttl: z.ZodOptional<z.ZodString>;
|
|
355
|
+
}, z.core.$strict>>;
|
|
318
356
|
hot_reload: z.ZodOptional<z.ZodBoolean>;
|
|
319
357
|
}, z.core.$strict>>;
|
|
320
358
|
budgets: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
@@ -358,8 +396,8 @@ declare const helioConfigSchema: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.Z
|
|
|
358
396
|
approval: z.ZodPrefault<z.ZodObject<{
|
|
359
397
|
timeout: z.ZodDefault<z.ZodString>;
|
|
360
398
|
default_on_timeout: z.ZodDefault<z.ZodEnum<{
|
|
361
|
-
allow: "allow";
|
|
362
399
|
deny: "deny";
|
|
400
|
+
allow: "allow";
|
|
363
401
|
}>>;
|
|
364
402
|
channels: z.ZodDefault<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
365
403
|
type: z.ZodLiteral<"slack">;
|
|
@@ -406,6 +444,8 @@ type HelioConfig = z.infer<typeof helioConfigSchema>;
|
|
|
406
444
|
type ApprovalChannel$1 = z.infer<typeof approvalChannelSchema>;
|
|
407
445
|
/** The policies section of the config. */
|
|
408
446
|
type PoliciesConfig = z.infer<typeof policiesSchema>;
|
|
447
|
+
/** A single identity source from `session.identity`. */
|
|
448
|
+
type SessionIdentitySource = z.infer<typeof sessionIdentitySourceSchema>;
|
|
409
449
|
/** A single named budget from the `budgets` array (issue #14). */
|
|
410
450
|
type BudgetConfig = z.infer<typeof budgetSchema>;
|
|
411
451
|
/** The `budgets` section of the config. */
|
|
@@ -513,6 +553,12 @@ interface CompiledLimits {
|
|
|
513
553
|
readonly key?: 'tool' | 'agent' | 'session' | 'sender_id';
|
|
514
554
|
readonly maxSpend?: CompiledSpendLimit;
|
|
515
555
|
}
|
|
556
|
+
/** Compiled tool revalidation settings with durations as milliseconds. */
|
|
557
|
+
interface CompiledToolRevalidation {
|
|
558
|
+
readonly enabled: boolean;
|
|
559
|
+
readonly intervalMs: number;
|
|
560
|
+
readonly maxAdvertisedTtlMs: number;
|
|
561
|
+
}
|
|
516
562
|
/** Policy action types. */
|
|
517
563
|
type PolicyAction = 'allow' | 'deny' | 'require_approval' | 'rate_limit' | 'spend_limit' | 'dry_run';
|
|
518
564
|
/** A fully compiled policy rule ready for engine evaluation. */
|
|
@@ -576,6 +622,8 @@ interface CompiledPolicy {
|
|
|
576
622
|
* at the use site — conservative by default.
|
|
577
623
|
*/
|
|
578
624
|
readonly onToolDrift?: 'block' | 'require_approval' | 'log';
|
|
625
|
+
/** Always set by compilePolicies; absent only in hand-built fixtures (treated as disabled). */
|
|
626
|
+
readonly toolRevalidation?: CompiledToolRevalidation;
|
|
579
627
|
readonly rules: readonly CompiledPolicyRule[];
|
|
580
628
|
/** Install-time policy (issue #13 — deny_install). Undefined ⇒ observational. */
|
|
581
629
|
readonly install?: CompiledInstallPolicy;
|
|
@@ -679,15 +727,63 @@ interface JsonRpcRequest {
|
|
|
679
727
|
method: string;
|
|
680
728
|
params?: unknown;
|
|
681
729
|
}
|
|
730
|
+
/** Resolved governance session identity (issue #218). */
|
|
731
|
+
interface ResolvedSession {
|
|
732
|
+
readonly id: string;
|
|
733
|
+
/** Strategy that produced it — recorded in the audit trail. */
|
|
734
|
+
readonly source: 'header' | 'meta' | 'legacy_header' | 'transport';
|
|
735
|
+
}
|
|
682
736
|
/** A parsed MCP request enriched with session context. */
|
|
683
737
|
interface McpRequest extends JsonRpcRequest {
|
|
684
|
-
/**
|
|
685
|
-
|
|
738
|
+
/** Proxy-resolved governance session identity (undefined when no strategy matched). */
|
|
739
|
+
session?: ResolvedSession;
|
|
740
|
+
/** Verbatim Mcp-Session-Id from the wire. Transport relay ONLY — never governance. */
|
|
741
|
+
transportSessionId?: string;
|
|
742
|
+
/**
|
|
743
|
+
* The client's verbatim MCP-Protocol-Version wire claim (issue #219) —
|
|
744
|
+
* captured raw, with no normalization, for the audit trail. It is the
|
|
745
|
+
* CLIENT'S claim, not the upstream era. Since issue #226 the
|
|
746
|
+
* streamable-http route validates header/body agreement before
|
|
747
|
+
* forwarding, so on a FORWARDED request a claim of `2026-07-28` has
|
|
748
|
+
* passed the agreement door; tier-2 claims (legacy, unknown, malformed)
|
|
749
|
+
* and notification claims remain unvalidated as captured. Streamable
|
|
750
|
+
* HTTP only: the header postdates the deprecated SSE transport, and
|
|
751
|
+
* stdio has no headers, so both leave it unset.
|
|
752
|
+
*/
|
|
753
|
+
protocolVersion?: string;
|
|
686
754
|
/** Per-request headers to forward to upstream (e.g. Authorization, X-* headers). */
|
|
687
755
|
headers?: Record<string, string>;
|
|
688
756
|
/** Abort signal tied to the downstream client request lifecycle. */
|
|
689
757
|
signal?: AbortSignal;
|
|
690
758
|
}
|
|
759
|
+
/**
|
|
760
|
+
* An inbound request rejected by the header/body agreement door (issue
|
|
761
|
+
* #226), as protocol facts: what the body said, what the headers claimed,
|
|
762
|
+
* and why they disagree. Deliberately camelCase and transport-free (no Hono
|
|
763
|
+
* types, no audit field names) — the audit-record mapping lives solely in
|
|
764
|
+
* `buildHeaderMismatchAuditRecord` on the audit side.
|
|
765
|
+
*/
|
|
766
|
+
interface HeaderMismatchRejection {
|
|
767
|
+
/** Human-readable mismatch reason; echoed values are display-capped. */
|
|
768
|
+
readonly reason: string;
|
|
769
|
+
/** The body's JSON-RPC method. */
|
|
770
|
+
readonly method: string;
|
|
771
|
+
/** The body's params, verbatim as parsed. */
|
|
772
|
+
readonly params?: unknown;
|
|
773
|
+
/**
|
|
774
|
+
* The body's name-bearing string field (`params.name` / `params.uri`),
|
|
775
|
+
* when the method defines one and the body carries a string value.
|
|
776
|
+
*/
|
|
777
|
+
readonly bodyName?: string;
|
|
778
|
+
/** The client's verbatim MCP-Protocol-Version wire claim, if any. */
|
|
779
|
+
readonly protocolVersion?: string;
|
|
780
|
+
/** The inbound marker headers that were present, verbatim as received. */
|
|
781
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
782
|
+
/** Proxy-resolved governance session identity, when a strategy matched. */
|
|
783
|
+
readonly session?: ResolvedSession;
|
|
784
|
+
/** Time from route-handler entry to rejection, in milliseconds. */
|
|
785
|
+
readonly durationMs: number;
|
|
786
|
+
}
|
|
691
787
|
/** The response returned by an MCP forwarder. */
|
|
692
788
|
interface McpResponse {
|
|
693
789
|
status: number;
|
|
@@ -724,6 +820,13 @@ interface ServerHandle {
|
|
|
724
820
|
interface CreateAppOptions {
|
|
725
821
|
/** Slack interactive action handler (mounted at /slack/actions). */
|
|
726
822
|
slackActionApp?: Hono;
|
|
823
|
+
/**
|
|
824
|
+
* Recorder for inbound header/body agreement rejections (issue #226) on
|
|
825
|
+
* the streamable-http route. Enforcement does not depend on it — with no
|
|
826
|
+
* recorder the request is still rejected and no record is written (the
|
|
827
|
+
* library-embedding posture `missing_tool_name` also takes).
|
|
828
|
+
*/
|
|
829
|
+
onHeaderMismatch?: (rejection: HeaderMismatchRejection) => void;
|
|
727
830
|
}
|
|
728
831
|
/**
|
|
729
832
|
* Create a Hono app configured with the MCP proxy routes.
|
|
@@ -748,6 +851,12 @@ declare function startServer(app: Hono, config: HelioConfig): ServerHandle;
|
|
|
748
851
|
*/
|
|
749
852
|
declare function startSidebandServer(app: Hono, port: number, host?: string): ServerHandle;
|
|
750
853
|
|
|
854
|
+
/**
|
|
855
|
+
* `upstream.protocol_version`: `auto` probes and caches; a dated pin is a
|
|
856
|
+
* constant — never probed, never cached, never cleared.
|
|
857
|
+
*/
|
|
858
|
+
type UpstreamProtocolVersionPin = 'auto' | '2025-06-18' | '2026-07-28';
|
|
859
|
+
|
|
751
860
|
interface StreamableHttpForwarderOptions {
|
|
752
861
|
/** The upstream MCP server URL (e.g. "http://localhost:8080/mcp"). */
|
|
753
862
|
url: string;
|
|
@@ -755,6 +864,8 @@ interface StreamableHttpForwarderOptions {
|
|
|
755
864
|
headers?: Record<string, string>;
|
|
756
865
|
/** Maximum time to wait for an upstream request before aborting. */
|
|
757
866
|
requestTimeoutMs?: number;
|
|
867
|
+
/** `upstream.protocol_version`: `auto` (default) probes; a dated pin skips it. */
|
|
868
|
+
protocolVersion?: UpstreamProtocolVersionPin;
|
|
758
869
|
}
|
|
759
870
|
/**
|
|
760
871
|
* Spec-compliant upstream MCP Streamable HTTP client.
|
|
@@ -775,11 +886,35 @@ declare class StreamableHttpForwarder implements McpForwarder {
|
|
|
775
886
|
/** Lifecycle parity with sse/stdio. */
|
|
776
887
|
close(): Promise<void>;
|
|
777
888
|
forward(request: McpRequest): Promise<ForwardResult>;
|
|
889
|
+
/**
|
|
890
|
+
* The dual-era bridge (relay leg, modern era): a modern-only server
|
|
891
|
+
* answers the retired `initialize` handshake with 404/-32601, so Helio
|
|
892
|
+
* synthesizes the legacy InitializeResult locally from the upstream's own
|
|
893
|
+
* probe-time DiscoverResult. No `mcp-session-id` response header — the
|
|
894
|
+
* legacy spec permits sessionless servers, and the downstream stays
|
|
895
|
+
* sessionless. The synthesized protocolVersion is always the current
|
|
896
|
+
* legacy revision, even for a client that offered an older one.
|
|
897
|
+
*/
|
|
898
|
+
private synthesizeInitializeResult;
|
|
899
|
+
/**
|
|
900
|
+
* The modern upstream removed `notifications/initialized`; answer the
|
|
901
|
+
* same minimal success envelope the SSE-notification path returns.
|
|
902
|
+
*/
|
|
903
|
+
private swallowInitializedNotification;
|
|
904
|
+
/**
|
|
905
|
+
* The relay-side era falsification door (issue #219): a legacy-leg relay whose answer only a
|
|
906
|
+
* modern server gives clears the cached legacy era (the manager no-ops on
|
|
907
|
+
* pins, uncached eras, and cached modern). The response still flows to the
|
|
908
|
+
* client unchanged — no in-place retry.
|
|
909
|
+
*/
|
|
910
|
+
private inspectLegacyRelayOutcome;
|
|
778
911
|
/**
|
|
779
912
|
* Helio-internal execution path (startup prime / internal maintenance) that
|
|
780
913
|
* may borrow the proxy-managed internal session.
|
|
781
914
|
*/
|
|
782
915
|
forwardInternal(request: McpRequest): Promise<ForwardResult>;
|
|
916
|
+
/** Drop the managed internal session AND the cached era; next internal call re-probes. */
|
|
917
|
+
resetInternalSession(): void;
|
|
783
918
|
private send;
|
|
784
919
|
}
|
|
785
920
|
|
|
@@ -939,8 +1074,14 @@ interface AuditRecord {
|
|
|
939
1074
|
readonly id: string;
|
|
940
1075
|
/** ISO 8601 timestamp of when the tool call was received by the proxy. */
|
|
941
1076
|
readonly timestamp: string;
|
|
942
|
-
/**
|
|
1077
|
+
/** Resolved governance session id, or the adapter-supplied id on sideband records. */
|
|
943
1078
|
readonly session_id: string | null;
|
|
1079
|
+
/**
|
|
1080
|
+
* Which identity strategy produced session_id (issue #218): `header`,
|
|
1081
|
+
* `meta`, `legacy_header`, or `transport` on the MCP door, `sideband` for
|
|
1082
|
+
* adapter-supplied ids. Null when no session resolved.
|
|
1083
|
+
*/
|
|
1084
|
+
readonly session_source: string | null;
|
|
944
1085
|
/** Agent identifier from config or request header, if present. */
|
|
945
1086
|
readonly agent_id: string | null;
|
|
946
1087
|
/** Runtime environment label configured on proxy startup, if set. */
|
|
@@ -1007,6 +1148,14 @@ interface AuditRecord {
|
|
|
1007
1148
|
* `match.metadata.*` (#13) and the dashboard metadata columns (#16).
|
|
1008
1149
|
*/
|
|
1009
1150
|
readonly metadata: Record<string, unknown> | null;
|
|
1151
|
+
/**
|
|
1152
|
+
* The client's verbatim MCP-Protocol-Version wire claim on the inbound
|
|
1153
|
+
* request (issue #219) — the CLIENT'S claim, not the upstream era, and not
|
|
1154
|
+
* a validated fact. Recorded uncapped: truncation would manufacture a
|
|
1155
|
+
* false claim in an audit record. Null when the request carried no header
|
|
1156
|
+
* and on records with no MCP wire (drift events, sideband records).
|
|
1157
|
+
*/
|
|
1158
|
+
readonly protocol_version: string | null;
|
|
1010
1159
|
/** ISO 8601 timestamp of when the record was persisted. */
|
|
1011
1160
|
readonly created_at: string;
|
|
1012
1161
|
}
|
|
@@ -2032,11 +2181,28 @@ declare class SpendLimiter {
|
|
|
2032
2181
|
close(): void;
|
|
2033
2182
|
}
|
|
2034
2183
|
|
|
2184
|
+
declare const GATED_SESSION: unique symbol;
|
|
2185
|
+
/** A session bucket value that provably passed the identity gate. */
|
|
2186
|
+
type GatedSession = string & {
|
|
2187
|
+
readonly [GATED_SESSION]: true;
|
|
2188
|
+
};
|
|
2189
|
+
declare const GATED_CHARGES: unique symbol;
|
|
2190
|
+
/** Budget charges that provably passed the session engagement check. */
|
|
2191
|
+
type GatedCharges = readonly BudgetCharge[] & {
|
|
2192
|
+
readonly [GATED_CHARGES]: true;
|
|
2193
|
+
};
|
|
2194
|
+
|
|
2035
2195
|
/** Everything the engine needs about one tool call to resolve its charges. */
|
|
2036
2196
|
interface BudgetChargeContext {
|
|
2037
2197
|
readonly toolName: string;
|
|
2038
2198
|
readonly toolArguments: Record<string, unknown> | undefined;
|
|
2039
|
-
|
|
2199
|
+
/**
|
|
2200
|
+
* Gate-minted session bucket value (issue #218), or null when identity is
|
|
2201
|
+
* unresolved — `bucketKey` builds `session:unknown` from null, which is
|
|
2202
|
+
* legal ONLY because `gateBudgetCharges` denies unresolved session-keyed
|
|
2203
|
+
* engagement before `peekAll`/`recordAll` can run.
|
|
2204
|
+
*/
|
|
2205
|
+
readonly sessionId: GatedSession | null;
|
|
2040
2206
|
/** Adapter-supplied sender id (sideband only); null on the MCP path. */
|
|
2041
2207
|
readonly senderId: string | null;
|
|
2042
2208
|
}
|
|
@@ -2220,8 +2386,12 @@ declare class BudgetEngine {
|
|
|
2220
2386
|
charges: BudgetCharge[];
|
|
2221
2387
|
failures: BudgetChargeFailure[];
|
|
2222
2388
|
};
|
|
2223
|
-
/**
|
|
2224
|
-
|
|
2389
|
+
/**
|
|
2390
|
+
* Check every charge without mutating. All-or-nothing: one deny flips
|
|
2391
|
+
* `allowed`. Accepts only gate-branded charges (issue #218) — a caller
|
|
2392
|
+
* cannot peek budget state without having run the session engagement check.
|
|
2393
|
+
*/
|
|
2394
|
+
peekAll(charges: GatedCharges): {
|
|
2225
2395
|
allowed: boolean;
|
|
2226
2396
|
entries: BudgetPeekEntry[];
|
|
2227
2397
|
};
|
|
@@ -2232,7 +2402,7 @@ declare class BudgetEngine {
|
|
|
2232
2402
|
* Recording is unconditional past the sink (an approved overage
|
|
2233
2403
|
* legitimately pushes a bucket past its limit).
|
|
2234
2404
|
*/
|
|
2235
|
-
recordAll(charges:
|
|
2405
|
+
recordAll(charges: GatedCharges, meta: BudgetCommitMeta): BudgetPeekEntry[];
|
|
2236
2406
|
/**
|
|
2237
2407
|
* Fire one `onBreach` event per breached entry. Called by the doors at the
|
|
2238
2408
|
* moment a peek outcome actually denies the call or raises the composite
|
|
@@ -2329,6 +2499,18 @@ declare class BudgetEngine {
|
|
|
2329
2499
|
private snapshot;
|
|
2330
2500
|
}
|
|
2331
2501
|
|
|
2502
|
+
/** Compiled session config, wired into the route factories at startup. */
|
|
2503
|
+
interface CompiledSessionIdentity {
|
|
2504
|
+
/** The configured identity chain, in order; first match wins. */
|
|
2505
|
+
readonly sources: readonly SessionIdentitySource[];
|
|
2506
|
+
readonly onUnresolved: 'deny' | 'anonymous';
|
|
2507
|
+
/**
|
|
2508
|
+
* Human-readable strategy list (e.g. `header "x-helio-session-id",
|
|
2509
|
+
* legacy_header`) — deny messages name the strategies that were tried.
|
|
2510
|
+
*/
|
|
2511
|
+
readonly strategySummary: string;
|
|
2512
|
+
}
|
|
2513
|
+
|
|
2332
2514
|
/** Options for constructing a GovernedForwarder. */
|
|
2333
2515
|
interface GovernedForwarderOptions {
|
|
2334
2516
|
/** The current environment label (e.g. "production", "staging"). */
|
|
@@ -2345,6 +2527,12 @@ interface GovernedForwarderOptions {
|
|
|
2345
2527
|
spendLimiter?: SpendLimiter;
|
|
2346
2528
|
/** Budget engine for named cross-tool budgets (issue #14). */
|
|
2347
2529
|
budgetEngine?: BudgetEngine;
|
|
2530
|
+
/**
|
|
2531
|
+
* Compiled session identity config (issue #218): the `on_unresolved` mode
|
|
2532
|
+
* and the strategy summary deny messages name. Defaults to the schema
|
|
2533
|
+
* default chain (deny mode) for direct/library construction.
|
|
2534
|
+
*/
|
|
2535
|
+
session?: CompiledSessionIdentity;
|
|
2348
2536
|
}
|
|
2349
2537
|
/** Result of attempting to prime the tool annotation cache. */
|
|
2350
2538
|
interface AnnotationCachePrimeResult {
|
|
@@ -2367,6 +2555,7 @@ declare class GovernedForwarder implements McpForwarder {
|
|
|
2367
2555
|
private readonly inner;
|
|
2368
2556
|
private policy;
|
|
2369
2557
|
private readonly environment;
|
|
2558
|
+
private readonly session;
|
|
2370
2559
|
private readonly auditWriter;
|
|
2371
2560
|
private readonly evidenceStore;
|
|
2372
2561
|
private readonly approvalRouter;
|
|
@@ -2406,6 +2595,20 @@ declare class GovernedForwarder implements McpForwarder {
|
|
|
2406
2595
|
*/
|
|
2407
2596
|
primeAnnotationCache(): Promise<AnnotationCachePrimeResult>;
|
|
2408
2597
|
forward(request: McpRequest): Promise<ForwardResult>;
|
|
2598
|
+
/**
|
|
2599
|
+
* Clamp an over-long `result.ttlMs` on a `tools/list` response to
|
|
2600
|
+
* `policies.tool_revalidation.max_advertised_ttl` (issue #221, D7).
|
|
2601
|
+
*
|
|
2602
|
+
* Downward-only: a `ttlMs` at or below the cap is left untouched, and a
|
|
2603
|
+
* response with no `ttlMs` never gains one — Helio does not manufacture a
|
|
2604
|
+
* cache hint the upstream never advertised. Non-numeric values are left
|
|
2605
|
+
* alone rather than coerced. `cacheScope` passes through untouched: Helio
|
|
2606
|
+
* baselines and vouches for tool *definitions* only, and its own
|
|
2607
|
+
* `tools/list` view is not caller-varying, so it has no basis to alter a
|
|
2608
|
+
* scope hint the upstream set. No-op when tool revalidation is disabled
|
|
2609
|
+
* (including hand-built `CompiledPolicy` fixtures that omit the field).
|
|
2610
|
+
*/
|
|
2611
|
+
private clampCacheHints;
|
|
2409
2612
|
/**
|
|
2410
2613
|
* Apply a tools/list response to the definition cache and surface any
|
|
2411
2614
|
* drift: console warning + immediate audit record per event. Single entry
|
|
@@ -2469,14 +2672,20 @@ declare class GovernedForwarder implements McpForwarder {
|
|
|
2469
2672
|
* or consuming any resources. Returns an MCP-compliant synthetic response.
|
|
2470
2673
|
*/
|
|
2471
2674
|
private handleDryRun;
|
|
2472
|
-
/**
|
|
2675
|
+
/**
|
|
2676
|
+
* Construct a non-session limit bucket key. Session keys are deliberately
|
|
2677
|
+
* NOT built here: they come only from the gate module's `sessionLimitKey`,
|
|
2678
|
+
* whose `GatedSession` parameter makes skipping the identity gate a
|
|
2679
|
+
* compile error (issue #218) — call sites branch on `key === 'session'`.
|
|
2680
|
+
*/
|
|
2473
2681
|
private buildLimitKey;
|
|
2474
2682
|
/**
|
|
2475
|
-
*
|
|
2476
|
-
*
|
|
2477
|
-
*
|
|
2683
|
+
* Gate a session-keyed limit at its key-build site (issue #218). Returns
|
|
2684
|
+
* the bucket key, or null when identity is unresolved under deny mode —
|
|
2685
|
+
* the caller denies (enforce) or reports the marker (dry-run).
|
|
2478
2686
|
*/
|
|
2479
|
-
private
|
|
2687
|
+
private gateSessionLimitKey;
|
|
2688
|
+
private makeSessionUnresolvedResult;
|
|
2480
2689
|
private writeAuditRecord;
|
|
2481
2690
|
private makeDriftBlockResult;
|
|
2482
2691
|
private makeDenyResult;
|
|
@@ -2611,6 +2820,13 @@ interface GovernanceServiceOptions {
|
|
|
2611
2820
|
readonly spendLimiter?: SpendLimiter;
|
|
2612
2821
|
/** Budget engine for named cross-tool budgets (issue #14). */
|
|
2613
2822
|
readonly budgetEngine?: BudgetEngine;
|
|
2823
|
+
/**
|
|
2824
|
+
* Compiled session identity config (issue #218): the sideband door gets
|
|
2825
|
+
* the SAME on_unresolved policy as the MCP door — an adapter omitting
|
|
2826
|
+
* session_id while a session-keyed control is engaged is denied under
|
|
2827
|
+
* `deny`, pools under `anonymous`. Defaults to the schema default chain.
|
|
2828
|
+
*/
|
|
2829
|
+
readonly session?: CompiledSessionIdentity;
|
|
2614
2830
|
readonly auditWriter?: AuditWriter;
|
|
2615
2831
|
/** Default approval timeout (ms) when a rule sets none. */
|
|
2616
2832
|
readonly approvalTimeoutMs?: number;
|
|
@@ -2630,6 +2846,7 @@ interface GovernanceServiceOptions {
|
|
|
2630
2846
|
declare class GovernanceService {
|
|
2631
2847
|
private policy;
|
|
2632
2848
|
private readonly environment;
|
|
2849
|
+
private readonly session;
|
|
2633
2850
|
private readonly evidenceStore;
|
|
2634
2851
|
private readonly approvalRouter;
|
|
2635
2852
|
private readonly rateLimiter;
|
|
@@ -2789,6 +3006,22 @@ declare class GovernanceConfigError extends Error {
|
|
|
2789
3006
|
constructor(message: string);
|
|
2790
3007
|
}
|
|
2791
3008
|
|
|
3009
|
+
/**
|
|
3010
|
+
* Map a header/body agreement rejection (issue #226) onto the audit record
|
|
3011
|
+
* shape, mirroring the `missing_tool_name` precedent: `policy_decision:
|
|
3012
|
+
* 'rejected'`, no rule evaluated, nothing forwarded, written by the caller
|
|
3013
|
+
* via `pushImmediate`. The snake_case mapping lives here alone — the
|
|
3014
|
+
* rejection payload itself stays camelCase protocol facts (`mcp/types.ts`).
|
|
3015
|
+
*
|
|
3016
|
+
* `record_kind` stays `'tool_call'` for the whole class, non-tool methods
|
|
3017
|
+
* included: it is the MCP-door request bucket, and `block_reason` is the
|
|
3018
|
+
* query key. `tool_name` carries the body's name-bearing field (the body
|
|
3019
|
+
* truth an investigator filters for) when the method has one, else the
|
|
3020
|
+
* reserved `'<header_mismatch>'` sentinel — either way `top_tools` already
|
|
3021
|
+
* excludes rejected records, so these rows never pollute tool rankings.
|
|
3022
|
+
*/
|
|
3023
|
+
declare function buildHeaderMismatchAuditRecord(rejection: HeaderMismatchRejection, environment?: string): Omit<AuditRecord, 'id' | 'created_at'>;
|
|
3024
|
+
|
|
2792
3025
|
/** @internal Exported for testing only. */
|
|
2793
3026
|
declare class QueueChannel implements ApprovalChannel {
|
|
2794
3027
|
readonly type = "dashboard";
|
|
@@ -2915,6 +3148,10 @@ interface ActionEvent {
|
|
|
2915
3148
|
readonly block_reason: string | null;
|
|
2916
3149
|
readonly approval_status: string | null;
|
|
2917
3150
|
readonly session_id: string | null;
|
|
3151
|
+
/** Identity strategy that produced session_id (issue #218), or null. */
|
|
3152
|
+
readonly session_source: string | null;
|
|
3153
|
+
/** The client's verbatim MCP-Protocol-Version wire claim (issue #219), or null. */
|
|
3154
|
+
readonly protocol_version: string | null;
|
|
2918
3155
|
readonly agent_id: string | null;
|
|
2919
3156
|
readonly environment: string | null;
|
|
2920
3157
|
readonly timestamp: string;
|
|
@@ -3061,4 +3298,4 @@ interface DashboardAppOptions {
|
|
|
3061
3298
|
*/
|
|
3062
3299
|
declare function createDashboardApp(deps: DashboardAppDeps, options?: DashboardAppOptions): Hono;
|
|
3063
3300
|
|
|
3064
|
-
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 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, compileBudgets, compilePolicies, createApp, createApprovalApp, createChannels, createDashboardApp, createSidebandApp, createSlackActionApp, evaluatePolicy, loadConfig, matchRule, startServer, startSidebandServer };
|
|
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 };
|